diff --git a/.changes/.editorconfig b/.changes/.editorconfig
new file mode 100644
index 0000000000..d37276d2b0
--- /dev/null
+++ b/.changes/.editorconfig
@@ -0,0 +1,2 @@
+[*.json]
+insert_final_newline = false
\ No newline at end of file
diff --git a/.changes/1.18.json b/.changes/1.18.json
new file mode 100644
index 0000000000..780dfd9c33
--- /dev/null
+++ b/.changes/1.18.json
@@ -0,0 +1,62 @@
+{
+ "date" : "2020-09-21",
+ "version" : "1.18",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for AWS SSO based credential profiles"
+ }, {
+ "type" : "feature",
+ "description" : "Support colons (`:`) in credential profile names"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for Lambda runtime java8.al2"
+ }, {
+ "type" : "feature",
+ "description" : "Allow connecting to RDS/Redshift databases with temporary IAM AWS credentials or a SecretsManager secret"
+ }, {
+ "type" : "feature",
+ "description" : "Several enhancements to the UX around connecting to AWS including:\n- Making connection settings more visible (now visible in the AWS Explorer)\n- Automatically selecting 'default' profile if it exists\n- Better visibility of connection validation workflow (more information when unable to connect)\n- Handling of default regions on credential profile\n- Better UX around partitions\n- Adding ability to refresh connection from the UI"
+ }, {
+ "type" : "feature",
+ "description" : "Save update Lambda code settings"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix several cases where features not supported by the host IDE are shown (#1980)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Start generating SAM project before the IDE is done indexing"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix several uncaught exceptions caused by plugins being installed but not enabled"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix removing a source_profile leading to an IDE error on profile file refresh"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue where templates > 51200 bytes would not deploy with \"Deploy Serverless Application\" (#1973)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix the function selection panel not reloading when changing SAM templates (#955)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix remote terminal start issue on 2020.2"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Rider building Lambda into incorrect folders"
+ }, {
+ "type" : "bugfix",
+ "description" : "Improved rendering speed of wrapped text in CloudWatch logs and CloudFormation events tables"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix the CloudWatch Logs table breaking when the service returns an exception during loading more entries (#1951)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Improve watching of the AWS profile files to incorporate changes made to the files outisde of the IDE"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix SAM Gradle Hello World syncing twice (#2003)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Quote template parameters when deploying a cloudformation template"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.19.json b/.changes/1.19.json
new file mode 100644
index 0000000000..91ecb2cbf0
--- /dev/null
+++ b/.changes/1.19.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2020-10-07",
+ "version" : "1.19",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add the ability to copy the URL to an S3 object"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for debugging dotnet 3.1 local lambdas (requires minimum SAM CLI version of 1.4.0)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.20.json b/.changes/1.20.json
new file mode 100644
index 0000000000..ebbedeaf0d
--- /dev/null
+++ b/.changes/1.20.json
@@ -0,0 +1,20 @@
+{
+ "date" : "2020-10-22",
+ "version" : "1.20",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for `+` in AWS profile names"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix being unable to use a SSO profile in a credential chain"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Aurora MySQL 5.7 not showing up in the AWS Explorer"
+ }, {
+ "type" : "bugfix",
+ "description" : "Improve IAM RDS connection: Fix Aurora MySQL, detect more error cases, fix database configuration validation throwing when there is no DB name"
+ }, {
+ "type" : "deprecation",
+ "description" : "2019.3 support will be removed in the next release"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.21.json b/.changes/1.21.json
new file mode 100644
index 0000000000..306df98989
--- /dev/null
+++ b/.changes/1.21.json
@@ -0,0 +1,29 @@
+{
+ "date" : "2020-11-24",
+ "version" : "1.21",
+ "entries" : [ {
+ "type" : "breaking",
+ "description" : "Remove support for 2019.3, 2020.1 is the new minimum version"
+ }, {
+ "type" : "feature",
+ "description" : "Add copy Logical/Physical ID actions to Stack View #2165"
+ }, {
+ "type" : "feature",
+ "description" : "Add SQS AWS Explorer node and the ability to send/poll for messages"
+ }, {
+ "type" : "feature",
+ "description" : "Add the ability to search CloudWatch Logs using CloudWatch Logs Insights"
+ }, {
+ "type" : "feature",
+ "description" : "Add copy actions to CloudFormation outputs (#2179)"
+ }, {
+ "type" : "feature",
+ "description" : "Support for the 2020.3 family of IDEs"
+ }, {
+ "type" : "feature",
+ "description" : "Add an AWS Explorer ECR node"
+ }, {
+ "type" : "bugfix",
+ "description" : "Significantly speed up loading the list of S3 buckets (#2174)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.22.json b/.changes/1.22.json
new file mode 100644
index 0000000000..a77215a6fd
--- /dev/null
+++ b/.changes/1.22.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2020-12-01",
+ "version" : "1.22",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Container Image Support in Lambda"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix update Lambda code for compiled languages (#2231)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.23.json b/.changes/1.23.json
new file mode 100644
index 0000000000..6717892b36
--- /dev/null
+++ b/.changes/1.23.json
@@ -0,0 +1,47 @@
+{
+ "date" : "2021-02-04",
+ "version" : "1.23",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add \"Copy S3 URI\" to S3 objects (#2208)"
+ }, {
+ "type" : "feature",
+ "description" : "Add Dotnet5 Lambda support (Image only)"
+ }, {
+ "type" : "feature",
+ "description" : "Add option to view past object versions in S3 file editor"
+ }, {
+ "type" : "feature",
+ "description" : "Nodejs14.x Lambda support"
+ }, {
+ "type" : "feature",
+ "description" : "Update Lambda max memory to 10240"
+ }, {
+ "type" : "bugfix",
+ "description" : "Re-add environment variable settings to SAM template based run configurations (#2282)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix error thrown on profile refresh if removing a profile that uses source_profile (#2309)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix NodeJS and Python breakpoints failing to hit sometimes"
+ }, {
+ "type" : "bugfix",
+ "description" : "Speed up loading CloudFormation resources"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix not invalidating credentials when a `source_profile` is updated"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix cell based copying in CloudWatch Logs (#2333)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix certain S3 buckets being unable to be shown in the explorer (#2342)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix exception thrown in the new project wizard when run immediately after the toolkit is installed"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixing issue with SSO refresh locking UI thread (#2224)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.24.json b/.changes/1.24.json
new file mode 100644
index 0000000000..3cc7392489
--- /dev/null
+++ b/.changes/1.24.json
@@ -0,0 +1,23 @@
+{
+ "date" : "2021-02-17",
+ "version" : "1.24",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "RDS serverless databases are now visible in the RDS node in the explorer"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix transient 'Aborted!' message on successful SAM CLI local Lambda execution"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix being unable to open the file browser in the Schemas download panel"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix being unable to type/copy paste into the SAM local run config's template path textbox"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Secrets Manager-based databse auth throwing NullPointer when editing settings in 2020.3.2 (Fixes #2403)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix making an un-needed service call on IDE startup (#2426)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.25.json b/.changes/1.25.json
new file mode 100644
index 0000000000..6abdcac1bb
--- /dev/null
+++ b/.changes/1.25.json
@@ -0,0 +1,47 @@
+{
+ "date" : "2021-03-10",
+ "version" : "1.25",
+ "entries" : [ {
+ "type" : "breaking",
+ "description" : "Minimum SAM CLI version is now 1.0.0"
+ }, {
+ "type" : "feature",
+ "description" : "Debugging Python based Lambdas locally now have the Python interactive console enabled (Fixes #1165)"
+ }, {
+ "type" : "feature",
+ "description" : "Add a setting for how the AWS profiles notification is shown (#2408)"
+ }, {
+ "type" : "feature",
+ "description" : "Deleting resources now requires typing \"delete me\" instead of the resource name"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for 2021.1"
+ }, {
+ "type" : "feature",
+ "description" : "Allow deploying SAM templates from the CloudFormaton node (#2166)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Improve error messages when properties are not found in templates (#2449)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix resource selectors assuming every region has every service (#2435)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Docker is now validated before building the Lambda when running and debugging locally (Fixes #2418)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixed several UI inconsistencies in the S3 bucket viewer actions"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix showing stack status notification on opening existing CloudFormation stack (#2157)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Processes using the Step system (e.g. SAM build) can now be stopped (#2418)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixed the Remote Lambda Run Configuration failing to load the list of functions if not in active region"
+ }, {
+ "type" : "deprecation",
+ "description" : "2020.1 support will be removed in the next release"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.26.json b/.changes/1.26.json
new file mode 100644
index 0000000000..75c69d5b2f
--- /dev/null
+++ b/.changes/1.26.json
@@ -0,0 +1,29 @@
+{
+ "date" : "2021-04-14",
+ "version" : "1.26",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for creating/debugging Golang Lambdas (#649)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix breaking run configuration gutter icons when the IDE has no languages installed that support Lambda local runtime (#2504)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue preventing deployment of CloudFormation templates with empty values (#1498)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix cloudformation stack events failing to update after reaching a final state (#2519)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix the Local Lambda run configuration always reseting the environemnt variables to defaults when using templates (#2509)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix being able to interact with objects from deleted buckets (#1601)"
+ }, {
+ "type" : "removal",
+ "description" : "Remove support for 2020.1"
+ }, {
+ "type" : "removal",
+ "description" : "Lambda gutter icons no longer take deployed Lambdas into account due to accuracy and performance issues"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.27.json b/.changes/1.27.json
new file mode 100644
index 0000000000..5b58fa28fb
--- /dev/null
+++ b/.changes/1.27.json
@@ -0,0 +1,20 @@
+{
+ "date" : "2021-05-24",
+ "version" : "1.27",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for AppRunner. Create/delete/pause/resume/deploy and view logs for your AppRunner services."
+ }, {
+ "type" : "feature",
+ "description" : "Add support for building and pushing local images to ECR"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for running/debugging Typescript Lambdas"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Rider locking up when right clicking a Lambda in the AWS Explorer with a dotnet runtime in 2021.1"
+ }, {
+ "type" : "bugfix",
+ "description" : "While debugging a Lambda function locally, make sure stopping the debugger will always stop the underlying SAM cli process (#2564)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.28.json b/.changes/1.28.json
new file mode 100644
index 0000000000..8b6b0d3bcb
--- /dev/null
+++ b/.changes/1.28.json
@@ -0,0 +1,47 @@
+{
+ "date" : "2021-07-12",
+ "version" : "1.28",
+ "entries" : [ {
+ "type" : "breaking",
+ "description" : "Python 2.7 Lambda template removed from New Project Wizard"
+ }, {
+ "type" : "feature",
+ "description" : "Adding the ability to inject credentials/region into existing IntelliJ IDEA and PyCharm Run Configurations (e.g Application, JUnit, Python, PyTest). This requires experiments `aws.feature.javaRunConfigurationExtension` / `aws.feature.pythonRunConfigurationExtension`, see [Enabling Experiments](https://github.com/aws/aws-toolkit-jetbrains/blob/master/README.md#experimental-features)"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for updating tags during SAM deployment"
+ }, {
+ "type" : "feature",
+ "description" : "(Experimental) Adding ability to create a local terminal using the currently selected AWS connection (experiment ID `aws.feature.connectedLocalTerminal`, see [Enabling Experiments](https://github.com/aws/aws-toolkit-jetbrains/blob/master/README.md#experimental-features)) #2151"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for pulling images from ECR"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix missing text in the View S3 bucket with prefix dialog"
+ }, {
+ "type" : "bugfix",
+ "description" : "Improved performance of listing S3 buckets in certain situations"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix copying action in CloudWatch Logs Stream and Event Time providing epoch time instead of displayed value"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix using message bus after project has been closed (Fixes #2615)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix S3 bucket viewer actions being triggered by short cuts even if it is not focused"
+ }, {
+ "type" : "bugfix",
+ "description" : "Don't show Lambda run configuration suggestions on Go test code"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix being unable to create Python 3.8 Image-based Lambdas in New Project wizard"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixed showing templates that were not for Image-based Lambdas when Image is selected in New Project wizard"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for IDEs based on the 2020.2 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.29.json b/.changes/1.29.json
new file mode 100644
index 0000000000..557175c358
--- /dev/null
+++ b/.changes/1.29.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2021-07-20",
+ "version" : "1.29",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "When uploading a file to S3, the content type is now set accoriding to the files extension"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix being unable to update Lambda configuration if the Image packaging type"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.30.json b/.changes/1.30.json
new file mode 100644
index 0000000000..00d1f3a65b
--- /dev/null
+++ b/.changes/1.30.json
@@ -0,0 +1,23 @@
+{
+ "date" : "2021-08-05",
+ "version" : "1.30",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add ability to view bucket by entering bucket name/URI"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix CWL last event sorting (#2737)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Go Lambda handler resolving into Go standard library (#2730)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix `ActionPlaces.isPopupPlace` error after opening the AWS connection settings menu (#2736)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix some warnings due to slow operations on EDT (#2735)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Java Lambda run marker issues and disable runmarker processing in tests and language-injected text fragments"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.31.json b/.changes/1.31.json
new file mode 100644
index 0000000000..3ff21e1b6f
--- /dev/null
+++ b/.changes/1.31.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2021-08-17",
+ "version" : "1.31",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for Python 3.9 Lambdas"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix regression in SAM run configurations using file-based input (#2762)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix CloudWatch sorting (#2737)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.32.json b/.changes/1.32.json
new file mode 100644
index 0000000000..4ecef1d82a
--- /dev/null
+++ b/.changes/1.32.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2021-09-07",
+ "version" : "1.32",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix IDE error about context.module being null (#2776)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix NullPointerException calling isInTestSourceContent (#2752)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.33.json b/.changes/1.33.json
new file mode 100644
index 0000000000..89adf4c5e2
--- /dev/null
+++ b/.changes/1.33.json
@@ -0,0 +1,32 @@
+{
+ "date" : "2021-10-14",
+ "version" : "1.33",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Surface read-only support for hundreds of resources under the Resources node in the AWS Explorer"
+ }, {
+ "type" : "feature",
+ "description" : "Amazon DynamoDB table viewer"
+ }, {
+ "type" : "bugfix",
+ "description" : "Changed error message 'Command did not exist successfully' to 'Command did not exit successfully'"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixed spelling and grammar in MessagesBundle.properties"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix not being able to start Rider debugger against a Lambda running on a host ARM machine"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix SSO login not being triggered when the auth code is invalid (#2796)"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2020.2.x IDEs"
+ }, {
+ "type" : "removal",
+ "description" : "Dropped support for the no longer supported Lambda runtime Python 2.7"
+ }, {
+ "type" : "removal",
+ "description" : "Dropped support for the no longer supported Lambda runtime Node.js 10.x"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.34.json b/.changes/1.34.json
new file mode 100644
index 0000000000..b762d1043f
--- /dev/null
+++ b/.changes/1.34.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2021-10-21",
+ "version" : "1.34",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix issue in Resources where some S3 Buckets fail to open"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix null exception when view documentation action executed for types with missing doc urls"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix uncaught exception when a resource does not support LIST in a certain region."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.35.json b/.changes/1.35.json
new file mode 100644
index 0000000000..7cd1d7902e
--- /dev/null
+++ b/.changes/1.35.json
@@ -0,0 +1,35 @@
+{
+ "date" : "2021-11-18",
+ "version" : "1.35",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Respect the `duration_seconds` property when assuming a role if set on the profile"
+ }, {
+ "type" : "feature",
+ "description" : "Added 2021.3 support"
+ }, {
+ "type" : "feature",
+ "description" : "Added support for AWS profiles that use the `credential_source` key"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Python Lambda gutter icons not generating handler paths relative to the requirements.txt file (#2853)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix file changes not being saved before running Local Lambda run configurations (#2889)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix incorrect behavior with RDS Secrets Manager Auth when SSH tunneling is enabled (#2781)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix copying out of the DynamoDB table viewer copying the in-memory representation instead of displayed value"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix error about write actions when opening files from the S3 browser (#2913)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix NullPointerException on combobox browse components (#2866)"
+ }, {
+ "type" : "removal",
+ "description" : "Dropped support for the no longer supported Lambda runtime .NET Core 2.1"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.36.json b/.changes/1.36.json
new file mode 100644
index 0000000000..bc53f65230
--- /dev/null
+++ b/.changes/1.36.json
@@ -0,0 +1,5 @@
+{
+ "date" : "2021-11-23",
+ "version" : "1.36",
+ "entries" : [ ]
+}
\ No newline at end of file
diff --git a/.changes/1.37.json b/.changes/1.37.json
new file mode 100644
index 0000000000..319122496b
--- /dev/null
+++ b/.changes/1.37.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-01-06",
+ "version" : "1.37",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add SAM Lambda ARM support"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix plugin deprecation warning in DynamoDB viewer (#2987)"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for IDEs based on the 2020.3 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.38.json b/.changes/1.38.json
new file mode 100644
index 0000000000..a5aea75e33
--- /dev/null
+++ b/.changes/1.38.json
@@ -0,0 +1,20 @@
+{
+ "date" : "2022-02-17",
+ "version" : "1.38",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix StringIndexOutOfBoundsException (#3025)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix regression preventing ECR repository creation"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Lambda run configuration exception while setting handler architecture"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix image-based Lambda debugging for Python 3.6"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2020.3.x IDEs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.39.json b/.changes/1.39.json
new file mode 100644
index 0000000000..3e9b50a277
--- /dev/null
+++ b/.changes/1.39.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-03-03",
+ "version" : "1.39",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Added in 1.37: The toolkit will now offer to open ARNs present in your code editor in your browser"
+ }, {
+ "type" : "feature",
+ "description" : "Added support for .NET 6 runtime for creating and debugging SAM functions"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue where console federation with long-term credentails results in session with no permissions"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.40.json b/.changes/1.40.json
new file mode 100644
index 0000000000..f54b2ace0d
--- /dev/null
+++ b/.changes/1.40.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2022-03-07",
+ "version" : "1.40",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix logged error due to ARN contributor taking too long (#3085)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.41.json b/.changes/1.41.json
new file mode 100644
index 0000000000..fbbb4b7d96
--- /dev/null
+++ b/.changes/1.41.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2022-03-25",
+ "version" : "1.41",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Adding Go (Golang) as a supported language for code binding generation through the EventBridge Schemas service"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.42.json b/.changes/1.42.json
new file mode 100644
index 0000000000..c0e06d0ac0
--- /dev/null
+++ b/.changes/1.42.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2022-04-13",
+ "version" : "1.42",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for 2022.1"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.43.json b/.changes/1.43.json
new file mode 100644
index 0000000000..c09d0a33c6
--- /dev/null
+++ b/.changes/1.43.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2022-04-14",
+ "version" : "1.43",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix regression in DataGrip 2022.1 caused by new APIs in the platform (#3125)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.44.json b/.changes/1.44.json
new file mode 100644
index 0000000000..a2fc7e50da
--- /dev/null
+++ b/.changes/1.44.json
@@ -0,0 +1,32 @@
+{
+ "date" : "2022-06-01",
+ "version" : "1.44",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add warning to indicate time delay in SQS queue deletion"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixed issue with uncaught exception in resource cache (#3098)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Don't attempt to setup run configurations for test code (#3075)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix toolWindow not running in EDT"
+ }, {
+ "type" : "bugfix",
+ "description" : "Handle Lambda pending states while updating function (#2984)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix modality issue when opening a CloudWatch log stream in editor (#2991)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Workaround regression with ARN console navigation in JSON files"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix 'The project directory does not exist!' when creating SAM/Gradle projects when the Android plugin is also installed"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for IDEs based on the 2021.1 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.45.json b/.changes/1.45.json
new file mode 100644
index 0000000000..ae7f916b05
--- /dev/null
+++ b/.changes/1.45.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2022-06-23",
+ "version" : "1.45",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "[CodeWhisperer](https://aws.amazon.com/codewhisperer) uses machine learning to generate code suggestions from the existing code and comments in your IDE. Supported languages include: Java, Python, and JavaScript."
+ }, {
+ "type" : "feature",
+ "description" : "Added 2022.2 support"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix .NET Lambda debugging regression in 2022.1.1"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2021.1.x IDEs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.46.json b/.changes/1.46.json
new file mode 100644
index 0000000000..8af8364612
--- /dev/null
+++ b/.changes/1.46.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-06-28",
+ "version" : "1.46",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Nodejs16.x Lambda runtime support"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix broken user UI due to 'Enter' handler override (#3193)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix SSM plugin install on deb/rpm systems (#3130)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.47.json b/.changes/1.47.json
new file mode 100644
index 0000000000..7360cf9f86
--- /dev/null
+++ b/.changes/1.47.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2022-07-08",
+ "version" : "1.47",
+ "entries" : [ {
+ "type" : "removal",
+ "description" : "Remove Cloud Debugging of ECS Services (beta)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.48.json b/.changes/1.48.json
new file mode 100644
index 0000000000..a13ebf3462
--- /dev/null
+++ b/.changes/1.48.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2022-07-26",
+ "version" : "1.48",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix to display appropriate error messaging for filtering Cloudwatch Streams using search patterns failures"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.49.json b/.changes/1.49.json
new file mode 100644
index 0000000000..42465d2743
--- /dev/null
+++ b/.changes/1.49.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-08-11",
+ "version" : "1.49",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix IllegalCallableAccessException thrown in several UI panels (#3228)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix to stop showing CodeWhisperer's welcome page every time on project start"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for IDEs based on the 2021.2 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.50.json b/.changes/1.50.json
new file mode 100644
index 0000000000..69646898c9
--- /dev/null
+++ b/.changes/1.50.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2022-08-23",
+ "version" : "1.50",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix opening toolwindow tabs in incorrect thread in Cloudwatch Logs"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix hitting enter inside braces will produce an extra newline (#3270)"
+ }, {
+ "type" : "deprecation",
+ "description" : "Remove support for deprecated Lambda runtime Python 3.6"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2021.2.x IDEs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.51.json b/.changes/1.51.json
new file mode 100644
index 0000000000..551d0bfc72
--- /dev/null
+++ b/.changes/1.51.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-09-22",
+ "version" : "1.51",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Resources (in AWS Explorer) can list more resource types for EC2, IoT, RDS, Redshift, NetworkManager, and other services"
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer now supports .jsx files"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer fixes"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.52.json b/.changes/1.52.json
new file mode 100644
index 0000000000..415afd81c4
--- /dev/null
+++ b/.changes/1.52.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2022-10-19",
+ "version" : "1.52",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Added 2022.3 support"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix `credential_process` retrieval when command contains quoted arguments on Windows (#3322)"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for IDEs based on the 2021.3 platform"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix `java.lang.IllegalStateException: Region provider data is missing default data` (#3264)"
+ } ]
+}
diff --git a/.changes/1.53.json b/.changes/1.53.json
new file mode 100644
index 0000000000..2deb702ca3
--- /dev/null
+++ b/.changes/1.53.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-11-23",
+ "version" : "1.53",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Sync Serverless Application(SAM Accelerate)"
+ }, {
+ "type" : "feature",
+ "description" : "New experiment to allow injection of AWS Connection details (region/credentials) into Golang Run Configurations"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2021.3.x IDEs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.54.json b/.changes/1.54.json
new file mode 100644
index 0000000000..52cf5be90f
--- /dev/null
+++ b/.changes/1.54.json
@@ -0,0 +1,20 @@
+{
+ "date" : "2022-11-28",
+ "version" : "1.54",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Amazon CodeWhisperer now supports JavaScript for Security Scan to catch security vulnerabilities."
+ }, {
+ "type" : "feature",
+ "description" : "Amazon CodeWhisperer recommendations are more context aware. We are removing the overlaps from CodeWhisperer suggestions specifically when the cursor is inside a code block."
+ }, {
+ "type" : "feature",
+ "description" : "Amazon CodeWhisperer now supports TypeScript and C# programming languages."
+ }, {
+ "type" : "feature",
+ "description" : "Amazon CodeWhisperer is now available as a supported feature and no longer an experimental feature."
+ }, {
+ "type" : "feature",
+ "description" : "Amazon CodeWhisperer now adds new access methods with AWS Builder ID and AWS IAM Identity Center to enable and get started."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.55.json b/.changes/1.55.json
new file mode 100644
index 0000000000..df43c7b0b6
--- /dev/null
+++ b/.changes/1.55.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-12-01",
+ "version" : "1.55",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Amazon CodeCatalyst: Connect JetBrains to your remote Dev Environments."
+ }, {
+ "type" : "feature",
+ "description" : "Amazon CodeCatalyst: Clone your repositories to your local machine."
+ }, {
+ "type" : "feature",
+ "description" : "Amazon CodeCatalyst: Connect using your AWS Builder ID."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.56.json b/.changes/1.56.json
new file mode 100644
index 0000000000..d938623917
--- /dev/null
+++ b/.changes/1.56.json
@@ -0,0 +1,20 @@
+{
+ "date" : "2022-12-08",
+ "version" : "1.56",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Remove redundant calls in certain Gateway UI panels"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix threading issue while attempting to login to CodeCatalyst"
+ }, {
+ "type" : "bugfix",
+ "description" : "Only list dev environments under projects that users are a member of"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix 'Learn more' link in Gateway 2022.2"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix connection issue with CodeCatalyst when user is already logged into CodeWhisperer"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.57.json b/.changes/1.57.json
new file mode 100644
index 0000000000..ec81e16e74
--- /dev/null
+++ b/.changes/1.57.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2022-12-15",
+ "version" : "1.57",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Change reauthentication prompt to be non-distruptive notification."
+ }, {
+ "type" : "bugfix",
+ "description" : "Add do not show again button for CodeWhisperer accountless usage notification"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix CodeWhisperer status widget is shown even when users are disconnected"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.58.json b/.changes/1.58.json
new file mode 100644
index 0000000000..a023a74495
--- /dev/null
+++ b/.changes/1.58.json
@@ -0,0 +1,20 @@
+{
+ "date" : "2023-01-12",
+ "version" : "1.58",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: more responsive Auto-Suggestions"
+ }, {
+ "type" : "feature",
+ "description" : "Added Nodejs18.x Lambda runtime support"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix regression in requirements.txt detection (#3041)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix `com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException when choosing an input template in Lambda Run Configurations` (#3359)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix Lambda Python console encoding issue (#2802)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.59.json b/.changes/1.59.json
new file mode 100644
index 0000000000..a2007dd2ea
--- /dev/null
+++ b/.changes/1.59.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-01-27",
+ "version" : "1.59",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Added an option to submit feedback for the AWS Toolkit in JetBrains Gateway"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.60.json b/.changes/1.60.json
new file mode 100644
index 0000000000..02c312244e
--- /dev/null
+++ b/.changes/1.60.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-02-01",
+ "version" : "1.60",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix Small Dev Environment instance sizes not connecting to the thin clients"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.61.json b/.changes/1.61.json
new file mode 100644
index 0000000000..906d7770bd
--- /dev/null
+++ b/.changes/1.61.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-02-17",
+ "version" : "1.61",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Authenticating through the browser now requires users to manually enter a user verification code for SSO/AWS Builder ID"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix NPE that may occur when installing the toolkit for the first time (#3433)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix network calls cant be made inside read/write action exception thrown from CodeWhisperer (#3423)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.62.json b/.changes/1.62.json
new file mode 100644
index 0000000000..ea0331729f
--- /dev/null
+++ b/.changes/1.62.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2023-03-20",
+ "version" : "1.62",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Show friendlier application name when signing in using SSO"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix confusing experience when attempting to sign in to multiple Builder IDs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.63.json b/.changes/1.63.json
new file mode 100644
index 0000000000..3103c88afe
--- /dev/null
+++ b/.changes/1.63.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-03-24",
+ "version" : "1.63",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix issue where multiple Builder ID entries show up in connection list"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix temporary deadlock when user fails to complete reauthentication request"
+ }, {
+ "type" : "bugfix",
+ "description" : "Only allow cloning a repository from CodeCatalyst if it's hosted on CodeCatalyst"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.64.json b/.changes/1.64.json
new file mode 100644
index 0000000000..e064c9bb6f
--- /dev/null
+++ b/.changes/1.64.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2023-03-29",
+ "version" : "1.64",
+ "entries" : [ {
+ "type" : "breaking",
+ "description" : "Required SAM CLI upgrade to v1.78.0 to for using Sync Serverless Application option."
+ }, {
+ "type" : "feature",
+ "description" : "Support for RDS MariaDB instances (#3530)"
+ }, {
+ "type" : "feature",
+ "description" : "Added 2023.1 support"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for IDEs based on the 2022.1 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.65.json b/.changes/1.65.json
new file mode 100644
index 0000000000..6d1da0325e
--- /dev/null
+++ b/.changes/1.65.json
@@ -0,0 +1,38 @@
+{
+ "date" : "2023-04-13",
+ "version" : "1.65",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "[CodeWhisperer]: Introducing \"Stop code scan\" feature where users will be able to stop the ongoing code scan and immediately start a new one. "
+ }, {
+ "type" : "feature",
+ "description" : "[CodeWhisperer]: Automatic import recommendations"
+ }, {
+ "type" : "feature",
+ "description" : "[CodeWhisperer]: Now supports cross region calls."
+ }, {
+ "type" : "feature",
+ "description" : "Attempt to download IDE thin client earlier in the CodeCatalyst Dev Environment connection process"
+ }, {
+ "type" : "feature",
+ "description" : "[CodeWhisperer]: New supported programming languages: C, C++, Go, Kotlin, Php, Ruby, Rust, Scala, Shell, Sql."
+ }, {
+ "type" : "bugfix",
+ "description" : "Include more information in the Dev Environment status tooltip"
+ }, {
+ "type" : "bugfix",
+ "description" : "Provide consistent UX in all Dev Environment wizard variants"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix 'MissingResourceException: Registry key is not defined'"
+ }, {
+ "type" : "bugfix",
+ "description" : "[CodeWhisperer]: Multiple bug fixes to improve user experience"
+ }, {
+ "type" : "removal",
+ "description" : "Drop support for the Node.js 12.x Lambda runtime"
+ }, {
+ "type" : "removal",
+ "description" : "Drop support for the .NET Core 3.1 Lambda runtime"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.66.json b/.changes/1.66.json
new file mode 100644
index 0000000000..e8437f0432
--- /dev/null
+++ b/.changes/1.66.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2023-04-19",
+ "version" : "1.66",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Display current space and project name on status bar while working in a CodeCatalyst Dev Environment"
+ }, {
+ "type" : "feature",
+ "description" : "Add support for Lambda runtime Python 3.10"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix `java.lang.Throwable: Invalid html: tag inserted automatically and shouldn't be used` (#3608)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue where nothing happens when trying to create an empty Dev Environment"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.67.json b/.changes/1.67.json
new file mode 100644
index 0000000000..3efb250b2c
--- /dev/null
+++ b/.changes/1.67.json
@@ -0,0 +1,23 @@
+{
+ "date" : "2023-04-27",
+ "version" : "1.67",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Using the least permissive set of scopes for features during BuilderID/SSO login. Using the same connection for multiple features will request additional scopes to be used."
+ }, {
+ "type" : "feature",
+ "description" : "Add support for Lambda Runtime Java17"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix the Add Connection Dialog box references to the correct documentation pages"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix thread access during validation of SAM templates"
+ }, {
+ "type" : "bugfix",
+ "description" : "[CodeWhisperer]: login session length should increase to it's expected length. Users will now see less frequent expired sessions."
+ }, {
+ "type" : "bugfix",
+ "description" : "Improve handling of disk errors related to SSO and align folder permissions with AWS CLI"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.68.json b/.changes/1.68.json
new file mode 100644
index 0000000000..400f8e4419
--- /dev/null
+++ b/.changes/1.68.json
@@ -0,0 +1,35 @@
+{
+ "date" : "2023-05-30",
+ "version" : "1.68",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer supports application wide connections"
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer improves auto-suggestions for java"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix threading issue preventing SAM Applications from opening in Rider 2023.1"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue reconnecting to CodeWhisperer using an Identity Center directory outside of us-east-1 (#3662)"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix 'null' is not a connection when authenticating to CodeWhisperer"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: user is sometimes required to re-login before token expiration"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue where the \"Do not ask again\" option is not respected when switching connections on CodeWhisperer/CodeCatalyst"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for JetBrains Gateway version 2022.2 and version 2022.3"
+ }, {
+ "type" : "removal",
+ "description" : "Remove support for Aurora MySQL v1 (#3356)"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2022.1.x IDEs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.69.json b/.changes/1.69.json
new file mode 100644
index 0000000000..9e707673c3
--- /dev/null
+++ b/.changes/1.69.json
@@ -0,0 +1,29 @@
+{
+ "date" : "2023-06-13",
+ "version" : "1.69",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer improves auto-suggestions for python csharp typescript and javascript"
+ }, {
+ "type" : "feature",
+ "description" : "Removed 10 secs delay when connecting to Dev environments of Small Instance Size"
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Improve file context fetching logic"
+ }, {
+ "type" : "bugfix",
+ "description" : "Inlay not supported exception in injected editor"
+ }, {
+ "type" : "bugfix",
+ "description" : "fix right context merging not accounting userinput, which result in cases CodeWhisperer still show recommendation where user already type the content of recommendation out thus no character is being inserted by CodeWhisperer"
+ }, {
+ "type" : "bugfix",
+ "description" : "Add error notification to upgrade SAM CLI v1.85-1.86.1 if on windows"
+ }, {
+ "type" : "bugfix",
+ "description" : "Always use AWS smile logo to reduce confusion if users are on the 'New UI' (#3636)"
+ }, {
+ "type" : "removal",
+ "description" : "Remove support for Gateway 2022.2 and 2022.3."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.70.json b/.changes/1.70.json
new file mode 100644
index 0000000000..4677e7481f
--- /dev/null
+++ b/.changes/1.70.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2023-06-27",
+ "version" : "1.70",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer improves auto-suggestions for tsx and jsx"
+ }, {
+ "type" : "bugfix",
+ "description" : "Show re-authenticate prompt when invoking CodeWhisperer APIs while connection expired"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.71.json b/.changes/1.71.json
new file mode 100644
index 0000000000..65da63ca08
--- /dev/null
+++ b/.changes/1.71.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-07-06",
+ "version" : "1.71",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix inproper request format when sending empty supplemental context"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.72.json b/.changes/1.72.json
new file mode 100644
index 0000000000..17dee555c0
--- /dev/null
+++ b/.changes/1.72.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2023-07-11",
+ "version" : "1.72",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Improve suggestion quality with enhanced file context fetching"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix AWS Lambda configuration window resize (#3657)"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.73.json b/.changes/1.73.json
new file mode 100644
index 0000000000..4841997dc9
--- /dev/null
+++ b/.changes/1.73.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-07-19",
+ "version" : "1.73",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Improve Java suggestion quality with enhanced file context fetching"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: Run read operation in the background thread without runReadAction"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: Fix an issue where CodeWhisperer would stuck in the invocation state indefinitely"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.74.json b/.changes/1.74.json
new file mode 100644
index 0000000000..51a267a330
--- /dev/null
+++ b/.changes/1.74.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-07-25",
+ "version" : "1.74",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Explorer is automatically refreshed with new credentials when they are added to credential file."
+ }, {
+ "type" : "feature",
+ "description" : "Added 2023.2 support"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix 'No display name is specified for configurable' in 2023.2"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.75.json b/.changes/1.75.json
new file mode 100644
index 0000000000..0b2b514071
--- /dev/null
+++ b/.changes/1.75.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-08-03",
+ "version" : "1.75",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Add support for Lambda runtime Python 3.11"
+ }, {
+ "type" : "bugfix",
+ "description" : "codewhisperer: file context fetching not considering file extension correctly"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for JetBrains Gateway version 2023.1 and for for IDEs based on the 2022.2 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.76.json b/.changes/1.76.json
new file mode 100644
index 0000000000..91a42114a2
--- /dev/null
+++ b/.changes/1.76.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2023-08-15",
+ "version" : "1.76",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Improve file context fetching for Python Typescript Javascript source files"
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Improve file context fetching for Java test files"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.77.json b/.changes/1.77.json
new file mode 100644
index 0000000000..f9bd340e92
--- /dev/null
+++ b/.changes/1.77.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2023-08-29",
+ "version" : "1.77",
+ "entries" : [ {
+ "type" : "removal",
+ "description" : "Removed support for 2022.2.x IDEs"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for Gateway 2023.1"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.78.json b/.changes/1.78.json
new file mode 100644
index 0000000000..14e163cc64
--- /dev/null
+++ b/.changes/1.78.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-09-08",
+ "version" : "1.78",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix 'not recognzied as an ... command' when connecting to CodeCatalyst Dev Environments on Windows"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.79.json b/.changes/1.79.json
new file mode 100644
index 0000000000..a0ce696af8
--- /dev/null
+++ b/.changes/1.79.json
@@ -0,0 +1,5 @@
+{
+ "date" : "2023-09-15",
+ "version" : "1.79",
+ "entries" : [ ]
+}
\ No newline at end of file
diff --git a/.changes/1.8-192.json b/.changes/1.8.json
similarity index 100%
rename from .changes/1.8-192.json
rename to .changes/1.8.json
diff --git a/.changes/1.80.json b/.changes/1.80.json
new file mode 100644
index 0000000000..f5f68a9bca
--- /dev/null
+++ b/.changes/1.80.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-09-29",
+ "version" : "1.80",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Authentication: When signing in to AWS Builder Id or IAM Identity Center (SSO), verify the device code matches instead of copy-pasting it"
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Improve the onboarding experience by showing a new onboarding tutorial to first-time users."
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue displaying SSO code on new UI in Windows"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.81.json b/.changes/1.81.json
new file mode 100644
index 0000000000..be8896abfa
--- /dev/null
+++ b/.changes/1.81.json
@@ -0,0 +1,5 @@
+{
+ "date" : "2023-09-29",
+ "version" : "1.81",
+ "entries" : [ ]
+}
\ No newline at end of file
diff --git a/.changes/1.82.json b/.changes/1.82.json
new file mode 100644
index 0000000000..9c376b8912
--- /dev/null
+++ b/.changes/1.82.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-10-06",
+ "version" : "1.82",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: Fixed an issue where the \"Learn CodeWhisperer\" page is shown for Gateway host"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.83.json b/.changes/1.83.json
new file mode 100644
index 0000000000..c4fb96c8d5
--- /dev/null
+++ b/.changes/1.83.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-10-13",
+ "version" : "1.83",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: improve auto-suggestions for additional languages"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.84.json b/.changes/1.84.json
new file mode 100644
index 0000000000..644863b333
--- /dev/null
+++ b/.changes/1.84.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-10-17",
+ "version" : "1.84",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Public preview for CodeWhisperer Enterprise: Enterprise customers can now customize CodeWhisperer to adopt and suggest code based on organization specific codebases."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.85.json b/.changes/1.85.json
new file mode 100644
index 0000000000..511551fefb
--- /dev/null
+++ b/.changes/1.85.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-10-27",
+ "version" : "1.85",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: reduce auto-suggestions when there is immediate right context"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.86.json b/.changes/1.86.json
new file mode 100644
index 0000000000..7a04d8d63c
--- /dev/null
+++ b/.changes/1.86.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2023-11-08",
+ "version" : "1.86",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Added the 'Setup Authentication for AWS Toolkit' page"
+ }, {
+ "type" : "feature",
+ "description" : "Added 2023.3 support"
+ }, {
+ "type" : "feature",
+ "description" : "auth: support `sso_session` for profiles in AWS shared ini files"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: Fix an issue where an IndexOutOfBoundException could be thrown when using CodeWhisperer"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.87.json b/.changes/1.87.json
new file mode 100644
index 0000000000..007d2b15ba
--- /dev/null
+++ b/.changes/1.87.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2023-11-10",
+ "version" : "1.87",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix issue where images in 'Authenticate' panel do not show up"
+ }, {
+ "type" : "deprecation",
+ "description" : "An upcoming release will remove support for JetBrains Gateway version 2023.2 and for for IDEs based on the 2022.3 platform"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.88.json b/.changes/1.88.json
new file mode 100644
index 0000000000..4d9706cd5c
--- /dev/null
+++ b/.changes/1.88.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-11-17",
+ "version" : "1.88",
+ "entries" : [ {
+ "type" : "bugfix",
+ "description" : "Fix issue where the toolkit calls the wrong CodeCatalyst service endpoint"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/1.89.json b/.changes/1.89.json
new file mode 100644
index 0000000000..a55632e863
--- /dev/null
+++ b/.changes/1.89.json
@@ -0,0 +1,14 @@
+{
+ "date" : "2023-11-26",
+ "version" : "1.89",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Uses Generative AI and automated reasoning to rewrite lines of code flagged for security vulnerabilities during a security scan."
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer now supports new IaC languages: JSON, YAML and Terraform."
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer security scans support typescript, csharp, json, yaml, tf and hcl files."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/2.0.json b/.changes/2.0.json
new file mode 100644
index 0000000000..691b7b5512
--- /dev/null
+++ b/.changes/2.0.json
@@ -0,0 +1,8 @@
+{
+ "date" : "2023-11-28",
+ "version" : "2.0",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Support for Amazon Q, your generative AI–powered assistant designed for work that can be tailored to your business, code, data, and operations."
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/2.1.json b/.changes/2.1.json
new file mode 100644
index 0000000000..6d03aa1369
--- /dev/null
+++ b/.changes/2.1.json
@@ -0,0 +1,17 @@
+{
+ "date" : "2023-12-04",
+ "version" : "2.1",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer: Simplify Learn More page"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: Security scans for Java no longer require build artifacts"
+ }, {
+ "type" : "bugfix",
+ "description" : "Amazon Q Transform: Fix an issue where the IDE may freeze after clicking \"Transform\""
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix JetBrains Gateway specific notifications being shown in non-Gateway IDEs"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/2.2.json b/.changes/2.2.json
new file mode 100644
index 0000000000..565c5d6087
--- /dev/null
+++ b/.changes/2.2.json
@@ -0,0 +1,38 @@
+{
+ "date" : "2023-12-13",
+ "version" : "2.2",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "CodeWhisperer security scans support ruby files."
+ }, {
+ "type" : "feature",
+ "description" : "Use MDE endpoint set by environment variable"
+ }, {
+ "type" : "feature",
+ "description" : "CodeWhisperer security scans now support Go files."
+ }, {
+ "type" : "bugfix",
+ "description" : "Normalize telemetry logging metrics for AmazonQ Transform"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix telemetry logging for new Amazon Q Code Transform telemetry updates"
+ }, {
+ "type" : "bugfix",
+ "description" : "CodeWhisperer: Increase polling frequency for security scans."
+ }, {
+ "type" : "bugfix",
+ "description" : "Fixed sign out button in the CodeWhisperer panel for Getting Started Page"
+ }, {
+ "type" : "bugfix",
+ "description" : "Fix issue where the CodeWhisperer status bar widget is visible in a remote development environment"
+ }, {
+ "type" : "bugfix",
+ "description" : "Amazon Q Transform: Fix to ensure backend gets necessary dependencies"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for 2022.3.x IDEs"
+ }, {
+ "type" : "removal",
+ "description" : "Removed support for Gateway 2023.2"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/next-release/bugfix-4c31acbd-6839-4b1f-a0ec-7e2c8402e6ff.json b/.changes/next-release/bugfix-4c31acbd-6839-4b1f-a0ec-7e2c8402e6ff.json
new file mode 100644
index 0000000000..58f5bcbeca
--- /dev/null
+++ b/.changes/next-release/bugfix-4c31acbd-6839-4b1f-a0ec-7e2c8402e6ff.json
@@ -0,0 +1,4 @@
+{
+ "type" : "bugfix",
+ "description": "Improved recursion when validating projects for Q Code Transform."
+}
\ No newline at end of file
diff --git a/.changes/next-release/bugfix-cf15b891-593c-4493-b594-8fda9f30d031.json b/.changes/next-release/bugfix-cf15b891-593c-4493-b594-8fda9f30d031.json
deleted file mode 100644
index 21a0fdcbec..0000000000
--- a/.changes/next-release/bugfix-cf15b891-593c-4493-b594-8fda9f30d031.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "bugfix",
- "description" : "Fix Rider building Lambda into incorrect folders"
-}
\ No newline at end of file
diff --git a/.changes/next-release/bugfix-d2b78feb-6a6d-4e7f-81dc-af756d3d92a0.json b/.changes/next-release/bugfix-d2b78feb-6a6d-4e7f-81dc-af756d3d92a0.json
deleted file mode 100644
index 66996ce4b4..0000000000
--- a/.changes/next-release/bugfix-d2b78feb-6a6d-4e7f-81dc-af756d3d92a0.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "bugfix",
- "description" : "Fix several uncaught exceptions caused by plugins being installed but not enabled"
-}
\ No newline at end of file
diff --git a/.changes/next-release/bugfix-eaed8849-efa2-4aa5-b236-57ce48eb595c.json b/.changes/next-release/bugfix-eaed8849-efa2-4aa5-b236-57ce48eb595c.json
deleted file mode 100644
index 4a17f11343..0000000000
--- a/.changes/next-release/bugfix-eaed8849-efa2-4aa5-b236-57ce48eb595c.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "bugfix",
- "description" : "Improve watching of the AWS profile files to incorporate changes made to the files outisde of the IDE"
-}
\ No newline at end of file
diff --git a/.changes/next-release/feature-09c9bb6d-d79b-4bbc-9241-735f0efc1098.json b/.changes/next-release/feature-09c9bb6d-d79b-4bbc-9241-735f0efc1098.json
new file mode 100644
index 0000000000..b24285a2ed
--- /dev/null
+++ b/.changes/next-release/feature-09c9bb6d-d79b-4bbc-9241-735f0efc1098.json
@@ -0,0 +1,4 @@
+{
+ "type" : "feature",
+ "description" : "Emit additional CodeTransform telemetry"
+}
\ No newline at end of file
diff --git a/.changes/next-release/feature-12cdfa65-4d7a-4c1e-9f61-04b388fce392.json b/.changes/next-release/feature-12cdfa65-4d7a-4c1e-9f61-04b388fce392.json
deleted file mode 100644
index a30810e4f4..0000000000
--- a/.changes/next-release/feature-12cdfa65-4d7a-4c1e-9f61-04b388fce392.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "feature",
- "description" : "Support colons (`:`) in credential profile names"
-}
\ No newline at end of file
diff --git a/.changes/next-release/feature-94919c2c-0d33-4f58-9c8e-f5b8cb661d1f.json b/.changes/next-release/feature-94919c2c-0d33-4f58-9c8e-f5b8cb661d1f.json
new file mode 100644
index 0000000000..de48006616
--- /dev/null
+++ b/.changes/next-release/feature-94919c2c-0d33-4f58-9c8e-f5b8cb661d1f.json
@@ -0,0 +1,4 @@
+{
+ "type" : "feature",
+ "description" : "Amazon Q Transform: Use the IDE Maven runner as a fallback"
+}
\ No newline at end of file
diff --git a/.changes/next-release/feature-f6a70036-5fd8-46f5-b70f-3841a04ca0c1.json b/.changes/next-release/feature-f6a70036-5fd8-46f5-b70f-3841a04ca0c1.json
deleted file mode 100644
index b92cd98f57..0000000000
--- a/.changes/next-release/feature-f6a70036-5fd8-46f5-b70f-3841a04ca0c1.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "feature",
- "description" : "Several enhancements to the UX around connecting to AWS including:\n- Making connection settings more visible (now visible in the AWS Explorer)\n- Automatically selecting 'default' profile if it exists\n- Better visibility of connection validation workflow (more information when unable to connect)\n- Handling of default regions on credential profile\n- Better UX around partitions\n- Adding ability to refresh connection from the UI"
-}
diff --git a/.editorconfig b/.editorconfig
index acd01cadb9..9498da2db1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -38,11 +38,32 @@ ij_java_use_single_class_imports = true
ij_java_while_brace_force = always
[{*.kts,*.kt}]
-# Temporary disabled this rule through .editorconfig due to https://youtrack.jetbrains.com/issue/KT-10974 and https://github.com/pinterest/ktlint/issues/527
-disabled_rules = import-ordering
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
+ij_kotlin_packages_to_use_import_on_demand = unset
+
+[{*.markdown,*.md}]
+ij_markdown_force_one_space_after_blockquote_symbol = true
+ij_markdown_force_one_space_after_header_symbol = true
+ij_markdown_force_one_space_after_list_bullet = true
+ij_markdown_force_one_space_between_words = true
+ij_markdown_keep_indents_on_empty_lines = false
+ij_markdown_max_lines_around_block_elements = 1
+ij_markdown_max_lines_around_header = 1
+ij_markdown_max_lines_between_paragraphs = 1
+ij_markdown_min_lines_around_block_elements = 1
+ij_markdown_min_lines_around_header = 1
+ij_markdown_min_lines_between_paragraphs = 1
+
+[*.properties]
+ij_properties_align_group_field_declarations = false
+ij_properties_keep_blank_lines = false
+ij_properties_key_value_delimiter = equals
+ij_properties_spaces_around_key_value_delimiter = false
[{*.yml,*.yaml}]
indent_size = 2
+
+[*.gold]
+insert_final_newline = false
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..8d5873b61b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# force git to use unix-style line endings
+* text=auto eol=lf
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index c3d877db91..83bac96961 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1,5 @@
-* @aws/aws-ides-team
\ No newline at end of file
+* @aws/aws-ides-team
+codewhisperer/ @aws/codewhisperer-team
+amazonq/ @aws/aws-mynah
+cwc/ @aws/aws-mynah
+codemodernizer @aws/elastic-gumby
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..a6dffbb5d0
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "gradle"
+ directory: "/"
+ target-branch: "main" # Don't run on forks, staging, etc.
+ schedule:
+ interval: "daily"
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 0bcc6c5308..829df05f25 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,3 +1,4 @@
+
## Types of changes
@@ -7,30 +8,14 @@
## Description
-
-## Motivation and Context
-
-
-## Related Issue(s)
-
-
-## Testing
-
-
-
-
-## Screenshots (if appropriate)
+
+
## Checklist
-
-
-- [ ] I have read the **README** document
-- [ ] I have read the **CONTRIBUTING** document
-- [ ] Local run of `gradlew check` succeeds
- [ ] My code follows the code style of this project
- [ ] I have added tests to cover my changes
-- [ ] All new and existing tests passed
-- [ ] A short description of the change has been added to the **CHANGELOG**
-
+- [ ] A short description of the change has been added to the **[CHANGELOG](https://github.com/aws/aws-toolkit-jetbrains/blob/master/CONTRIBUTING.md#contributing-via-pull-requests)** if the change is customer-facing in the IDE.
+- [ ] I have added metrics for my changes (if required)
+
## License
I confirm that my contribution is made under the terms of the Apache 2.0 license.
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index a5efeef16a..7c9acb14af 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -5,9 +5,9 @@ name: Unit Test
on:
push:
- branches: [ master ]
+ branches: [ main ]
pull_request:
- branches: [ master, feature/* ]
+ branches: [ main, feature/* ]
jobs:
build:
@@ -17,10 +17,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up JDK 11
+ - name: Set up JDK 17
uses: actions/setup-java@v1
with:
- java-version: 11
+ java-version: 17
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
diff --git a/.github/workflows/qodana.yml b/.github/workflows/qodana.yml
new file mode 100644
index 0000000000..bd38ec758e
--- /dev/null
+++ b/.github/workflows/qodana.yml
@@ -0,0 +1,27 @@
+name: Qodana
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main, feature/* ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ qodana:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: 'Qodana Scan'
+ uses: JetBrains/qodana-action@v2023.1.0
+ with:
+ cache-default-branch-only: true
+ - uses: github/codeql-action/upload-sarif@v2
+ with:
+ sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
diff --git a/.github/workflows/ssm-integ.yml b/.github/workflows/ssm-integ.yml
new file mode 100644
index 0000000000..6f1b82396b
--- /dev/null
+++ b/.github/workflows/ssm-integ.yml
@@ -0,0 +1,38 @@
+# This workflow tests the SSM plugin resolution and installation
+
+name: SSM Integration Test
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main, feature/* ]
+ # PRs only need to run this if the SSM plugin logic has changed
+ paths:
+ - 'jetbrains-core/src/software/aws/toolkits/jetbrains/services/ssm/SsmPlugin.kt'
+ - 'jetbrains-core/it/software/aws/toolkits/jetbrains/services/ssm/SsmPluginTest.kt'
+
+jobs:
+ build:
+ name: ${{ matrix.os }}
+
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+
+ steps:
+ - name: Support longpaths
+ run: git config --system core.longpaths true
+ if: ${{ matrix.os == 'windows-latest' }}
+ - uses: actions/checkout@v2
+ - name: Set up JDK 11
+ uses: actions/setup-java@v1
+ with:
+ java-version: 11
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Build with Gradle
+ run: ./gradlew :jetbrains-core:integrationTest --info --full-stacktrace --console plain --tests "software.aws.toolkits.jetbrains.services.ssm.SsmPluginTest"
diff --git a/.github/workflows/stale-issue-cleanup.yml b/.github/workflows/stale-issue-cleanup.yml
new file mode 100644
index 0000000000..ce1ed2b33c
--- /dev/null
+++ b/.github/workflows/stale-issue-cleanup.yml
@@ -0,0 +1,52 @@
+name: "Close stale issues"
+
+# Controls when the action will run.
+on:
+ # allows doing a manual trigger of the action and running it on a schedule at 10am PT everyday(converted since expression is anchored to UTC)
+ workflow_dispatch:
+ schedule:
+ - cron: "0 17 * * *"
+
+permissions: {}
+jobs:
+ cleanup:
+ permissions:
+ issues: write # to label, comment and close issues (aws-actions/stale-issue-cleanup)
+
+ runs-on: ubuntu-latest
+ name: Stale issues
+ steps:
+ - uses: aws-actions/stale-issue-cleanup@v6
+ with:
+ # Types of issues that will be processed
+ issue-types: issues
+ # Setting messages to an empty string will cause the automation to skip
+ # that category
+ stale-issue-message: This issue is missing required information and will be closed in 7 days. To keep the issue open, leave a comment.
+ # this has been added as a placeholder but will likely not be used because the timeline is set as 100 years
+ ancient-issue-message: This issue will automatically close because it has stalled for 1 year. To keep the issue open, leave a comment.
+
+ # These labels are required
+ stale-issue-label: closing-soon
+ exempt-issue-labels: no-autoclose
+ response-requested-label: needs-response
+ closed-for-staleness-label: closed-for-staleness
+
+ # Issue timing
+ days-before-stale: 30
+ days-before-close: 7
+ # setting this to 100 years to avoid closing valid old issues that are yet to be triaged
+ days-before-ancient: 36500
+
+ # If you don't want to mark an issue as being ancient based on a
+ # threshold of "upvotes", you can set this here. An "upvote" is
+ # the total number of +1, heart, hooray, and rocket reactions
+ # on an issue.
+ minimum-upvotes-to-exempt: 5
+
+ # Leave this alone, or set to a PAT for the action to use
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ # Testing/debugging options
+ loglevel: DEBUG
+ # Set dry-run to true to not perform label or close actions.
+ dry-run: false
diff --git a/.gitignore b/.gitignore
index 1043977aa2..0ac2cbe5b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,27 @@ Build/
venv/
guitest.log
stale_outputs_checked
+# sam build directory
+.aws-sam/
+
+# dotnet version
+global.json
+
+# local dev files
+unload-PythonCore-*
+unload-aws.toolkit-*
+jetbrains-core/bin/
+jetbrains-ultimate/bin/
+resources/bin/
+core/bin
+jetbrains-gateway/bin
+jetbrains-rider/bin
+sdk-codegen/bin
+ui-tests/bin
+
+#CodeWhispererChat
+
+/jetbrains-rider/testData/NuGet.config
+/jetbrains-core/ui/package-lock.json
+node_modules
+package-lock.json
diff --git a/.run/Run IDE - Core [2019.3].run.xml b/.run/Run IDE - Core [2019.3].run.xml
deleted file mode 100644
index b3666fc843..0000000000
--- a/.run/Run IDE - Core [2019.3].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Core [2020.1].run.xml b/.run/Run IDE - Core [2020.1].run.xml
deleted file mode 100644
index ced43c7866..0000000000
--- a/.run/Run IDE - Core [2020.1].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Core [2020.2].run.xml b/.run/Run IDE - Core [2020.2].run.xml
deleted file mode 100644
index cb5849e5be..0000000000
--- a/.run/Run IDE - Core [2020.2].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Core [2023.1].run.xml b/.run/Run IDE - Core [2023.1].run.xml
new file mode 100644
index 0000000000..2584b2295d
--- /dev/null
+++ b/.run/Run IDE - Core [2023.1].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
\ No newline at end of file
diff --git a/.run/Run IDE - Core [2023.2].run.xml b/.run/Run IDE - Core [2023.2].run.xml
new file mode 100644
index 0000000000..fec48ffc5b
--- /dev/null
+++ b/.run/Run IDE - Core [2023.2].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/.run/Run IDE - Core [2023.3].run.xml b/.run/Run IDE - Core [2023.3].run.xml
new file mode 100644
index 0000000000..fefbdd6a42
--- /dev/null
+++ b/.run/Run IDE - Core [2023.3].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/.run/Run IDE - Gateway [2023.3].run.xml b/.run/Run IDE - Gateway [2023.3].run.xml
new file mode 100644
index 0000000000..8293576e16
--- /dev/null
+++ b/.run/Run IDE - Gateway [2023.3].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/.run/Run IDE - Rider [2019.3].run.xml b/.run/Run IDE - Rider [2019.3].run.xml
deleted file mode 100644
index a0a23060e7..0000000000
--- a/.run/Run IDE - Rider [2019.3].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Rider [2020.1].run.xml b/.run/Run IDE - Rider [2020.1].run.xml
deleted file mode 100644
index c4f8eb37a6..0000000000
--- a/.run/Run IDE - Rider [2020.1].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Rider [2020.2].run.xml b/.run/Run IDE - Rider [2020.2].run.xml
deleted file mode 100644
index f39b43810c..0000000000
--- a/.run/Run IDE - Rider [2020.2].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Rider [2023.1].run.xml b/.run/Run IDE - Rider [2023.1].run.xml
new file mode 100644
index 0000000000..3e13ae5d8f
--- /dev/null
+++ b/.run/Run IDE - Rider [2023.1].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
\ No newline at end of file
diff --git a/.run/Run IDE - Rider [2023.2].run.xml b/.run/Run IDE - Rider [2023.2].run.xml
new file mode 100644
index 0000000000..98b8fc97a9
--- /dev/null
+++ b/.run/Run IDE - Rider [2023.2].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/.run/Run IDE - Rider [2023.3].run.xml b/.run/Run IDE - Rider [2023.3].run.xml
new file mode 100644
index 0000000000..dca8b5a5a4
--- /dev/null
+++ b/.run/Run IDE - Rider [2023.3].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/.run/Run IDE - Ultimate [2019.3].run.xml b/.run/Run IDE - Ultimate [2019.3].run.xml
deleted file mode 100644
index 14860e668c..0000000000
--- a/.run/Run IDE - Ultimate [2019.3].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Ultimate [2020.1].run.xml b/.run/Run IDE - Ultimate [2020.1].run.xml
deleted file mode 100644
index 6731609977..0000000000
--- a/.run/Run IDE - Ultimate [2020.1].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Ultimate [2020.2].run.xml b/.run/Run IDE - Ultimate [2020.2].run.xml
deleted file mode 100644
index b887740f2e..0000000000
--- a/.run/Run IDE - Ultimate [2020.2].run.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
\ No newline at end of file
diff --git a/.run/Run IDE - Ultimate [2023.1].run.xml b/.run/Run IDE - Ultimate [2023.1].run.xml
new file mode 100644
index 0000000000..3780738e22
--- /dev/null
+++ b/.run/Run IDE - Ultimate [2023.1].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
\ No newline at end of file
diff --git a/.run/Run IDE - Ultimate [2023.2].run.xml b/.run/Run IDE - Ultimate [2023.2].run.xml
new file mode 100644
index 0000000000..9c29e2e3a9
--- /dev/null
+++ b/.run/Run IDE - Ultimate [2023.2].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/.run/Run IDE - Ultimate [2023.3].run.xml b/.run/Run IDE - Ultimate [2023.3].run.xml
new file mode 100644
index 0000000000..0ecbfa1b87
--- /dev/null
+++ b/.run/Run IDE - Ultimate [2023.3].run.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0dddcf3fad..745b63a485 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,454 @@
+# _2.1_ (2023-12-04)
+- **(Feature)** CodeWhisperer: Simplify Learn More page
+- **(Bug Fix)** CodeWhisperer: Security scans for Java no longer require build artifacts
+- **(Bug Fix)** Amazon Q Transform: Fix an issue where the IDE may freeze after clicking "Transform"
+- **(Bug Fix)** Fix JetBrains Gateway specific notifications being shown in non-Gateway IDEs
+
+# _2.0_ (2023-11-28)
+- **(Feature)** Support for Amazon Q, your generative AI–powered assistant designed for work that can be tailored to your business, code, data, and operations.
+
+# _1.89_ (2023-11-26)
+- **(Feature)** CodeWhisperer: Uses Generative AI and automated reasoning to rewrite lines of code flagged for security vulnerabilities during a security scan.
+- **(Feature)** CodeWhisperer now supports new IaC languages: JSON, YAML and Terraform.
+- **(Feature)** CodeWhisperer security scans support typescript, csharp, json, yaml, tf and hcl files.
+
+# _1.88_ (2023-11-17)
+- **(Bug Fix)** Fix issue where the toolkit calls the wrong CodeCatalyst service endpoint
+
+# _1.87_ (2023-11-10)
+- **(Bug Fix)** Fix issue where images in 'Authenticate' panel do not show up
+- **(Deprecation)** An upcoming release will remove support for JetBrains Gateway version 2023.2 and for for IDEs based on the 2022.3 platform
+
+# _1.86_ (2023-11-08)
+- **(Feature)** Added the 'Setup Authentication for AWS Toolkit' page
+- **(Feature)** Added 2023.3 support
+- **(Feature)** auth: support `sso_session` for profiles in AWS shared ini files
+- **(Bug Fix)** CodeWhisperer: Fix an issue where an IndexOutOfBoundException could be thrown when using CodeWhisperer
+
+# _1.85_ (2023-10-27)
+- **(Feature)** CodeWhisperer: reduce auto-suggestions when there is immediate right context
+
+# _1.84_ (2023-10-17)
+- **(Feature)** Public preview for CodeWhisperer Enterprise: Enterprise customers can now customize CodeWhisperer to adopt and suggest code based on organization specific codebases.
+
+# _1.83_ (2023-10-13)
+- **(Feature)** CodeWhisperer: improve auto-suggestions for additional languages
+
+# _1.82_ (2023-10-06)
+- **(Bug Fix)** CodeWhisperer: Fixed an issue where the "Learn CodeWhisperer" page is shown for Gateway host
+
+# _1.80_ (2023-09-29)
+- **(Feature)** Authentication: When signing in to AWS Builder Id or IAM Identity Center (SSO), verify the device code matches instead of copy-pasting it
+- **(Feature)** CodeWhisperer: Improve the onboarding experience by showing a new onboarding tutorial to first-time users.
+- **(Bug Fix)** Fix issue displaying SSO code on new UI in Windows
+
+# _1.81_ (2023-09-29)
+
+# _1.79_ (2023-09-15)
+
+# _1.78_ (2023-09-08)
+- **(Bug Fix)** Fix 'not recognzied as an ... command' when connecting to CodeCatalyst Dev Environments on Windows
+
+# _1.77_ (2023-08-29)
+- **(Removal)** Removed support for 2022.2.x IDEs
+- **(Removal)** Removed support for Gateway 2023.1
+
+# _1.76_ (2023-08-15)
+- **(Feature)** CodeWhisperer: Improve file context fetching for Python Typescript Javascript source files
+- **(Feature)** CodeWhisperer: Improve file context fetching for Java test files
+
+# _1.75_ (2023-08-03)
+- **(Feature)** Add support for Lambda runtime Python 3.11
+- **(Bug Fix)** codewhisperer: file context fetching not considering file extension correctly
+- **(Deprecation)** An upcoming release will remove support for JetBrains Gateway version 2023.1 and for for IDEs based on the 2022.2 platform
+
+# _1.74_ (2023-07-25)
+- **(Feature)** Explorer is automatically refreshed with new credentials when they are added to credential file.
+- **(Feature)** Added 2023.2 support
+- **(Bug Fix)** Fix 'No display name is specified for configurable' in 2023.2
+
+# _1.73_ (2023-07-19)
+- **(Feature)** CodeWhisperer: Improve Java suggestion quality with enhanced file context fetching
+- **(Bug Fix)** CodeWhisperer: Run read operation in the background thread without runReadAction
+- **(Bug Fix)** CodeWhisperer: Fix an issue where CodeWhisperer would stuck in the invocation state indefinitely
+
+# _1.72_ (2023-07-11)
+- **(Feature)** CodeWhisperer: Improve suggestion quality with enhanced file context fetching
+- **(Bug Fix)** Fix AWS Lambda configuration window resize ([#3657](https://github.com/aws/aws-toolkit-jetbrains/issues/3657))
+
+# _1.71_ (2023-07-06)
+- **(Bug Fix)** Fix inproper request format when sending empty supplemental context
+
+# _1.70_ (2023-06-27)
+- **(Feature)** CodeWhisperer improves auto-suggestions for tsx and jsx
+- **(Bug Fix)** Show re-authenticate prompt when invoking CodeWhisperer APIs while connection expired
+
+# _1.69_ (2023-06-13)
+- **(Feature)** CodeWhisperer improves auto-suggestions for python csharp typescript and javascript
+- **(Feature)** Removed 10 secs delay when connecting to Dev environments of Small Instance Size
+- **(Feature)** CodeWhisperer: Improve file context fetching logic
+- **(Bug Fix)** Inlay not supported exception in injected editor
+- **(Bug Fix)** fix right context merging not accounting userinput, which result in cases CodeWhisperer still show recommendation where user already type the content of recommendation out thus no character is being inserted by CodeWhisperer
+- **(Bug Fix)** Add error notification to upgrade SAM CLI v1.85-1.86.1 if on windows
+- **(Bug Fix)** Always use AWS smile logo to reduce confusion if users are on the 'New UI' ([#3636](https://github.com/aws/aws-toolkit-jetbrains/issues/3636))
+- **(Removal)** Remove support for Gateway 2022.2 and 2022.3.
+
+# _1.68_ (2023-05-30)
+- **(Feature)** CodeWhisperer supports application wide connections
+- **(Feature)** CodeWhisperer improves auto-suggestions for java
+- **(Bug Fix)** Fix threading issue preventing SAM Applications from opening in Rider 2023.1
+- **(Bug Fix)** Fix issue reconnecting to CodeWhisperer using an Identity Center directory outside of us-east-1 ([#3662](https://github.com/aws/aws-toolkit-jetbrains/issues/3662))
+- **(Bug Fix)** Fix 'null' is not a connection when authenticating to CodeWhisperer
+- **(Bug Fix)** CodeWhisperer: user is sometimes required to re-login before token expiration
+- **(Bug Fix)** Fix issue where the "Do not ask again" option is not respected when switching connections on CodeWhisperer/CodeCatalyst
+- **(Deprecation)** An upcoming release will remove support for JetBrains Gateway version 2022.2 and version 2022.3
+- **(Removal)** Remove support for Aurora MySQL v1 ([#3356](https://github.com/aws/aws-toolkit-jetbrains/issues/3356))
+- **(Removal)** Removed support for 2022.1.x IDEs
+
+# _1.67_ (2023-04-27)
+- **(Feature)** Using the least permissive set of scopes for features during BuilderID/SSO login. Using the same connection for multiple features will request additional scopes to be used.
+- **(Feature)** Add support for Lambda Runtime Java17
+- **(Bug Fix)** Fix the Add Connection Dialog box references to the correct documentation pages
+- **(Bug Fix)** Fix thread access during validation of SAM templates
+- **(Bug Fix)** [CodeWhisperer]: login session length should increase to it's expected length. Users will now see less frequent expired sessions.
+- **(Bug Fix)** Improve handling of disk errors related to SSO and align folder permissions with AWS CLI
+
+# _1.66_ (2023-04-19)
+- **(Feature)** Display current space and project name on status bar while working in a CodeCatalyst Dev Environment
+- **(Feature)** Add support for Lambda runtime Python 3.10
+- **(Bug Fix)** Fix `java.lang.Throwable: Invalid html: tag inserted automatically and shouldn't be used` ([#3608](https://github.com/aws/aws-toolkit-jetbrains/issues/3608))
+- **(Bug Fix)** Fix issue where nothing happens when trying to create an empty Dev Environment
+
+# _1.65_ (2023-04-13)
+- **(Feature)** [CodeWhisperer]: Introducing "Stop code scan" feature where users will be able to stop the ongoing code scan and immediately start a new one.
+- **(Feature)** [CodeWhisperer]: Automatic import recommendations
+- **(Feature)** [CodeWhisperer]: Now supports cross region calls.
+- **(Feature)** Attempt to download IDE thin client earlier in the CodeCatalyst Dev Environment connection process
+- **(Feature)** [CodeWhisperer]: New supported programming languages: C, C++, Go, Kotlin, Php, Ruby, Rust, Scala, Shell, Sql.
+- **(Bug Fix)** Include more information in the Dev Environment status tooltip
+- **(Bug Fix)** Provide consistent UX in all Dev Environment wizard variants
+- **(Bug Fix)** Fix 'MissingResourceException: Registry key is not defined'
+- **(Bug Fix)** [CodeWhisperer]: Multiple bug fixes to improve user experience
+- **(Removal)** Drop support for the Node.js 12.x Lambda runtime
+- **(Removal)** Drop support for the .NET Core 3.1 Lambda runtime
+
+# _1.64_ (2023-03-29)
+- **(Breaking Change)** Required SAM CLI upgrade to v1.78.0 to for using Sync Serverless Application option.
+- **(Feature)** Support for RDS MariaDB instances ([#3530](https://github.com/aws/aws-toolkit-jetbrains/issues/3530))
+- **(Feature)** Added 2023.1 support
+- **(Deprecation)** An upcoming release will remove support for IDEs based on the 2022.1 platform
+
+# _1.63_ (2023-03-24)
+- **(Bug Fix)** Fix issue where multiple Builder ID entries show up in connection list
+- **(Bug Fix)** Fix temporary deadlock when user fails to complete reauthentication request
+- **(Bug Fix)** Only allow cloning a repository from CodeCatalyst if it's hosted on CodeCatalyst
+
+# _1.62_ (2023-03-20)
+- **(Bug Fix)** Show friendlier application name when signing in using SSO
+- **(Bug Fix)** Fix confusing experience when attempting to sign in to multiple Builder IDs
+
+# _1.61_ (2023-02-17)
+- **(Bug Fix)** Authenticating through the browser now requires users to manually enter a user verification code for SSO/AWS Builder ID
+- **(Bug Fix)** Fix NPE that may occur when installing the toolkit for the first time ([#3433](https://github.com/aws/aws-toolkit-jetbrains/issues/3433))
+- **(Bug Fix)** Fix network calls cant be made inside read/write action exception thrown from CodeWhisperer ([#3423](https://github.com/aws/aws-toolkit-jetbrains/issues/3423))
+
+# _1.60_ (2023-02-01)
+- **(Bug Fix)** Fix Small Dev Environment instance sizes not connecting to the thin clients
+
+# _1.59_ (2023-01-27)
+- **(Feature)** Added an option to submit feedback for the AWS Toolkit in JetBrains Gateway
+
+# _1.58_ (2023-01-12)
+- **(Feature)** CodeWhisperer: more responsive Auto-Suggestions
+- **(Feature)** Added Nodejs18.x Lambda runtime support
+- **(Bug Fix)** Fix regression in requirements.txt detection ([#3041](https://github.com/aws/aws-toolkit-jetbrains/issues/3041))
+- **(Bug Fix)** Fix `com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException when choosing an input template in Lambda Run Configurations` ([#3359](https://github.com/aws/aws-toolkit-jetbrains/issues/3359))
+- **(Bug Fix)** Fix Lambda Python console encoding issue ([#2802](https://github.com/aws/aws-toolkit-jetbrains/issues/2802))
+
+# _1.57_ (2022-12-15)
+- **(Feature)** Change reauthentication prompt to be non-distruptive notification.
+- **(Bug Fix)** Add do not show again button for CodeWhisperer accountless usage notification
+- **(Bug Fix)** Fix CodeWhisperer status widget is shown even when users are disconnected
+
+# _1.56_ (2022-12-08)
+- **(Bug Fix)** Remove redundant calls in certain Gateway UI panels
+- **(Bug Fix)** Fix threading issue while attempting to login to CodeCatalyst
+- **(Bug Fix)** Only list dev environments under projects that users are a member of
+- **(Bug Fix)** Fix 'Learn more' link in Gateway 2022.2
+- **(Bug Fix)** Fix connection issue with CodeCatalyst when user is already logged into CodeWhisperer
+
+# _1.55_ (2022-12-01)
+- **(Feature)** Amazon CodeCatalyst: Connect JetBrains to your remote Dev Environments.
+- **(Feature)** Amazon CodeCatalyst: Clone your repositories to your local machine.
+- **(Feature)** Amazon CodeCatalyst: Connect using your AWS Builder ID.
+
+# _1.54_ (2022-11-28)
+- **(Feature)** Amazon CodeWhisperer now supports JavaScript for Security Scan to catch security vulnerabilities.
+- **(Feature)** Amazon CodeWhisperer recommendations are more context aware. We are removing the overlaps from CodeWhisperer suggestions specifically when the cursor is inside a code block.
+- **(Feature)** Amazon CodeWhisperer now supports TypeScript and C# programming languages.
+- **(Feature)** Amazon CodeWhisperer is now available as a supported feature and no longer an experimental feature.
+- **(Feature)** Amazon CodeWhisperer now adds new access methods with AWS Builder ID and AWS IAM Identity Center to enable and get started.
+
+# _1.53_ (2022-11-23)
+- **(Feature)** Sync Serverless Application(SAM Accelerate)
+- **(Feature)** New experiment to allow injection of AWS Connection details (region/credentials) into Golang Run Configurations
+- **(Removal)** Removed support for 2021.3.x IDEs
+
+# _1.52_ (2022-10-19)
+- **(Feature)** Added 2022.3 support
+- **(Bug Fix)** Fix `credential_process` retrieval when command contains quoted arguments on Windows ([#3322](https://github.com/aws/aws-toolkit-jetbrains/issues/3322))
+- **(Deprecation)** An upcoming release will remove support for IDEs based on the 2021.3 platform
+- **(Bug Fix)** Fix `java.lang.IllegalStateException: Region provider data is missing default data` ([#3264](https://github.com/aws/aws-toolkit-jetbrains/issues/3264))
+
+# _1.51_ (2022-09-22)
+- **(Feature)** Resources (in AWS Explorer) can list more resource types for EC2, IoT, RDS, Redshift, NetworkManager, and other services
+- **(Feature)** CodeWhisperer now supports .jsx files
+- **(Bug Fix)** CodeWhisperer fixes
+
+# _1.50_ (2022-08-23)
+- **(Bug Fix)** Fix opening toolwindow tabs in incorrect thread in Cloudwatch Logs
+- **(Bug Fix)** Fix hitting enter inside braces will produce an extra newline ([#3270](https://github.com/aws/aws-toolkit-jetbrains/issues/3270))
+- **(Deprecation)** Remove support for deprecated Lambda runtime Python 3.6
+- **(Removal)** Removed support for 2021.2.x IDEs
+
+# _1.49_ (2022-08-11)
+- **(Bug Fix)** Fix IllegalCallableAccessException thrown in several UI panels ([#3228](https://github.com/aws/aws-toolkit-jetbrains/issues/3228))
+- **(Bug Fix)** Fix to stop showing CodeWhisperer's welcome page every time on project start
+- **(Deprecation)** An upcoming release will remove support for IDEs based on the 2021.2 platform
+
+# _1.48_ (2022-07-26)
+- **(Bug Fix)** Fix to display appropriate error messaging for filtering Cloudwatch Streams using search patterns failures
+
+# _1.47_ (2022-07-08)
+- **(Removal)** Remove Cloud Debugging of ECS Services (beta)
+
+# _1.46_ (2022-06-28)
+- **(Feature)** Nodejs16.x Lambda runtime support
+- **(Bug Fix)** Fix broken user UI due to 'Enter' handler override ([#3193](https://github.com/aws/aws-toolkit-jetbrains/issues/3193))
+- **(Bug Fix)** Fix SSM plugin install on deb/rpm systems ([#3130](https://github.com/aws/aws-toolkit-jetbrains/issues/3130))
+
+# _1.45_ (2022-06-23)
+- **(Feature)** [CodeWhisperer](https://aws.amazon.com/codewhisperer) uses machine learning to generate code suggestions from the existing code and comments in your IDE. Supported languages include: Java, Python, and JavaScript.
+- **(Feature)** Added 2022.2 support
+- **(Bug Fix)** Fix .NET Lambda debugging regression in 2022.1.1
+- **(Removal)** Removed support for 2021.1.x IDEs
+
+# _1.44_ (2022-06-01)
+- **(Feature)** Add warning to indicate time delay in SQS queue deletion
+- **(Bug Fix)** Fixed issue with uncaught exception in resource cache ([#3098](https://github.com/aws/aws-toolkit-jetbrains/issues/3098))
+- **(Bug Fix)** Don't attempt to setup run configurations for test code ([#3075](https://github.com/aws/aws-toolkit-jetbrains/issues/3075))
+- **(Bug Fix)** Fix toolWindow not running in EDT
+- **(Bug Fix)** Handle Lambda pending states while updating function ([#2984](https://github.com/aws/aws-toolkit-jetbrains/issues/2984))
+- **(Bug Fix)** Fix modality issue when opening a CloudWatch log stream in editor ([#2991](https://github.com/aws/aws-toolkit-jetbrains/issues/2991))
+- **(Bug Fix)** Workaround regression with ARN console navigation in JSON files
+- **(Bug Fix)** Fix 'The project directory does not exist!' when creating SAM/Gradle projects when the Android plugin is also installed
+- **(Deprecation)** An upcoming release will remove support for IDEs based on the 2021.1 platform
+
+# _1.43_ (2022-04-14)
+- **(Bug Fix)** Fix regression in DataGrip 2022.1 caused by new APIs in the platform ([#3125](https://github.com/aws/aws-toolkit-jetbrains/issues/3125))
+
+# _1.42_ (2022-04-13)
+- **(Feature)** Add support for 2022.1
+
+# _1.41_ (2022-03-25)
+- **(Feature)** Adding Go (Golang) as a supported language for code binding generation through the EventBridge Schemas service
+
+# _1.40_ (2022-03-07)
+- **(Bug Fix)** Fix logged error due to ARN contributor taking too long ([#3085](https://github.com/aws/aws-toolkit-jetbrains/issues/3085))
+
+# _1.39_ (2022-03-03)
+- **(Feature)** Added in 1.37: The toolkit will now offer to open ARNs present in your code editor in your browser
+- **(Feature)** Added support for .NET 6 runtime for creating and debugging SAM functions
+- **(Bug Fix)** Fix issue where console federation with long-term credentails results in session with no permissions
+
+# _1.38_ (2022-02-17)
+- **(Bug Fix)** Fix StringIndexOutOfBoundsException ([#3025](https://github.com/aws/aws-toolkit-jetbrains/issues/3025))
+- **(Bug Fix)** Fix regression preventing ECR repository creation
+- **(Bug Fix)** Fix Lambda run configuration exception while setting handler architecture
+- **(Bug Fix)** Fix image-based Lambda debugging for Python 3.6
+- **(Removal)** Removed support for 2020.3.x IDEs
+
+# _1.37_ (2022-01-06)
+- **(Feature)** Add SAM Lambda ARM support
+- **(Bug Fix)** Fix plugin deprecation warning in DynamoDB viewer ([#2987](https://github.com/aws/aws-toolkit-jetbrains/issues/2987))
+- **(Deprecation)** An upcoming release will remove support for IDEs based on the 2020.3 platform
+
+# _1.36_ (2021-11-23)
+
+# _1.35_ (2021-11-18)
+- **(Feature)** Respect the `duration_seconds` property when assuming a role if set on the profile
+- **(Feature)** Added 2021.3 support
+- **(Feature)** Added support for AWS profiles that use the `credential_source` key
+- **(Bug Fix)** Fix Python Lambda gutter icons not generating handler paths relative to the requirements.txt file ([#2853](https://github.com/aws/aws-toolkit-jetbrains/issues/2853))
+- **(Bug Fix)** Fix file changes not being saved before running Local Lambda run configurations ([#2889](https://github.com/aws/aws-toolkit-jetbrains/issues/2889))
+- **(Bug Fix)** Fix incorrect behavior with RDS Secrets Manager Auth when SSH tunneling is enabled ([#2781](https://github.com/aws/aws-toolkit-jetbrains/issues/2781))
+- **(Bug Fix)** Fix copying out of the DynamoDB table viewer copying the in-memory representation instead of displayed value
+- **(Bug Fix)** Fix error about write actions when opening files from the S3 browser ([#2913](https://github.com/aws/aws-toolkit-jetbrains/issues/2913))
+- **(Bug Fix)** Fix NullPointerException on combobox browse components ([#2866](https://github.com/aws/aws-toolkit-jetbrains/issues/2866))
+- **(Removal)** Dropped support for the no longer supported Lambda runtime .NET Core 2.1
+
+# _1.34_ (2021-10-21)
+- **(Bug Fix)** Fix issue in Resources where some S3 Buckets fail to open
+- **(Bug Fix)** Fix null exception when view documentation action executed for types with missing doc urls
+- **(Bug Fix)** Fix uncaught exception when a resource does not support LIST in a certain region.
+
+# _1.33_ (2021-10-14)
+- **(Feature)** Surface read-only support for hundreds of resources under the Resources node in the AWS Explorer
+- **(Feature)** Amazon DynamoDB table viewer
+- **(Bug Fix)** Changed error message 'Command did not exist successfully' to 'Command did not exit successfully'
+- **(Bug Fix)** Fixed spelling and grammar in MessagesBundle.properties
+- **(Bug Fix)** Fix not being able to start Rider debugger against a Lambda running on a host ARM machine
+- **(Bug Fix)** Fix SSO login not being triggered when the auth code is invalid ([#2796](https://github.com/aws/aws-toolkit-jetbrains/issues/2796))
+- **(Removal)** Removed support for 2020.2.x IDEs
+- **(Removal)** Dropped support for the no longer supported Lambda runtime Python 2.7
+- **(Removal)** Dropped support for the no longer supported Lambda runtime Node.js 10.x
+
+# _1.32_ (2021-09-07)
+- **(Bug Fix)** Fix IDE error about context.module being null ([#2776](https://github.com/aws/aws-toolkit-jetbrains/issues/2776))
+- **(Bug Fix)** Fix NullPointerException calling isInTestSourceContent ([#2752](https://github.com/aws/aws-toolkit-jetbrains/issues/2752))
+
+# _1.31_ (2021-08-17)
+- **(Feature)** Add support for Python 3.9 Lambdas
+- **(Bug Fix)** Fix regression in SAM run configurations using file-based input ([#2762](https://github.com/aws/aws-toolkit-jetbrains/issues/2762))
+- **(Bug Fix)** Fix CloudWatch sorting ([#2737](https://github.com/aws/aws-toolkit-jetbrains/issues/2737))
+
+# _1.30_ (2021-08-05)
+- **(Feature)** Add ability to view bucket by entering bucket name/URI
+- **(Bug Fix)** Fix CWL last event sorting ([#2737](https://github.com/aws/aws-toolkit-jetbrains/issues/2737))
+- **(Bug Fix)** Fix Go Lambda handler resolving into Go standard library ([#2730](https://github.com/aws/aws-toolkit-jetbrains/issues/2730))
+- **(Bug Fix)** Fix `ActionPlaces.isPopupPlace` error after opening the AWS connection settings menu ([#2736](https://github.com/aws/aws-toolkit-jetbrains/issues/2736))
+- **(Bug Fix)** Fix some warnings due to slow operations on EDT ([#2735](https://github.com/aws/aws-toolkit-jetbrains/issues/2735))
+- **(Bug Fix)** Fix Java Lambda run marker issues and disable runmarker processing in tests and language-injected text fragments
+
+# _1.29_ (2021-07-20)
+- **(Feature)** When uploading a file to S3, the content type is now set accoriding to the files extension
+- **(Bug Fix)** Fix being unable to update Lambda configuration if the Image packaging type
+
+# _1.28_ (2021-07-12)
+- **(Breaking Change)** Python 2.7 Lambda template removed from New Project Wizard
+- **(Feature)** Adding the ability to inject credentials/region into existing IntelliJ IDEA and PyCharm Run Configurations (e.g Application, JUnit, Python, PyTest). This requires experiments `aws.feature.javaRunConfigurationExtension` / `aws.feature.pythonRunConfigurationExtension`, see [Enabling Experiments](https://github.com/aws/aws-toolkit-jetbrains/blob/master/README.md#experimental-features)
+- **(Feature)** Add support for updating tags during SAM deployment
+- **(Feature)** (Experimental) Adding ability to create a local terminal using the currently selected AWS connection (experiment ID `aws.feature.connectedLocalTerminal`, see [Enabling Experiments](https://github.com/aws/aws-toolkit-jetbrains/blob/master/README.md#experimental-features)) [#2151](https://github.com/aws/aws-toolkit-jetbrains/issues/2151)
+- **(Feature)** Add support for pulling images from ECR
+- **(Bug Fix)** Fix missing text in the View S3 bucket with prefix dialog
+- **(Bug Fix)** Improved performance of listing S3 buckets in certain situations
+- **(Bug Fix)** Fix copying action in CloudWatch Logs Stream and Event Time providing epoch time instead of displayed value
+- **(Bug Fix)** Fix using message bus after project has been closed (Fixes [#2615](https://github.com/aws/aws-toolkit-jetbrains/issues/2615))
+- **(Bug Fix)** Fix S3 bucket viewer actions being triggered by short cuts even if it is not focused
+- **(Bug Fix)** Don't show Lambda run configuration suggestions on Go test code
+- **(Bug Fix)** Fix being unable to create Python 3.8 Image-based Lambdas in New Project wizard
+- **(Bug Fix)** Fixed showing templates that were not for Image-based Lambdas when Image is selected in New Project wizard
+- **(Deprecation)** An upcoming release will remove support for IDEs based on the 2020.2 platform
+
+# _1.27_ (2021-05-24)
+- **(Feature)** Add support for AppRunner. Create/delete/pause/resume/deploy and view logs for your AppRunner services.
+- **(Feature)** Add support for building and pushing local images to ECR
+- **(Feature)** Add support for running/debugging Typescript Lambdas
+- **(Bug Fix)** Fix Rider locking up when right clicking a Lambda in the AWS Explorer with a dotnet runtime in 2021.1
+- **(Bug Fix)** While debugging a Lambda function locally, make sure stopping the debugger will always stop the underlying SAM cli process ([#2564](https://github.com/aws/aws-toolkit-jetbrains/issues/2564))
+
+# _1.26_ (2021-04-14)
+- **(Feature)** Add support for creating/debugging Golang Lambdas ([#649](https://github.com/aws/aws-toolkit-jetbrains/issues/649))
+- **(Bug Fix)** Fix breaking run configuration gutter icons when the IDE has no languages installed that support Lambda local runtime ([#2504](https://github.com/aws/aws-toolkit-jetbrains/issues/2504))
+- **(Bug Fix)** Fix issue preventing deployment of CloudFormation templates with empty values ([#1498](https://github.com/aws/aws-toolkit-jetbrains/issues/1498))
+- **(Bug Fix)** Fix cloudformation stack events failing to update after reaching a final state ([#2519](https://github.com/aws/aws-toolkit-jetbrains/issues/2519))
+- **(Bug Fix)** Fix the Local Lambda run configuration always reseting the environemnt variables to defaults when using templates ([#2509](https://github.com/aws/aws-toolkit-jetbrains/issues/2509))
+- **(Bug Fix)** Fix being able to interact with objects from deleted buckets ([#1601](https://github.com/aws/aws-toolkit-jetbrains/issues/1601))
+- **(Removal)** Remove support for 2020.1
+- **(Removal)** Lambda gutter icons no longer take deployed Lambdas into account due to accuracy and performance issues
+
+# _1.25_ (2021-03-10)
+- **(Breaking Change)** Minimum SAM CLI version is now 1.0.0
+- **(Feature)** Debugging Python based Lambdas locally now have the Python interactive console enabled (Fixes [#1165](https://github.com/aws/aws-toolkit-jetbrains/issues/1165))
+- **(Feature)** Add a setting for how the AWS profiles notification is shown ([#2408](https://github.com/aws/aws-toolkit-jetbrains/issues/2408))
+- **(Feature)** Deleting resources now requires typing "delete me" instead of the resource name
+- **(Feature)** Add support for 2021.1
+- **(Feature)** Allow deploying SAM templates from the CloudFormaton node ([#2166](https://github.com/aws/aws-toolkit-jetbrains/issues/2166))
+- **(Bug Fix)** Improve error messages when properties are not found in templates ([#2449](https://github.com/aws/aws-toolkit-jetbrains/issues/2449))
+- **(Bug Fix)** Fix resource selectors assuming every region has every service ([#2435](https://github.com/aws/aws-toolkit-jetbrains/issues/2435))
+- **(Bug Fix)** Docker is now validated before building the Lambda when running and debugging locally (Fixes [#2418](https://github.com/aws/aws-toolkit-jetbrains/issues/2418))
+- **(Bug Fix)** Fixed several UI inconsistencies in the S3 bucket viewer actions
+- **(Bug Fix)** Fix showing stack status notification on opening existing CloudFormation stack ([#2157](https://github.com/aws/aws-toolkit-jetbrains/issues/2157))
+- **(Bug Fix)** Processes using the Step system (e.g. SAM build) can now be stopped ([#2418](https://github.com/aws/aws-toolkit-jetbrains/issues/2418))
+- **(Bug Fix)** Fixed the Remote Lambda Run Configuration failing to load the list of functions if not in active region
+- **(Deprecation)** 2020.1 support will be removed in the next release
+
+# _1.24_ (2021-02-17)
+- **(Feature)** RDS serverless databases are now visible in the RDS node in the explorer
+- **(Bug Fix)** Fix transient 'Aborted!' message on successful SAM CLI local Lambda execution
+- **(Bug Fix)** Fix being unable to open the file browser in the Schemas download panel
+- **(Bug Fix)** Fix being unable to type/copy paste into the SAM local run config's template path textbox
+- **(Bug Fix)** Fix Secrets Manager-based databse auth throwing NullPointer when editing settings in 2020.3.2 (Fixes [#2403](https://github.com/aws/aws-toolkit-jetbrains/issues/2403))
+- **(Bug Fix)** Fix making an un-needed service call on IDE startup ([#2426](https://github.com/aws/aws-toolkit-jetbrains/issues/2426))
+
+# _1.23_ (2021-02-04)
+- **(Feature)** Add "Copy S3 URI" to S3 objects ([#2208](https://github.com/aws/aws-toolkit-jetbrains/issues/2208))
+- **(Feature)** Add Dotnet5 Lambda support (Image only)
+- **(Feature)** Add option to view past object versions in S3 file editor
+- **(Feature)** Nodejs14.x Lambda support
+- **(Feature)** Update Lambda max memory to 10240
+- **(Bug Fix)** Re-add environment variable settings to SAM template based run configurations ([#2282](https://github.com/aws/aws-toolkit-jetbrains/issues/2282))
+- **(Bug Fix)** Fix error thrown on profile refresh if removing a profile that uses source_profile ([#2309](https://github.com/aws/aws-toolkit-jetbrains/issues/2309))
+- **(Bug Fix)** Fix NodeJS and Python breakpoints failing to hit sometimes
+- **(Bug Fix)** Speed up loading CloudFormation resources
+- **(Bug Fix)** Fix not invalidating credentials when a `source_profile` is updated
+- **(Bug Fix)** Fix cell based copying in CloudWatch Logs ([#2333](https://github.com/aws/aws-toolkit-jetbrains/issues/2333))
+- **(Bug Fix)** Fix certain S3 buckets being unable to be shown in the explorer ([#2342](https://github.com/aws/aws-toolkit-jetbrains/issues/2342))
+- **(Bug Fix)** Fix exception thrown in the new project wizard when run immediately after the toolkit is installed
+- **(Bug Fix)** Fixing issue with SSO refresh locking UI thread ([#2224](https://github.com/aws/aws-toolkit-jetbrains/issues/2224))
+
+# _1.22_ (2020-12-01)
+- **(Feature)** Container Image Support in Lambda
+- **(Bug Fix)** Fix update Lambda code for compiled languages ([#2231](https://github.com/aws/aws-toolkit-jetbrains/issues/2231))
+
+# _1.21_ (2020-11-24)
+- **(Breaking Change)** Remove support for 2019.3, 2020.1 is the new minimum version
+- **(Feature)** Add copy Logical/Physical ID actions to Stack View [#2165](https://github.com/aws/aws-toolkit-jetbrains/issues/2165)
+- **(Feature)** Add SQS AWS Explorer node and the ability to send/poll for messages
+- **(Feature)** Add the ability to search CloudWatch Logs using CloudWatch Logs Insights
+- **(Feature)** Add copy actions to CloudFormation outputs ([#2179](https://github.com/aws/aws-toolkit-jetbrains/issues/2179))
+- **(Feature)** Support for the 2020.3 family of IDEs
+- **(Feature)** Add an AWS Explorer ECR node
+- **(Bug Fix)** Significantly speed up loading the list of S3 buckets ([#2174](https://github.com/aws/aws-toolkit-jetbrains/issues/2174))
+
+# _1.20_ (2020-10-22)
+- **(Feature)** Add support for `+` in AWS profile names
+- **(Bug Fix)** Fix being unable to use a SSO profile in a credential chain
+- **(Bug Fix)** Fix Aurora MySQL 5.7 not showing up in the AWS Explorer
+- **(Bug Fix)** Improve IAM RDS connection: Fix Aurora MySQL, detect more error cases, fix database configuration validation throwing when there is no DB name
+- **(Deprecation)** 2019.3 support will be removed in the next release
+
+# _1.19_ (2020-10-07)
+- **(Feature)** Add the ability to copy the URL to an S3 object
+- **(Feature)** Add support for debugging dotnet 3.1 local lambdas (requires minimum SAM CLI version of 1.4.0)
+
+# _1.18_ (2020-09-21)
+- **(Feature)** Add support for AWS SSO based credential profiles
+- **(Feature)** Support colons (`:`) in credential profile names
+- **(Feature)** Add support for Lambda runtime java8.al2
+- **(Feature)** Allow connecting to RDS/Redshift databases with temporary IAM AWS credentials or a SecretsManager secret
+- **(Feature)** Several enhancements to the UX around connecting to AWS including:
+ - Making connection settings more visible (now visible in the AWS Explorer)
+ - Automatically selecting 'default' profile if it exists
+ - Better visibility of connection validation workflow (more information when unable to connect)
+ - Handling of default regions on credential profile
+ - Better UX around partitions
+ - Adding ability to refresh connection from the UI
+- **(Feature)** Save update Lambda code settings
+- **(Bug Fix)** Fix several cases where features not supported by the host IDE are shown ([#1980](https://github.com/aws/aws-toolkit-jetbrains/issues/1980))
+- **(Bug Fix)** Start generating SAM project before the IDE is done indexing
+- **(Bug Fix)** Fix several uncaught exceptions caused by plugins being installed but not enabled
+- **(Bug Fix)** Fix removing a source_profile leading to an IDE error on profile file refresh
+- **(Bug Fix)** Fix issue where templates > 51200 bytes would not deploy with "Deploy Serverless Application" ([#1973](https://github.com/aws/aws-toolkit-jetbrains/issues/1973))
+- **(Bug Fix)** Fix the function selection panel not reloading when changing SAM templates ([#955](https://github.com/aws/aws-toolkit-jetbrains/issues/955))
+- **(Bug Fix)** Fix remote terminal start issue on 2020.2
+- **(Bug Fix)** Fix Rider building Lambda into incorrect folders
+- **(Bug Fix)** Improved rendering speed of wrapped text in CloudWatch logs and CloudFormation events tables
+- **(Bug Fix)** Fix the CloudWatch Logs table breaking when the service returns an exception during loading more entries ([#1951](https://github.com/aws/aws-toolkit-jetbrains/issues/1951))
+- **(Bug Fix)** Improve watching of the AWS profile files to incorporate changes made to the files outisde of the IDE
+- **(Bug Fix)** Fix SAM Gradle Hello World syncing twice ([#2003](https://github.com/aws/aws-toolkit-jetbrains/issues/2003))
+- **(Bug Fix)** Quote template parameters when deploying a cloudformation template
+
# _1.17_ (2020-07-16)
- **(Feature)** Wrap logstream entries when they are selected ([#1863](https://github.com/aws/aws-toolkit-jetbrains/issues/1863))
- **(Feature)** Adding 'Outputs' tab to the CloudFormation Stack Viewer
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e5d30349cb..2bd1c3fef3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,12 +27,12 @@ reported the issue. Please try to include as much information as you can. Detail
* Dotnet Framework (Windows)
* macOS steps:
```
- brew cask install dotnet-sdk
+ brew install --cask dotnet-sdk
```
-
+ * It is recommended dotnet version `5.0.403` and `below`. If your dotnet versions were higher, you should refer to this [link](https://github.com/isen-ng/homebrew-dotnet-sdk-versions).
### Instructions
-1. Clone the github repository and run `./gradlew buildPlugin`
(This will produce a plugin zip under `build/distributions`)
+1. Clone the github repository and run `./gradlew :intellij:buildPlugin`
(This will produce a plugin zip under `intellij/build/distributions`)
2. In your JetBrains IDE (e.g. IntelliJ) navigate to the `Plugins` preferences and select "Install Plugin from Disk...", navigate to the zip file produced in step 1.
4. You will be prompted to restart your IDE.
@@ -40,7 +40,7 @@ reported the issue. Please try to include as much information as you can. Detail
Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
-1. You are working against the latest source on the *master* branch.
+1. You are working against the latest source on the *main* branch.
2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
@@ -53,9 +53,9 @@ To send us a pull request, please:
./gradlew check
```
-4. Generate a change log entry for your change using
+4. Generate a change log entry for your change if the change is visible to users of the toolkit in their IDE.
```
- ./gradlew newChange --console plain
+ ./gradlew :newChange --console plain
```
and following the prompts. Change log entries should describe the change
@@ -70,51 +70,75 @@ GitHub provides additional documentation on [forking a repository](https://help.
## Debugging/Running Locally
-To test your changes locally, you can run the project from IntelliJ or gradle.
+To test your changes locally, you can run the project from IntelliJ or Gradle using the `runIde` tasks. Each build will download the required IDE version and
+start it in a sandbox (isolated) configuration.
+
+### In IDE Approach (Recommended)
+
+Launch the IDE through your IntelliJ instance using the provided run configurations.
+If ran using the Debug feature, a debugger will be auto-attached to the sandbox IDE.
+
+### Running manually
-- **Simple approach:** from the top-level of the repository, run:
- ```
- ./gradlew runIde --info
- ```
- The `runIde` task automatically downloads the correct version of IntelliJ
- Community Edition, builds and installs the plugin, and starts a _new_
- instance of IntelliJ with the built extension.
-- To run **Rider or "Ultimate"**, specify the respective gradle target:
```
+ ./gradlew jetbrains-core:runIde
./gradlew jetbrains-ultimate:runIde
./gradlew jetbrains-rider:runIde
```
- These targets download the required IDE for testing.
- - Do not specify `ALTERNATIVE_IDE`.
+
+#### Alternative IDE
+
- To run the plugin in a **specific JetBrains IDE** (and you have it installed), specify the `ALTERNATIVE_IDE` environment variable:
```
- ALTERNATIVE_IDE=/path/to/ide ./gradlew :runIde
+ ALTERNATIVE_IDE=/path/to/ide ./gradlew :intellij:runIde
```
- This is needed to run PyCharm and WebStorm.
- - Notice that the top-level `:runIde` target is always used with `ALTERNATIVE_IDE`.
- - See also `alternativeIdePath` in the Gradle IntelliJ Plugin [documentation](https://github.com/JetBrains/gradle-intellij-plugin).
-- To run **integration tests**:
- ```
- ./gradlew integrationTest
- ```
- - Requires valid AWS credentials (take care: it will respect any credentials currently defined in your environmental variables, and fallback to your default AWS profile otherwise).
- - Requires [`sam`](https://github.com/awslabs/serverless-application-model) CLI to be on your `$PATH`.
+ - See also `alternativeIdePath` option in the `runIde` tasks provided by the Gradle IntelliJ Plugin [documentation](https://github.com/JetBrains/gradle-intellij-plugin).
+
+## Running Tests
+
+### Unit Tests / Checkstyle
+
+These tests make no network calls and are safe for anyone to run.
+ ```
+ ./gradlew check
+ ```
+
+### Integration Tests
+
+It is **NOT** recommended for third party contributors to run these due to they create and mutate AWS resources.
+
+- Requires valid AWS credentials (take care: it will respect any credentials currently defined in your environmental variables, and fallback to your default AWS profile otherwise).
+- Requires [`sam`](https://github.com/awslabs/serverless-application-model) CLI to be on your `$PATH`.
- Requires [`cfn-lint`](https://github.com/aws-cloudformation/cfn-python-lint/) CLI to be on your `$PATH`.
-- To run **GUI tests**:
- ```
- ./gradlew uiTestCore
- ```
- - To debug GUI tests,
- 1. Start the IDE that will be debugged `./gradlew :jetbrains-core:runIdeForUiTests --debug-jvm`
- 2. In your running Intellij instance `Run -> Attach to process` attach to the ide test debug process.
- 4. Run `./gradlew uiTestCore`. This will attach to the running debug IDE instance and run tests.
+ ```
+ ./gradlew integrationTest
+ ```
+
+### UI Tests
+
+It is **NOT** recommended for third party contributors to run these due to they create and mutate AWS resources.
+
+- Requires valid AWS credentials (take care: it will respect any credentials currently defined in your environmental variables, and fallback to your default AWS profile otherwise).
+- Requires `sam` CLI to be on your `$PATH`.
+ ```
+ ./gradlew :ui-tests:uiTestCore
+ ```
+
+#### Debug GUI tests
+
+The sandbox IDE runs with a debug port open (`5005`). In your main IDE, create a Java Remote Debug run configuration and tell it to attach to that port.
+
+If the tests run too quickly, you can tell the UI tests to wait for the debugger to attach by editing the `suspend.set(false)` to `true` in the tasks
+`RunIdeForUiTestTask` in [toolkit-intellij-subplugin Gradle plugin](buildSrc/src/main/kotlin/toolkit-intellij-subplugin.gradle.kts)
### Logging
- Log messages (`LOG.info`, `LOG.error()`, …) by default are written to:
```
- jetbrains-core/build/idea-sandbox/system/log/idea.log
- jetbrains-core/build/idea-sandbox/system-test/logs/idea.log # Tests
+ jetbrains-[subModule]/build/idea-sandbox/system/log/idea.log
+ jetbrains-[subModule]/build/idea-sandbox/system-test/logs/idea.log # Tests
```
- DEBUG-level log messages are skipped by default. To enable them, add the
following line to the _Help_ \> _Debug Log Settings_ dialog in the IDE
@@ -122,7 +146,7 @@ To test your changes locally, you can run the project from IntelliJ or gradle.
```
software.aws.toolkits
```
-
+ **Please be aware that debug level logs may contain more sensitive information. It is not advisable to keep it on nor share log files that contain debug logs**
## Guidelines
@@ -133,7 +157,6 @@ To test your changes locally, you can run the project from IntelliJ or gradle.
Looking at the existing issues is a great way to find something to contribute on. Any of the [help wanted](https://github.com/aws/aws-toolkit-jetbrains/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issues is a great place to start.
-
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
diff --git a/README.md b/README.md
index de7266e4e2..ec0c8f4240 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,29 @@
![Build Status](https://codebuild.eu-west-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiekhxeERIMmNLSkNYUktnUFJzUVJucmJqWnFLMGlpNXJiNE1LLzVWV3B1QUpSSkhCS04veHZmUGxZZ0ZmZlRzYjJ3T1VtVEs1b3JxbWNVOHFOeFJDOTAwPSIsIml2UGFyYW1ldGVyU3BlYyI6ImZXNW5KaytDRGNLdjZuZDgiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master)
[![Coverage](https://img.shields.io/codecov/c/github/aws/aws-toolkit-jetbrains/master.svg)](https://codecov.io/gh/aws/aws-toolkit-jetbrains/branch/master)
-[![Gitter](https://badges.gitter.im/aws/aws-toolkit-jetbrains.svg)](https://gitter.im/aws/aws-toolkit-jetbrains?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Downloads](https://img.shields.io/jetbrains/plugin/d/11349-aws-toolkit.svg)](https://plugins.jetbrains.com/plugin/11349-aws-toolkit)
[![Version](https://img.shields.io/jetbrains/plugin/v/11349.svg?label=version)](https://plugins.jetbrains.com/plugin/11349-aws-toolkit)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=aws_aws-toolkit-jetbrains&metric=alert_status)](https://sonarcloud.io/dashboard?id=aws_aws-toolkit-jetbrains)
# AWS Toolkit for JetBrains
-AWS Toolkit for JetBrains - a plugin for interacting with AWS from JetBrains IDEs. The plugin includes features that
-make it easier to write applications on [Amazon Web Services](https://aws.amazon.com/) using a JetBrains IDE.
+AWS Toolkit for JetBrains is a plugin for JetBrains IDEs that
+make it easier to write applications built on [Amazon Web Services](https://aws.amazon.com/)
-This is an open source project because we want you to be involved. We love issues, feature requests, code reviews, pull
-requests or any positive contribution. See [CONTRIBUTING](CONTRIBUTING.md) for how to help.
+The AWS Toolkit for JetBrains is open source because we want you to be involved. We appreciate issues, feature requests, pull
+requests, code reviews or any other contributions.
## Feedback
We want your feedback!
-- Upvote 👍 [feature requests](https://github.com/aws/aws-toolkit-jetbrains/issues?q=is%3Aissue+is%3Aopen+label%3Afeature-request+sort%3Areactions-%2B1-desc)
-- [Ask a question](https://github.com/aws/aws-toolkit-jetbrains/issues/new?labels=guidance&template=guidance_request.md)
+- Vote on [feature requests](https://github.com/aws/aws-toolkit-jetbrains/issues?q=is%3Aissue+is%3Aopen+label%3Afeature-request+sort%3Areactions-%2B1-desc). Votes help us drive prioritization of features
- [Request a new feature](https://github.com/aws/aws-toolkit-jetbrains/issues/new?labels=feature-request&template=feature_request.md)
+- [Ask a question](https://github.com/aws/aws-toolkit-jetbrains/issues/new?labels=guidance&template=guidance_request.md)
- [File an issue](https://github.com/aws/aws-toolkit-jetbrains/issues/new?labels=bug&template=bug_report.md)
+- Code contributions. See [our contributing guide](CONTRIBUTING.md) for how to get started.
-## Requirements
-Supported IDEs:
-* IntelliJ Community/Ultimate 2019.2+
-* PyCharm Community/Professional 2019.2+
-* Rider 2019.2+
-* WebStorm 2019.2+
+## Supported IDEs
+All JetBrains IDEs 2023.1+
## Installation
@@ -35,7 +31,7 @@ See [Installing the AWS Toolkit for JetBrains](https://docs.aws.amazon.com/conso
To use this AWS Toolkit, you will first need an AWS account, a user within that account, and an access key for that
user. To use the AWS Toolkit to do AWS serverless application development and to run/debug AWS Lambda functions locally,
-you will also need to install the AWS CLI, Docker, and the AWS SAM CLI. The preceding link covers setting up all of
+you will also need to install the AWS CLI, Docker, and the AWS SAM CLI. The installation guide covers setting up all of
these prerequisites.
### EAP Builds
@@ -46,27 +42,26 @@ In order to opt-in:
going to **Plugins->Gear Icon->Manage Plugin Repositories** and adding the URL to the list
* Check for updates.
-### From Source
+### Installing From Source
Please see [CONTRIBUTING](CONTRIBUTING.md#building-from-source) for instructions.
## Features
### General
-Features that don't relate to a specific AWS service.
-
-* **Credential management** - the ability to select how you want to authenticate with AWS, management of several
-credential types and the ability to easily switch between profiles.
-[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/credentials)
-* **Region management** - the ability to switch between viewing resources in different AWS regions.
-[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/regions)
* **AWS Resource Explorer** - tree-view of AWS resources available in your
selected account/region. This does not represent all resources available in your account, only a sub-set of those
resource types supported by the plugin.
[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/aws-explorer)
+* **Authentication** - Connect to AWS using static credentials, credential process, AWS Builder ID or AWS SSO. [Learn more about
+authentication options](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/credentials)
### Services
+#### ![CloudFormation][cloudformation-icon] AWS CloudFormation
+* View events, resources, and outputs for your CloudFormation stacks
+#### ![CloudWatch Logs][cloudwatch-logs-icon] CloudWatch Logs
+* View and search your CloudWatch log streams
#### ![AWS Lambda][lambda-icon] AWS Lambda
Many of these features require the [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) to be installed, see the
@@ -75,8 +70,6 @@ installation of the SAM CLI.
**SAM features support Java, Python, Node.js, and .NET Core**
-* **New Project Wizard** - Get started quickly by using one of the quickstart serverless application templates.
-[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/new-project)
* **Run/Debug Local Lambda Functions** - Locally test and step-through debug functions in a Lambda-like execution
environment provided by the SAM CLI.
[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/lambda-local)
@@ -84,30 +77,39 @@ environment provided by the SAM CLI.
[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/lambda-remote)
* **Package & Deploy Lambda Functions** - Ability to package a Lambda function zip and create a remote lambda
[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/lambda-deploy)
-* **Deploy SAM-based Applications** - Package, deploy & track SAM-based applications
+* **Sync SAM-based Applications** - Sync & track SAM-based applications
[Learn More](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/sam-deploy)
-*NB: Python-only features are available in both PyCharm and IntelliJ with the
+*Note: Python features are available in both PyCharm and IntelliJ with the
[Python Plugin](https://www.jetbrains.com/help/idea/plugin-overview.html) installed.*
-## Roadmap
+#### ![Amazon Redshift][redshift-icon] Amazon RDS/Redshift
+* Connect to RDS/Redshift databases using temporary credentials with IAM/SecretsManager, no copy paste required
+
+*Note: database features require using a paid JetBrains product*
+#### ![Amazon S3][s3-icon] Amazon S3
+* View and manage your S3 buckets
+* Upload/Download to from buckets
+* [Learn more](https://docs.aws.amazon.com/console/toolkit-for-jetbrains/s3-tasks)
+
+### Experimental Features
-The best view of our long-term road-map is by looking the upcoming Release
-[Milestones](https://github.com/aws/aws-toolkit-jetbrains/milestones).
+Sometimes we'll introduce experimental features that we're trying out. These may have bugs, usability problems or may not be fully functional, and because these
+aren't ready for prime-time we'll hide them behind an experimental feature flag.
-In addition to GitHub's built-in [Projects](https://github.com/aws/aws-toolkit-jetbrains/projects) and
-[Milestones](https://github.com/aws/aws-toolkit-jetbrains/milestones) we use [ZenHub](https://www.zenhub.com) to help:
-* manage our back-log
-* prioritize features
-* estimate issues
-* create sprint-boards
+Experimental features can be enabled in the settings/preferences
+(`Settings -> Tools -> AWS -> Experimental Features`) or via the Addtional Settings (![Gear Icon][gear-icon]) in the AWS Explorer Tool Window.
-To enable these enhanced views can sign-up for ZenHub (using your GitHub account - it's free), install
-the ZenHub [extension](https://www.zenhub.com/extension) for your browser and then navigate to the
-[ZebHub](https://github.com/aws/aws-toolkit-jetbrains#zenhub) tab in the toolkit repository.
+Please note that experimental features may be disabled / removed at any time.
## Licensing
The plugin is distributed according to the terms outlined in our [LICENSE](LICENSE).
[lambda-icon]: jetbrains-core/resources/icons/resources/LambdaFunction.svg
+[s3-icon]: jetbrains-core/resources/icons/resources/S3Bucket.svg
+[cloudwatch-logs-icon]: jetbrains-core/resources/icons/resources/cloudwatchlogs/CloudWatchLogs.svg
+[cloudformation-icon]: jetbrains-core/resources/icons/resources/CloudFormationStack.svg
+[redshift-icon]: jetbrains-core/resources/icons/resources/Redshift.svg
+[find-action]: https://www.jetbrains.com/help/idea/searching-everywhere.html#search_actions
+[gear-icon]: https://raw.githubusercontent.com/JetBrains/intellij-community/master/platform/icons/src/general/gear.svg
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index b54eebb066..0000000000
--- a/build.gradle
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-import toolkits.gradle.changelog.tasks.GenerateGithubChangeLog
-import java.nio.file.Files
-import java.nio.file.Paths
-
-buildscript {
- repositories {
- maven { url "https://plugins.gradle.org/m2/" }
- mavenCentral()
- jcenter()
- }
- dependencies {
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
- classpath "gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:$ideaPluginVersion"
- classpath "com.adarshr:gradle-test-logger-plugin:1.7.0"
- }
-}
-
-plugins {
- id "de.undercouch.download" version "4.1.1" apply false
-}
-
-apply from: 'intellijJVersions.gradle'
-
-def ideVersion = shortenVersion(resolveIdeProfileName())
-
-group 'software.aws.toolkits'
-// please check changelog generation logic if this format is changed
-version "$toolkitVersion-$ideVersion".toString()
-
-repositories {
- maven { url "https://www.jetbrains.com/intellij-repository/snapshots/" }
-}
-
-allprojects {
- repositories {
- mavenLocal()
- mavenCentral()
- jcenter()
- }
-
- apply plugin: "com.adarshr.test-logger"
- apply plugin: 'java'
- apply plugin: 'jacoco'
-
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
-
- tasks.withType(JavaExec) {
- systemProperty("aws.toolkits.enableTelemetry", false)
- }
-
- tasks.withType(org.jetbrains.intellij.tasks.RunIdeTask) {
- intellij {
- if (System.env.ALTERNATIVE_IDE) {
- if (file(System.env.ALTERNATIVE_IDE).exists()) {
- alternativeIdePath = System.env.ALTERNATIVE_IDE
- } else {
- throw new GradleException("ALTERNATIVE_IDE path not found"
- + (System.env.ALTERNATIVE_IDE ==~ /.*[\/\\] *$/
- ? " (HINT: remove trailing slash '/')"
- : ": ${System.env.ALTERNATIVE_IDE}"))
- }
- }
- }
- }
-
- configurations {
- runtimeClasspath.exclude group: "org.slf4j"
- runtimeClasspath.exclude group: "org.jetbrains.kotlin"
- runtimeClasspath.exclude group: "org.jetbrains.kotlinx"
- runtimeClasspath.exclude group: "software.amazon.awssdk", module: "netty-nio-client"
- }
-}
-
-// Kotlin plugin seems to be bugging out when there are no kotlin sources
-configure(subprojects - project(":telemetry-client")) {
- apply plugin: 'kotlin'
-
- sourceSets {
- integrationTest {
- kotlin.srcDir 'it'
- }
- }
-}
-
-subprojects {
- group = parent.group
- version = parent.version
-
- apply plugin: 'java'
- apply plugin: 'idea'
-
- sourceSets {
- main.java.srcDirs = SourceUtils.findFolders(project, "src", ideVersion)
- main.resources.srcDirs = SourceUtils.findFolders(project, "resources", ideVersion)
- test.java.srcDirs = SourceUtils.findFolders(project, "tst", ideVersion)
- test.resources.srcDirs = SourceUtils.findFolders(project, "tst-resources", ideVersion)
- integrationTest {
- compileClasspath += main.output + test.output
- runtimeClasspath += main.output + test.output
- java.srcDirs = SourceUtils.findFolders(project, "it", ideVersion)
- resources.srcDirs = SourceUtils.findFolders(project, "it-resources", ideVersion)
- }
- }
-
- configurations {
- testArtifacts
-
- integrationTestImplementation.extendsFrom testImplementation
- integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
- }
-
- dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
- implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
- testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockitoKotlinVersion"
- testImplementation "org.mockito:mockito-core:$mockitoVersion"
- testImplementation "org.assertj:assertj-core:$assertjVersion"
- testImplementation "junit:junit:$junitVersion"
- }
-
- testlogger {
- showFullStackTraces true
- showStandardStreams true
- showPassedStandardStreams false
- showSkippedStandardStreams true
- showFailedStandardStreams true
- }
-
- test {
- jacoco {
- // don't instrument sdk, icons, ktlint, etc.
- includes = ["software.aws.toolkits.*"]
- excludes = ["software.aws.toolkits.ktlint.*"]
- }
-
- reports {
- junitXml.enabled = true
- html.enabled = true
- }
- }
-
- idea {
- module {
- sourceDirs += sourceSets.main.java.srcDirs
- resourceDirs += sourceSets.main.resources.srcDirs
- testSourceDirs += file("tst-$ideVersion")
- testResourceDirs += file("tst-resources-$ideVersion")
-
- sourceDirs -= file("it")
- testSourceDirs += file("it")
- testSourceDirs += file("it-$ideVersion")
-
- resourceDirs -= file("it-resources")
- testResourceDirs += file("it-resources")
- testResourceDirs += file("it-resources-$ideVersion")
- }
- }
-
- task integrationTest(type: Test) {
- group = LifecycleBasePlugin.VERIFICATION_GROUP
- description = "Runs the integration tests."
- testClassesDirs = sourceSets.integrationTest.output.classesDirs
- classpath = sourceSets.integrationTest.runtimeClasspath
-
- jacoco {
- // don't instrument sdk, icons, ktlint, etc.
- includes = ["software.aws.toolkits.*"]
- excludes = ["software.aws.toolkits.ktlint.*"]
- }
-
- project.plugins.withId("org.jetbrains.intellij") {
- systemProperty("log.dir", "${project.intellij.sandboxDirectory}-test/logs")
- }
-
- mustRunAfter tasks.test
- }
-
- project.plugins.withId("org.jetbrains.intellij") {
- downloadRobotServerPlugin.version = remoteRobotVersion
-
- tasks.withType(org.jetbrains.intellij.tasks.RunIdeForUiTestTask).all {
- systemProperty "robot-server.port", remoteRobotPort
- systemProperty "ide.mac.file.chooser.native", "false"
- systemProperty "jb.consents.confirmation.enabled", "false"
- // This does some magic in EndUserAgreement.java to make it not show the privacy policy
- systemProperty "jb.privacy.policy.text", ""
- if (System.getenv("CI") != null) {
- systemProperty("aws.sharedCredentialsFile", "/tmp/.aws/credentials")
- }
- }
-
- jacoco.applyTo(runIdeForUiTests)
- }
-
- tasks.withType(KotlinCompile).all {
- kotlinOptions.jvmTarget = "1.8"
- }
-
- // Force us to compile the integration tests even during check even though we don't run them
- check.dependsOn(integrationTestClasses)
-
- task testJar(type: Jar) {
- baseName = "${project.name}-test"
- from sourceSets.test.output
- from sourceSets.integrationTest.output
- }
-
- artifacts {
- testArtifacts testJar
- }
-
- // Remove the tasks added in by gradle-intellij-plugin so that we don't publish/verify multiple times
- project.afterEvaluate {
- removeTask(tasks, org.jetbrains.intellij.tasks.PublishTask)
- removeTask(tasks, org.jetbrains.intellij.tasks.VerifyPluginTask)
- removeTask(tasks, org.jetbrains.intellij.tasks.BuildSearchableOptionsTask)
- }
-}
-
-configurations {
- ktlint
-}
-
-def removeTask(TaskContainer tasks, Class extends Task> takeType) {
- tasks.withType(takeType).configureEach {
- setEnabled(false)
- }
-}
-
-apply plugin: 'org.jetbrains.intellij'
-apply plugin: 'toolkit-change-log'
-
-intellij {
- version ideSdkVersion("IC")
- pluginName 'aws-jetbrains-toolkit'
- updateSinceUntilBuild false
- downloadSources = System.getenv("CI") == null
-}
-
-prepareSandbox {
- tasks.findByPath(":jetbrains-rider:prepareSandbox")?.collect {
- from(it)
- }
-}
-
-publishPlugin {
- token publishToken
- channels publishChannel ? publishChannel.split(',').collect { it.trim() } : []
-}
-
-tasks.register('generateChangeLog', GenerateGithubChangeLog) {
- changeLogFile = project.file("CHANGELOG.md")
-}
-
-task ktlint(type: JavaExec, group: "verification") {
- description = "Check Kotlin code style."
- classpath = configurations.ktlint
- main = "com.pinterest.ktlint.Main"
-
- def isWindows = System.properties['os.name'].toLowerCase().contains('windows')
-
- def toInclude = project.rootDir.relativePath(project.projectDir) + "/**/*.kt"
- def toExclude = project.rootDir.relativePath(new File(project.projectDir, "jetbrains-rider")) + "/**/*.Generated.kt"
-
- if (isWindows) {
- toInclude = toInclude.replace("/", "\\")
- toExclude = toExclude.replace("/", "\\")
- }
-
- args "-v", toInclude, "!${toExclude}", "!/**/generated-src/**/*.kt"
-
- inputs.files(project.fileTree(dir: ".", include: "**/*.kt"))
- outputs.dir("${project.buildDir}/reports/ktlint/")
-}
-
-task validateLocalizedMessages(group: "verification") {
- doLast {
- BufferedReader files = Files.newBufferedReader(Paths.get("${project.rootDir}/resources/resources/software/aws/toolkits/resources/localized_messages.properties"))
- files
- .lines()
- .map({ item ->
- if (item == null || item.isEmpty()) {
- return ""
- }
- String[] chunks = item.split("=")
- if (chunks.length <= 1) {
- return ""
- } else {
- return chunks[0]
- }
- })
- .filter({ item -> !item.isEmpty() })
- .reduce({ item1, item2 ->
- if (item1 > item2) {
- throw new GradleException("localization file is not sorted:" + item1 + " > " + item2)
- }
-
- return item2
- })
- }
-}
-
-check.dependsOn ktlint
-check.dependsOn validateLocalizedMessages
-check.dependsOn verifyPlugin
-
-task coverageReport(type: JacocoReport) {
- executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
-
- getAdditionalSourceDirs().from(subprojects.sourceSets.main.java.srcDirs)
- getSourceDirectories().from(subprojects.sourceSets.main.java.srcDirs)
- getClassDirectories().from(subprojects.sourceSets.main.output.classesDirs)
-
- reports {
- html.enabled true
- xml.enabled true
- }
-}
-subprojects.forEach {
- coverageReport.mustRunAfter(it.tasks.withType(Test))
-}
-check.dependsOn coverageReport
-
-// Workaround for runIde being defined in multiple projects, if we request the root project runIde, "alias" it to
-// community edition
-if (gradle.startParameter.taskNames.contains("runIde")) {
- // Only disable this if running from root project
- if (gradle.startParameter.projectDir == project.rootProject.rootDir
- || System.properties.containsKey("idea.gui.tests.gradle.runner")) {
- println("Top level runIde selected, excluding sub-projects' runIde")
- gradle.taskGraph.whenReady { graph ->
- graph.allTasks.forEach {
- if (it.name == "runIde" &&
- it.project != project(':jetbrains-core')) {
- it.enabled = false
- }
- }
- }
- }
-}
-
-dependencies {
- implementation project(':jetbrains-ultimate')
- project.findProject(':jetbrains-rider')?.collect {
- implementation it
- }
-
- ktlint "com.pinterest:ktlint:$ktlintVersion"
- ktlint project(":ktlint-rules")
-}
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000000..45b784988b
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,80 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+import org.jetbrains.gradle.ext.ProjectSettings
+import org.jetbrains.gradle.ext.TaskTriggersConfig
+import software.aws.toolkits.gradle.changelog.tasks.GenerateGithubChangeLog
+
+plugins {
+ id("base")
+ id("toolkit-changelog")
+ id("toolkit-jacoco-report")
+ id("org.jetbrains.gradle.plugin.idea-ext")
+}
+
+allprojects {
+ repositories {
+ val codeArtifactUrl: Provider = providers.environmentVariable("CODEARTIFACT_URL")
+ val codeArtifactToken: Provider = providers.environmentVariable("CODEARTIFACT_AUTH_TOKEN")
+ if (codeArtifactUrl.isPresent && codeArtifactToken.isPresent) {
+ maven {
+ url = uri(codeArtifactUrl.get())
+ credentials {
+ username = "aws"
+ password = codeArtifactToken.get()
+ }
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+
+ configurations.all {
+ resolutionStrategy {
+ failOnDynamicVersions()
+ failOnChangingVersions()
+ }
+ }
+}
+
+val generateChangeLog = tasks.register("generateChangeLog") {
+ changeLogFile.set(project.file("CHANGELOG.md"))
+}
+
+tasks.createRelease.configure {
+ mustRunAfter(generateChangeLog)
+
+ releaseVersion.set(providers.gradleProperty("toolkitVersion"))
+}
+
+dependencies {
+ aggregateCoverage(project(":intellij"))
+ aggregateCoverage(project(":ui-tests"))
+}
+
+tasks.register("runIde") {
+ doFirst {
+ throw GradleException("Use project specific runIde command, i.e. :jetbrains-core:runIde, :intellij:runIde")
+ }
+}
+
+if (idea.project != null) { // may be null during script compilation
+ idea {
+ project {
+ settings {
+ taskTriggers {
+ afterSync(":sdk-codegen:generateSdks")
+ afterSync(":jetbrains-core:generateTelemetry")
+ }
+ }
+ }
+ }
+}
+
+fun org.gradle.plugins.ide.idea.model.IdeaProject.settings(configuration: ProjectSettings.() -> Unit) = (this as ExtensionAware).configure(configuration)
+fun ProjectSettings.taskTriggers(action: TaskTriggersConfig.() -> Unit, ) = (this as ExtensionAware).extensions.configure("taskTriggers", action)
+
+// is there a better way to do this?
+// coverageReport has implicit dependency on 'test' outputs since the task outputs the test.exec file
+tasks.coverageReport {
+ mustRunAfter(rootProject.subprojects.map { it.tasks.withType() })
+}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index f4d883d576..49cf19d524 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -1,66 +1,39 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-
-val jacksonVersion: String by project
-val kotlinVersion: String by project
-val awsSdkVersion: String by project
-
-val assertjVersion: String by project
-val junitVersion: String by project
-val mockitoVersion: String by project
-val mockitoKotlinVersion: String by project
-
buildscript {
// This has to be here otherwise properties are not loaded and nothing works
- val props = java.util.Properties()
+ val props = `java.util`.Properties()
file("${project.projectDir.parent}/gradle.properties").inputStream().use { props.load(it) }
- props.entries.forEach { it: Map.Entry -> project.extensions.add(it.key.toString(), it.value) }
-
- val kotlinVersion: String by project
- dependencies {
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
- }
-}
-
-repositories {
- mavenLocal()
- mavenCentral()
- jcenter()
+ props.entries.forEach { project.extensions.add(it.key.toString(), it.value) }
}
plugins {
- // TODO this really doesn't work. The plugin block requires a const string but the above
- // hack we had in place to copy the properties also fixes this for now.
- val kotlinVersion: String by project
- kotlin("jvm") version kotlinVersion
- `java-gradle-plugin`
+ `kotlin-dsl`
}
-sourceSets {
- main.get().java.srcDir("src")
- test.get().java.srcDir("src")
-}
-dependencies {
- api("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")
- api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
- api("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
- api("org.eclipse.jgit:org.eclipse.jgit:5.0.2.201807311906-r")
- api("com.atlassian.commonmark:commonmark:0.11.0")
- api("software.amazon.awssdk:codegen:$awsSdkVersion")
+// Note: We can't use our standard source layout due to https://github.com/gradle/gradle/issues/14310
- testImplementation("org.assertj:assertj-core:$assertjVersion")
- testImplementation("junit:junit:$junitVersion")
- testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:$mockitoKotlinVersion")
- testImplementation("org.mockito:mockito-core:$mockitoVersion")
+dependencies {
+ implementation(libs.jacoco)
+ implementation(libs.aws.codeGen)
+ implementation(libs.bundles.jackson)
+ implementation(libs.commonmark)
+ implementation(libs.gradlePlugin.detekt)
+ implementation(libs.gradlePlugin.intellij)
+ implementation(libs.gradlePlugin.kotlin)
+ implementation(libs.gradlePlugin.testLogger)
+ implementation(libs.gradlePlugin.testRetry)
+ implementation(libs.jgit)
+
+ testImplementation(libs.assertj)
+ testImplementation(libs.junit4)
+ testImplementation(libs.bundles.mockito)
+
+ testRuntimeOnly(libs.junit5.jupiterVintage)
}
-gradlePlugin {
- plugins {
- create("changeLog") {
- id = "toolkit-change-log"
- implementationClass = "toolkits.gradle.changelog.ChangeLogPlugin"
- }
- }
+tasks.test {
+ useJUnitPlatform()
}
diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts
new file mode 100644
index 0000000000..2517d7c108
--- /dev/null
+++ b/buildSrc/settings.gradle.kts
@@ -0,0 +1,48 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+val codeArtifactMavenRepo = fun RepositoryHandler.(): MavenArtifactRepository? {
+ val codeArtifactUrl: Provider = providers.environmentVariable("CODEARTIFACT_URL")
+ val codeArtifactToken: Provider = providers.environmentVariable("CODEARTIFACT_AUTH_TOKEN")
+ return if (codeArtifactUrl.isPresent && codeArtifactToken.isPresent) {
+ maven {
+ url = uri(codeArtifactUrl.get())
+ credentials {
+ username = "aws"
+ password = codeArtifactToken.get()
+ }
+ }
+ } else {
+ null
+ }
+}.also {
+ pluginManagement {
+ repositories {
+ it()
+ gradlePluginPortal()
+ }
+ }
+}
+
+dependencyResolutionManagement {
+ versionCatalogs {
+ create("libs") {
+ from(files("../gradle/libs.versions.toml"))
+
+ apply(from = "../kotlinResolution.settings.gradle.kts")
+ }
+ }
+
+ repositories {
+ codeArtifactMavenRepo()
+ mavenCentral()
+ gradlePluginPortal()
+ maven {
+ url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
+ content {
+ // only allowed to pull snapshots of gradle-intellij-plugin from here
+ includeModule("org.jetbrains.intellij", "org.jetbrains.intellij.gradle.plugin")
+ includeModule("org.jetbrains.intellij.plugins", "gradle-intellij-plugin")
+ }
+ }
+ }
+}
diff --git a/buildSrc/src/SourcesUtils.kt b/buildSrc/src/SourcesUtils.kt
deleted file mode 100644
index d34e6e182a..0000000000
--- a/buildSrc/src/SourcesUtils.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-@file:JvmName("SourceUtils")
-
-import org.gradle.api.Project
-import java.io.FileFilter
-
-/**
- * Determines the sub-folders under a project that should be included based on ideVersion
- *
- * [project] the project to use as a directory base
- * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources')
- * [ideVersion] is the 3 digit numerical version of the JetBrains SDK (e.g. 192, 201 etc)
- */
-fun findFolders(project: Project, type: String, ideVersion: String): List = project.projectDir.listFiles(FileFilter {
- it.isDirectory && includeFolder(type, ideVersion, it.name)
-})?.map { it.name } ?: emptyList()
-
-/**
- * Determines if a folder should be included based on the ideVersion being targeted
- * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources')
- * [ideVersion] is the 3 digit numerical version of the JetBrains SDK (e.g. 192, 201 etc)
- * [folderName] is the folder name to match on, relative to the project directory (e.g. 'tst-201')
- *
- * Examples:
- * Given [includeFolder] is called with a [type] of "tst" and an [ideVersion] of "201"
- *
- * Then following will match:
- * - tst
- * - tst-201
- * - tst-201+
- * - tst-192+
- *
- * The following with *not* match:
- * - tst-resources
- * - tst-resources-201
- * - tst-192
- * - tst-202
- * - tst-202+
- */
-internal fun includeFolder(type: String, ideVersion: String, folderName: String): Boolean {
- val ideVersionAsInt = ideVersion.toInt()
- val match = "$type(-(\\d{3}))?(\\+)?".toRegex().matchEntire(folderName) ?: return false
- val (_, version, plus) = match.destructured
- return when {
- version.isBlank() -> true
- plus.isBlank() -> version.toInt() == ideVersionAsInt
- else -> version.toInt() <= ideVersionAsInt
- }
-}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/BuildScriptUtils.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/BuildScriptUtils.kt
new file mode 100644
index 0000000000..7f5e6099cf
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/BuildScriptUtils.kt
@@ -0,0 +1,63 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle
+
+import org.eclipse.jgit.api.Git
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.api.provider.Provider
+import software.aws.toolkits.gradle.intellij.IdeVersions
+import java.io.IOException
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as KotlinVersionEnum
+
+/**
+ * Only run the given block if this build is running within a CI system (e.g. GitHub actions, CodeBuild etc)
+ */
+fun Project.ciOnly(block: () -> Unit) {
+ if (isCi()) {
+ block()
+ }
+}
+
+fun Project.isCi() : Boolean = providers.environmentVariable("CI").isPresent
+
+fun Project.jvmTarget(): Provider = withCurrentProfileName {
+ JavaVersion.VERSION_17
+}
+
+// https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#other-bundled-kotlin-libraries
+fun Project.kotlinTarget(): Provider = withCurrentProfileName {
+ when (it) {
+ "2022.3" -> KotlinVersionEnum.KOTLIN_1_7
+ "2023.1", "2023.2" -> KotlinVersionEnum.KOTLIN_1_8
+ "2023.3" -> KotlinVersionEnum.KOTLIN_1_9
+ else -> error("not set")
+ }.version
+}
+
+fun Project.withCurrentProfileName(consumer: (String) -> T): Provider {
+ val name = IdeVersions.ideProfile(providers).map { it.name }
+ return name.map {
+ consumer(it)
+ }
+}
+
+fun Project.buildMetadata() =
+ try {
+ val git = Git.open(rootDir)
+ val currentShortHash = git.repository.findRef("HEAD").objectId.abbreviate(7).name()
+ val isDirty = git.status().call().hasUncommittedChanges()
+
+ buildString {
+ append(currentShortHash)
+
+ if (isDirty) {
+ append(".modified")
+ }
+ }
+ } catch(e: IOException) {
+ logger.warn("Could not determine current commit", e)
+
+ "unknownCommit"
+ }
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/SourcesUtils.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/SourcesUtils.kt
new file mode 100644
index 0000000000..a398f24d13
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/SourcesUtils.kt
@@ -0,0 +1,60 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle
+
+import org.gradle.api.Project
+import software.aws.toolkits.gradle.intellij.Profile
+import java.io.File
+import java.io.FileFilter
+
+/**
+ * Determines the sub-folders under a project that should be included based on ideVersion
+ *
+ * [project] the project to use as a directory base
+ * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources')
+ * [ideProfile] is the IDE [Profile] currently configured in the project
+ */
+fun findFolders(project: Project, type: String, ideProfile: Profile): Set = project.projectDir.listFiles(
+ FileFilter {
+ it.isDirectory && includeFolder(type, ideProfile.shortName, it.name)
+ }
+)?.map { File(it.name) }?.toSet() ?: setOf()
+
+/**
+ * Determines if a folder should be included based on the ideVersion being targeted
+ * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources')
+ * [ideVersion] is the 3 digit numerical version of the JetBrains SDK (e.g. 192, 201 etc)
+ * [folderName] is the folder name to match on, relative to the project directory (e.g. 'tst-201')
+ *
+ * Examples:
+ * Given [includeFolder] is called with a [type] of "tst" and an [ideVersion] of "201"
+ *
+ * Then following will match:
+ * - tst
+ * - tst-201
+ * - tst-201+
+ * - tst-192+
+ * - tst-201-202
+ *
+ * The following with *not* match:
+ * - tst-resources
+ * - tst-resources-201
+ * - tst-192
+ * - tst-202
+ * - tst-202+
+ */
+internal fun includeFolder(type: String, ideVersion: String, folderName: String): Boolean {
+ val ideVersionAsInt = ideVersion.toInt()
+ // Check version range first
+ "$type-(\\d{3})-(\\d{3})".toRegex().matchEntire(folderName)?.destructured?.let { (minVersion, maxVersion) ->
+ return ideVersionAsInt in minVersion.toInt()..maxVersion.toInt()
+ }
+ // Then check singular versions/min+
+ val (_, version, plus) = "$type(-(\\d{3}))?(\\+)?".toRegex().matchEntire(folderName)?.destructured ?: return false
+ return when {
+ version.isBlank() -> true
+ plus.isBlank() -> version.toInt() == ideVersionAsInt
+ else -> version.toInt() <= ideVersionAsInt
+ }
+}
diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLog.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLog.kt
similarity index 93%
rename from buildSrc/src/toolkits/gradle/changelog/ChangeLog.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLog.kt
index 5d0e494181..1945a839fa 100644
--- a/buildSrc/src/toolkits/gradle/changelog/ChangeLog.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLog.kt
@@ -1,7 +1,7 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.MapperFeature
diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLogGenerator.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt
similarity index 83%
rename from buildSrc/src/toolkits/gradle/changelog/ChangeLogGenerator.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt
index 591a05fa12..570cd6f1c1 100644
--- a/buildSrc/src/toolkits/gradle/changelog/ChangeLogGenerator.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt
@@ -1,32 +1,31 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
-import org.gradle.api.logging.Logging
+import org.gradle.api.logging.Logger
import java.nio.file.Path
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import kotlin.streams.toList
-/* ktlint-disable custom-ktlint-rules:log-not-lazy */
/**
* Generates a combined change log file based in Markdown syntax
*/
-class ChangeLogGenerator(private val writers: List) {
+class ChangeLogGenerator(private val writers: List, private val logger: Logger) : AutoCloseable {
fun addUnreleasedChanges(unreleasedFiles: List) {
val entries = unreleasedFiles.parallelStream()
.map { readFile(it.toFile()) }
.toList().filterNotNull()
val unreleasedEntry = ReleaseEntry(LocalDate.now(), "Pending Release", entries)
- LOGGER.info("Adding unreleased entry: $unreleasedEntry")
+ logger.info("Adding unreleased entry: $unreleasedEntry")
generateEntry(unreleasedEntry)
}
fun addReleasedChanges(changelogFiles: List) {
val versions = mutableSetOf()
- LOGGER.info("Including release change logs: $changelogFiles")
+ logger.info("Including release change logs: $changelogFiles")
changelogFiles.parallelStream()
.map { readFile(it.toFile()) }
.toList()
@@ -38,7 +37,7 @@ class ChangeLogGenerator(private val writers: List) {
}
.sortedByDescending { it.date }
.forEach {
- LOGGER.info("Adding release entry: $it")
+ logger.info("Adding release entry: $it")
generateEntry(it)
}
}
@@ -50,13 +49,11 @@ class ChangeLogGenerator(private val writers: List) {
}
}
- fun close() {
+ override fun close() {
writers.forEach { it.close() }
}
companion object {
- private val LOGGER = Logging.getLogger(ChangeLogGenerator::class.java)
-
fun renderEntry(releaseEntry: ReleaseEntry): String {
val renderedEntry = StringBuilder()
diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLogWriter.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt
similarity index 82%
rename from buildSrc/src/toolkits/gradle/changelog/ChangeLogWriter.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt
index 7737b85e17..7ef459f3e4 100644
--- a/buildSrc/src/toolkits/gradle/changelog/ChangeLogWriter.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt
@@ -1,7 +1,7 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
abstract class ChangeLogWriter(issueUrl: String? = null) {
private val issueUrl = issueUrl?.trimEnd('/')?.plus("/")
@@ -21,7 +21,7 @@ abstract class ChangeLogWriter(issueUrl: String? = null) {
return entry
}
- val regex = """#(\d+)""".toRegex()
+ val regex = "#(\\d+)".toRegex()
return regex.replace(entry) {
val issue = it.groups[1]?.value ?: return@replace it.value
"[#$issue]($issueUrl$issue)"
diff --git a/buildSrc/src/toolkits/gradle/changelog/GitStager.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/GitStager.kt
similarity index 88%
rename from buildSrc/src/toolkits/gradle/changelog/GitStager.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/GitStager.kt
index e9c87cb753..0a63f97f52 100644
--- a/buildSrc/src/toolkits/gradle/changelog/GitStager.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/GitStager.kt
@@ -1,7 +1,7 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
import org.eclipse.jgit.api.Git
import java.io.File
diff --git a/buildSrc/src/toolkits/gradle/changelog/GithubWriter.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/GithubWriter.kt
similarity index 79%
rename from buildSrc/src/toolkits/gradle/changelog/GithubWriter.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/GithubWriter.kt
index ee4ca5d7d9..acbe0d8402 100644
--- a/buildSrc/src/toolkits/gradle/changelog/GithubWriter.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/GithubWriter.kt
@@ -1,7 +1,7 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
import java.nio.file.Path
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt
new file mode 100644
index 0000000000..c47404b09e
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt
@@ -0,0 +1,54 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.changelog
+
+import org.commonmark.node.AbstractVisitor
+import org.commonmark.node.Heading
+import org.commonmark.parser.Parser
+import org.commonmark.renderer.html.HtmlRenderer
+import java.io.File
+import java.lang.Math.max
+import java.lang.Math.min
+
+class JetBrainsWriter(private val changeNotesFile: File, issueUrl: String? = null) : ChangeLogWriter(issueUrl) {
+ private val sb = StringBuilder()
+
+ override fun append(line: String) {
+ sb.append(line)
+ }
+
+ override fun close() {
+ val renderer = HtmlRenderer.builder()
+ .softbreak("
")
+ .build()
+ val parser = Parser.builder()
+ .postProcessor {
+ it.accept(
+ object : AbstractVisitor() {
+ override fun visit(heading: Heading) {
+ heading.level = max(1, min(heading.level + 2, 6))
+ }
+ }
+ )
+
+ it
+ }
+ .build()
+ val htmlVersionError = renderer.render(parser.parse(sb.toString()))
+
+ changeNotesFile.writeText(
+ """
+
+
+
+
+
+ """.trimIndent()
+ )
+ }
+
+ override fun toString(): String = "JetBrainsWriter(file=$changeNotesFile)"
+}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ReleaseCreator.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ReleaseCreator.kt
new file mode 100644
index 0000000000..eedf5122a2
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ReleaseCreator.kt
@@ -0,0 +1,28 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.changelog
+
+import org.gradle.api.logging.Logger
+import java.io.File
+import java.time.LocalDate
+
+class ReleaseCreator(private val unreleasedFiles: Collection, private val nextReleaseFile: File, logger: Logger) {
+ init {
+ if (nextReleaseFile.exists()) {
+ throw RuntimeException("Release file $nextReleaseFile already exists!")
+ }
+ if (unreleasedFiles.isEmpty()) {
+ logger.warn("Release created without any unreleased change files, this will yield an empty changelog")
+ }
+ }
+
+ fun create(version: String, date: LocalDate = LocalDate.now()) {
+ val entriesByType = unreleasedFiles.map { readFile(it) }.groupBy { it.type }
+ val entries = ChangeType.values().flatMap { entriesByType.getOrDefault(it, emptyList()) }
+ val release = ReleaseEntry(date, version, entries)
+
+ MAPPER.writerWithDefaultPrettyPrinter().writeValue(nextReleaseFile, release)
+ unreleasedFiles.forEach { it.delete() }
+ }
+}
diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/ChangeLogTask.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt
similarity index 83%
rename from buildSrc/src/toolkits/gradle/changelog/tasks/ChangeLogTask.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt
index 7a6f0baeee..bd336edc0f 100644
--- a/buildSrc/src/toolkits/gradle/changelog/tasks/ChangeLogTask.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt
@@ -1,7 +1,7 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog.tasks
+package software.aws.toolkits.gradle.changelog.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
@@ -9,7 +9,7 @@ import org.gradle.api.file.FileTree
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
-import toolkits.gradle.changelog.GitStager
+import software.aws.toolkits.gradle.changelog.GitStager
abstract class ChangeLogTask : DefaultTask() {
@Internal
@@ -21,7 +21,11 @@ abstract class ChangeLogTask : DefaultTask() {
@InputFiles
val nextReleaseDirectory: DirectoryProperty = project.objects.directoryProperty().convention(changesDirectory.dir("next-release"))
+ init {
+ group = "changelog"
+ }
+
protected fun DirectoryProperty.jsonFiles(): FileTree = this.asFileTree.matching {
- it.include("*.json")
+ include("*.json")
}
}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt
new file mode 100644
index 0000000000..4935e50761
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt
@@ -0,0 +1,58 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.changelog.tasks
+
+import org.gradle.api.file.ProjectLayout
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import software.aws.toolkits.gradle.changelog.ChangeLogGenerator
+import software.aws.toolkits.gradle.changelog.GithubWriter
+import software.aws.toolkits.gradle.changelog.ReleaseCreator
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+import javax.inject.Inject
+
+open class CreateRelease @Inject constructor(projectLayout: ProjectLayout) : ChangeLogTask() {
+ @Input
+ val releaseDate: Property = project.objects.property(String::class.java).convention(DateTimeFormatter.ISO_DATE.format(LocalDate.now()))
+
+ @Input
+ val releaseVersion: Property = project.objects.property(String::class.java)
+
+ @Input
+ @Optional
+ val issuesUrl: Provider = project.objects.property(String::class.java).convention("https://github.com/aws/aws-toolkit-jetbrains/issues")
+
+ @OutputFile
+ val releaseFile: RegularFileProperty = project.objects.fileProperty().convention(changesDirectory.file(releaseVersion.map { "$it.json" }))
+
+ @OutputFile
+ val changeLogFile: RegularFileProperty = project.objects.fileProperty().convention(projectLayout.buildDirectory.file("releaseChangeLog.md"))
+
+ @TaskAction
+ fun create() {
+ val releaseDate = DateTimeFormatter.ISO_DATE.parse(releaseDate.get()).let {
+ LocalDate.from(it)
+ }
+
+ val releaseEntries = nextReleaseDirectory.jsonFiles()
+
+ val creator = ReleaseCreator(releaseEntries.files, releaseFile.get().asFile, logger)
+ creator.create(releaseVersion.get(), releaseDate)
+ if (git != null) {
+ git.stage(releaseFile.get().asFile.absoluteFile)
+ git.stage(nextReleaseDirectory.get().asFile.absoluteFile)
+ }
+
+ val generator = ChangeLogGenerator(listOf(GithubWriter(changeLogFile.get().asFile.toPath(), issuesUrl.get())), logger)
+ generator.use {
+ generator.addReleasedChanges(listOf(releaseFile.get().asFile.toPath()))
+ }
+ }
+}
diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt
similarity index 84%
rename from buildSrc/src/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt
index 363658b3f8..47e24670a4 100644
--- a/buildSrc/src/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt
@@ -1,7 +1,7 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog.tasks
+package software.aws.toolkits.gradle.changelog.tasks
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
@@ -10,12 +10,11 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
-import toolkits.gradle.changelog.ChangeLogGenerator
-import toolkits.gradle.changelog.ChangeLogWriter
-import toolkits.gradle.changelog.GithubWriter
-import toolkits.gradle.changelog.JetBrainsWriter
+import software.aws.toolkits.gradle.changelog.ChangeLogGenerator
+import software.aws.toolkits.gradle.changelog.ChangeLogWriter
+import software.aws.toolkits.gradle.changelog.GithubWriter
+import software.aws.toolkits.gradle.changelog.JetBrainsWriter
-/* ktlint-disable custom-ktlint-rules:log-not-lazy */
abstract class GenerateChangeLog(private val shouldStage: Boolean) : ChangeLogTask() {
@Input
@Optional
@@ -31,7 +30,7 @@ abstract class GenerateChangeLog(private val shouldStage: Boolean) : ChangeLogTa
fun generate() {
val writer = createWriter()
logger.info("Generating Changelog with $writer")
- val generator = ChangeLogGenerator(listOf(writer))
+ val generator = ChangeLogGenerator(listOf(writer), logger)
if (includeUnreleased.get()) {
val unreleasedEntries = nextReleaseDirectory.jsonFiles().files
diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/NewChange.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt
similarity index 86%
rename from buildSrc/src/toolkits/gradle/changelog/tasks/NewChange.kt
rename to buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt
index 1457bd3971..c52dcc8cab 100644
--- a/buildSrc/src/toolkits/gradle/changelog/tasks/NewChange.kt
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt
@@ -1,13 +1,13 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog.tasks
+package software.aws.toolkits.gradle.changelog.tasks
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
-import toolkits.gradle.changelog.ChangeType
-import toolkits.gradle.changelog.Entry
-import toolkits.gradle.changelog.MAPPER
+import software.aws.toolkits.gradle.changelog.ChangeType
+import software.aws.toolkits.gradle.changelog.Entry
+import software.aws.toolkits.gradle.changelog.MAPPER
import java.io.File
import java.util.Scanner
import java.util.UUID
@@ -59,13 +59,14 @@ open class NewChange : ChangeLogTask() {
}
private fun createChange(changeType: ChangeType, description: String) = newFile(changeType).apply {
- MAPPER.writerWithDefaultPrettyPrinter().writeValue(this,
+ MAPPER.writerWithDefaultPrettyPrinter().writeValue(
+ this,
Entry(changeType, description)
)
}
private fun newFile(changeType: ChangeType) = nextReleaseDirectory.file("${changeType.name.toLowerCase()}-${UUID.randomUUID()}.json").get().asFile.apply {
- parentFile?.mkdirs()
- createNewFile()
- }
+ parentFile?.mkdirs()
+ createNewFile()
+ }
}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt
new file mode 100644
index 0000000000..86898ae34a
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt
@@ -0,0 +1,198 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.intellij
+
+import org.gradle.api.Project
+import org.gradle.api.provider.Provider
+import org.gradle.api.provider.ProviderFactory
+
+
+enum class IdeFlavor { GW, IC, IU, RD }
+
+object IdeVersions {
+ private val commonPlugins = arrayOf(
+ "vcs-git",
+ "org.jetbrains.plugins.terminal",
+ "org.jetbrains.plugins.yaml"
+ )
+
+ private val ideProfiles = listOf(
+ Profile(
+ name = "2023.1",
+ community = ProductProfile(
+ sdkFlavor = IdeFlavor.IC,
+ sdkVersion = "2023.1",
+ plugins = commonPlugins + listOf(
+ "java",
+ "com.intellij.gradle",
+ "org.jetbrains.idea.maven",
+ "PythonCore:231.8109.144",
+ "Docker:231.8109.217"
+ )
+ ),
+ ultimate = ProductProfile(
+ sdkFlavor = IdeFlavor.IU,
+ sdkVersion = "2023.1",
+ plugins = commonPlugins + listOf(
+ "JavaScript",
+ // Transitive dependency needed for javascript
+ // Can remove when https://github.com/JetBrains/gradle-intellij-plugin/issues/608 is fixed
+ "com.intellij.css",
+ "JavaScriptDebugger",
+ "com.intellij.database",
+ "com.jetbrains.codeWithMe",
+ "Pythonid:231.8109.175",
+ "org.jetbrains.plugins.go:231.8109.175",
+ // https://github.com/JetBrains/gradle-intellij-plugin/issues/1056
+ "org.intellij.intelliLang"
+ )
+ ),
+ rider = RiderProfile(
+ sdkVersion = "2023.1",
+ plugins = commonPlugins + listOf(
+ "rider-plugins-appender" // Workaround for https://youtrack.jetbrains.com/issue/IDEA-179607
+ ),
+ netFrameworkTarget = "net472",
+ rdGenVersion = "2023.1.2",
+ nugetVersion = "2023.1.0"
+ )
+ ),
+ Profile(
+ name = "2023.2",
+ community = ProductProfile(
+ sdkFlavor = IdeFlavor.IC,
+ sdkVersion = "2023.2.2",
+ plugins = commonPlugins + listOf(
+ "java",
+ "com.intellij.gradle",
+ "org.jetbrains.idea.maven",
+ "PythonCore:232.8660.185",
+ "Docker:232.8660.185"
+ )
+ ),
+ ultimate = ProductProfile(
+ sdkFlavor = IdeFlavor.IU,
+ sdkVersion = "2023.2.2",
+ plugins = commonPlugins + listOf(
+ "JavaScript",
+ // Transitive dependency needed for javascript
+ // Can remove when https://github.com/JetBrains/gradle-intellij-plugin/issues/608 is fixed
+ "com.intellij.css",
+ "JavaScriptDebugger",
+ "com.intellij.database",
+ "com.jetbrains.codeWithMe",
+ "Pythonid:232.8660.185",
+ "org.jetbrains.plugins.go:232.8660.142",
+ // https://github.com/JetBrains/gradle-intellij-plugin/issues/1056
+ "org.intellij.intelliLang"
+ )
+ ),
+ rider = RiderProfile(
+ sdkVersion = "2023.2",
+ plugins = commonPlugins + listOf(
+ "rider-plugins-appender" // Workaround for https://youtrack.jetbrains.com/issue/IDEA-179607
+ ),
+ netFrameworkTarget = "net472",
+ rdGenVersion = "2023.2.3",
+ nugetVersion = "2023.2.0"
+ )
+ ),
+ Profile(
+ name = "2023.3",
+ gateway = ProductProfile(
+ sdkFlavor = IdeFlavor.GW,
+ sdkVersion = "233.11799-EAP-CANDIDATE-SNAPSHOT",
+ plugins = arrayOf("org.jetbrains.plugins.terminal")
+ ),
+ community = ProductProfile(
+ sdkFlavor = IdeFlavor.IC,
+ sdkVersion = "2023.3",
+ plugins = commonPlugins + listOf(
+ "java",
+ "com.intellij.gradle",
+ "org.jetbrains.idea.maven",
+ "PythonCore:233.11799.241",
+ "Docker:233.11799.244"
+ )
+ ),
+ ultimate = ProductProfile(
+ sdkFlavor = IdeFlavor.IU,
+ sdkVersion = "2023.3",
+ plugins = commonPlugins + listOf(
+ "JavaScript",
+ // Transitive dependency needed for javascript
+ // Can remove when https://github.com/JetBrains/gradle-intellij-plugin/issues/608 is fixed
+ "com.intellij.css",
+ "JavaScriptDebugger",
+ "com.intellij.database",
+ "com.jetbrains.codeWithMe",
+ "Pythonid:233.11799.241",
+ "org.jetbrains.plugins.go:233.11799.196",
+ // https://github.com/JetBrains/gradle-intellij-plugin/issues/1056
+ "org.intellij.intelliLang"
+ )
+ ),
+ rider = RiderProfile(
+ sdkVersion = "2023.3",
+ plugins = commonPlugins + listOf(
+ "rider-plugins-appender" // Workaround for https://youtrack.jetbrains.com/issue/IDEA-179607
+ ),
+ netFrameworkTarget = "net472",
+ rdGenVersion = "2023.3.2",
+ nugetVersion = "2023.3.0"
+ )
+ ),
+
+ ).associateBy { it.name }
+
+ fun ideProfile(project: Project): Profile = ideProfile(project.providers).get()
+
+ fun ideProfile(providers: ProviderFactory): Provider = resolveIdeProfileName(providers).map {
+ ideProfiles[it] ?: throw IllegalStateException("Can't find profile for $it")
+ }
+
+ private fun resolveIdeProfileName(providers: ProviderFactory): Provider = providers.gradleProperty("ideProfileName")
+}
+
+open class ProductProfile(
+ val sdkFlavor: IdeFlavor,
+ val sdkVersion: String,
+ val plugins: Array = emptyArray()
+) {
+ fun version(): String? = if (!isLocalPath(sdkVersion)) {
+ sdkFlavor.name + "-" + sdkVersion
+ } else {
+ null
+ }
+
+ fun localPath(): String? = sdkVersion.takeIf {
+ isLocalPath(it)
+ }
+
+ private fun isLocalPath(str: String) = str.startsWith("/") || str.getOrNull(1) == ':'
+}
+
+class RiderProfile(
+ sdkVersion: String,
+ plugins: Array,
+ val netFrameworkTarget: String,
+ val rdGenVersion: String, // https://central.sonatype.com/artifact/com.jetbrains.rd/rd-gen/2023.2.3/versions
+ val nugetVersion: String // https://www.nuget.org/packages/JetBrains.Rider.SDK/
+) : ProductProfile(IdeFlavor.RD, sdkVersion, plugins)
+
+class Profile(
+ val name: String,
+ val shortName: String = shortenedIdeProfileName(name),
+ val sinceVersion: String = shortName,
+ val untilVersion: String = "$sinceVersion.*",
+ val gateway: ProductProfile? = null,
+ val community: ProductProfile,
+ val ultimate: ProductProfile,
+ val rider: RiderProfile,
+)
+
+private fun shortenedIdeProfileName(sdkName: String): String {
+ val parts = sdkName.trim().split(".")
+ return parts[0].substring(2) + parts[1]
+}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/ToolkitIntelliJExtension.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/ToolkitIntelliJExtension.kt
new file mode 100644
index 0000000000..14db898ef3
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/ToolkitIntelliJExtension.kt
@@ -0,0 +1,31 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.intellij
+
+import org.gradle.api.provider.Property
+import org.gradle.api.provider.Provider
+import org.gradle.api.provider.ProviderFactory
+
+abstract class ToolkitIntelliJExtension(private val providers: ProviderFactory) {
+ abstract val ideFlavor: Property
+
+ fun ideProfile() = IdeVersions.ideProfile(providers)
+
+ fun version(): Provider = productProfile().flatMap { profile ->
+ providers.provider { profile.version() }
+ }
+
+ fun localPath(): Provider = productProfile().flatMap { profile ->
+ providers.provider { profile.localPath() }
+ }
+
+ fun productProfile(): Provider = ideFlavor.flatMap { flavor ->
+ when (flavor) {
+ IdeFlavor.IC -> ideProfile().map { it.community }
+ IdeFlavor.IU -> ideProfile().map { it.ultimate }
+ IdeFlavor.RD -> ideProfile().map { it.rider }
+ IdeFlavor.GW -> ideProfile().map { it.gateway!! }
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/jacoco/RemoteCoverage.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/jacoco/RemoteCoverage.kt
new file mode 100644
index 0000000000..4fd4beaeac
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/jacoco/RemoteCoverage.kt
@@ -0,0 +1,125 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.jacoco
+
+import org.gradle.BuildAdapter
+import org.gradle.BuildResult
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.services.BuildService
+import org.gradle.api.services.BuildServiceParameters
+import org.gradle.api.tasks.testing.Test
+import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
+import org.jacoco.core.data.ExecutionData
+import org.jacoco.core.data.ExecutionDataWriter
+import org.jacoco.core.data.IExecutionDataVisitor
+import org.jacoco.core.data.ISessionInfoVisitor
+import org.jacoco.core.data.SessionInfo
+import org.jacoco.core.runtime.RemoteControlReader
+import org.jacoco.core.runtime.RemoteControlWriter
+import java.io.FileOutputStream
+import java.net.ServerSocket
+import java.net.Socket
+import java.util.concurrent.atomic.AtomicBoolean
+
+class RemoteCoverage private constructor(task: Test) {
+ companion object {
+ fun enableRemoteCoverage(task: Test) = RemoteCoverage(task)
+
+ private const val DEFAULT_JACOCO_PORT = 6300
+ }
+
+ init {
+ task.extensions.findByType(JacocoTaskExtension::class.java)?.let {
+ val execFile = it.destinationFile ?: return@let
+
+ // Use a shared service since it does not block task execution
+ val jacocoServer = task.project.gradle.sharedServices.registerIfAbsent("jacocoServer", JacocoServer::class.java) {
+ if (!execFile.exists()) {
+ task.project.mkdir(execFile.parentFile)
+ execFile.createNewFile()
+ }
+
+ parameters.execFile.set(execFile)
+ }
+
+ task.doFirst {
+ jacocoServer.get().start()
+ task.project.gradle.addBuildListener(object : BuildAdapter() {
+ override fun buildFinished(result: BuildResult) {
+ task.project.gradle.removeListener(this)
+ runCatching {
+ jacocoServer.get().close()
+ }
+ }
+ })
+ }
+ } ?: task.logger.warn("$task does not have Jacoco enabled on it")
+ }
+
+ abstract class JacocoServer : BuildService, AutoCloseable {
+ interface Params : BuildServiceParameters {
+ val execFile: RegularFileProperty
+ }
+
+ private val serverSocket = ServerSocket(DEFAULT_JACOCO_PORT)
+ private val isRunning = AtomicBoolean(false)
+
+ private val serverRunnable = Runnable {
+ parameters.execFile.asFile.get().outputStream().use {
+ while (isRunning.get()) {
+ val clientSocket = serverSocket.accept()
+ JacocoHandler(clientSocket, it).run()
+ }
+ }
+ }
+ private lateinit var serverThread: Thread
+
+ fun start() {
+ if (!isRunning.getAndSet(true)) {
+ serverThread = Thread(serverRunnable)
+ serverThread.start()
+ } else {
+ throw IllegalStateException("Jacoco server is already running!")
+ }
+ }
+
+ override fun close() {
+ if (isRunning.getAndSet(false)) {
+ serverThread.interrupt()
+ }
+ }
+ }
+
+ private class JacocoHandler(private val socket: Socket, private val outputFile: FileOutputStream) : ISessionInfoVisitor, IExecutionDataVisitor {
+ private val fileWriter = ExecutionDataWriter(outputFile)
+
+ fun run() {
+ socket.use {
+ socket.getInputStream().use { input ->
+ socket.getOutputStream().use { output ->
+ val reader = RemoteControlReader(input)
+ reader.setSessionInfoVisitor(this)
+ reader.setExecutionDataVisitor(this)
+
+ RemoteControlWriter(output)
+
+ @Suppress("ControlFlowWithEmptyBody")
+ // Read all the data from jacoco
+ while (reader.read()) {
+ }
+ synchronized(fileWriter) { fileWriter.flush() }
+ }
+ }
+ }
+ }
+
+ override fun visitSessionInfo(info: SessionInfo) {
+ synchronized(fileWriter) { fileWriter.visitSessionInfo(info) }
+ }
+
+ override fun visitClassExecution(data: ExecutionData) {
+ synchronized(fileWriter) { fileWriter.visitClassExecution(data) }
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/resources/ValidateMessages.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/resources/ValidateMessages.kt
new file mode 100644
index 0000000000..a0dc28e875
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/resources/ValidateMessages.kt
@@ -0,0 +1,68 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.resources
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP
+import java.time.Instant
+
+open class ValidateMessages : DefaultTask() {
+ private companion object {
+ const val COPYRIGHT_HEADER_LINES = 2
+ }
+ @InputFiles
+ val paths: ConfigurableFileCollection = project.objects.fileCollection()
+
+ @OutputFile
+ val output: RegularFileProperty = project.objects.fileProperty().convention {
+ project.buildDir.resolve("validateMessages")
+ }
+
+ init {
+ group = VERIFICATION_GROUP
+ }
+
+ @TaskAction
+ fun validateMessage() {
+ var hasError = false
+ paths
+ .map { it.absolutePath to it.readLines() }
+ .forEach { (filePath, fileLines) ->
+ fileLines
+ // filter out blank lines and comments
+ .filter { it.isNotBlank() && it.trim().firstOrNull() != '#' }
+ .mapIndexed { lineNumber, it ->
+ if (it.contains("=")) {
+ it
+ } else {
+ logger.error(""""$filePath:${lineNumber + COPYRIGHT_HEADER_LINES} contains invalid message missing a '=': "$it"""")
+ hasError = true
+ null
+ }
+ }
+ .filterNotNull()
+ .map { it.split("=").first() }
+ .reduceIndexed { lineNumber, item1, item2 ->
+ if (item1 > item2) {
+ logger.error("""$filePath:${lineNumber + COPYRIGHT_HEADER_LINES} is not sorted:"$item1" > "$item2"""")
+ hasError = true
+ }
+
+ item2
+ }
+ if (hasError) {
+ throw GradleException("$filePath has one or more out of order items!")
+ }
+ }
+
+ // Write the current time to the file so it will be cacheable (gradle can only use files to determine up to date checks)
+ output.asFile.get().writeText(Instant.now().toString())
+ }
+}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/sdk/GenerateSdk.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/sdk/GenerateSdk.kt
new file mode 100644
index 0000000000..03275d5b52
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/sdk/GenerateSdk.kt
@@ -0,0 +1,63 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.sdk
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import software.amazon.awssdk.codegen.C2jModels
+import software.amazon.awssdk.codegen.CodeGenerator
+import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig
+import software.amazon.awssdk.codegen.model.service.ServiceModel
+import software.amazon.awssdk.codegen.utils.ModelLoaderUtils
+import java.io.File
+
+open class GenerateSdk : DefaultTask() {
+ @InputDirectory
+ val c2jFolder: DirectoryProperty = project.objects.directoryProperty().convention(project.extensions.getByType(GenerateSdkExtension::class.java).c2jFolder)
+
+ @OutputDirectory
+ val srcDir: DirectoryProperty = project.objects.directoryProperty().convention(project.extensions.getByType(GenerateSdkExtension::class.java).srcDir())
+
+ @Internal
+ val testDir: DirectoryProperty = project.objects.directoryProperty().convention(project.extensions.getByType(GenerateSdkExtension::class.java).testDir())
+
+ @TaskAction
+ fun generate() {
+ val srcDir = srcDir.asFile.get()
+ val testDir = testDir.asFile.get()
+ srcDir.deleteRecursively()
+ testDir.deleteRecursively()
+
+ c2jFolder.get().asFileTree.visit {
+ if (isDirectory) {
+ with(file) {
+ logger.info("Generating SDK from $this")
+ val models = C2jModels.builder()
+ .serviceModel(loadServiceModel())
+ .paginatorsModel(loadOptionalModel("paginators-1.json"))
+ .customizationConfig(loadOptionalModel("customization.config") ?: CustomizationConfig.create())
+ .waitersModel(loadOptionalModel("waiters-2.json"))
+ .build()
+
+ CodeGenerator.builder()
+ .models(models)
+ .sourcesDirectory(srcDir.absolutePath)
+ .testsDirectory(testDir.absolutePath)
+ .build()
+ .execute()
+ }
+ }
+ }
+ }
+
+ private fun File.loadServiceModel(): ServiceModel? = ModelLoaderUtils.loadModel(ServiceModel::class.java, resolve("service-2.json"))
+
+ private inline fun File.loadOptionalModel(fileName: String): T? = resolve(fileName).takeIf { it.exists() }?.let {
+ ModelLoaderUtils.loadModel(T::class.java, it)
+ }
+}
diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/sdk/GenerateSdkExtension.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/sdk/GenerateSdkExtension.kt
new file mode 100644
index 0000000000..7f5a394835
--- /dev/null
+++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/sdk/GenerateSdkExtension.kt
@@ -0,0 +1,18 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.sdk
+
+import org.gradle.api.file.Directory
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.provider.Provider
+
+open class GenerateSdkExtension(objects: ObjectFactory) {
+ val c2jFolder: DirectoryProperty = objects.directoryProperty()
+
+ val outputDir: DirectoryProperty = objects.directoryProperty()
+
+ fun srcDir(): Provider = outputDir.dir("src")
+ fun testDir(): Provider = outputDir.dir("tst")
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-changelog.gradle.kts b/buildSrc/src/main/kotlin/toolkit-changelog.gradle.kts
new file mode 100644
index 0000000000..141a76549e
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-changelog.gradle.kts
@@ -0,0 +1,24 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import software.aws.toolkits.gradle.changelog.ChangeType
+import software.aws.toolkits.gradle.changelog.tasks.CreateRelease
+import software.aws.toolkits.gradle.changelog.tasks.NewChange
+
+tasks.register("createRelease")
+
+tasks.register("newChange") {
+ description = "Creates a new change entry for inclusion in the Change Log"
+}
+
+tasks.register("newFeature") {
+ description = "Creates a new feature change entry for inclusion in the Change Log"
+
+ defaultChangeType = ChangeType.FEATURE
+}
+
+tasks.register("newBugFix") {
+ description = "Creates a new bug-fix change entry for inclusion in the Change Log"
+
+ defaultChangeType = ChangeType.BUGFIX
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-detekt.gradle.kts b/buildSrc/src/main/kotlin/toolkit-detekt.gradle.kts
new file mode 100644
index 0000000000..fa268707f3
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-detekt.gradle.kts
@@ -0,0 +1,62 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
+import kotlin.reflect.KVisibility
+import kotlin.reflect.full.companionObject
+import kotlin.reflect.full.companionObjectInstance
+import kotlin.reflect.full.functions
+import kotlin.reflect.full.memberFunctions
+import kotlin.reflect.full.memberProperties
+
+plugins {
+ id("io.gitlab.arturbosch.detekt")
+ id("toolkit-testing")
+}
+
+// TODO: https://github.com/gradle/gradle/issues/15383
+val versionCatalog = extensions.getByType().named("libs")
+dependencies {
+ detektPlugins(versionCatalog.findLibrary("detekt-formattingRules").get())
+ detektPlugins(project(":detekt-rules"))
+}
+
+detekt {
+ val rulesProject = project(":detekt-rules").projectDir
+ source.setFrom(projectDir)
+ buildUponDefaultConfig = true
+ parallel = true
+ allRules = false
+ config.setFrom("$rulesProject/detekt.yml")
+ autoCorrect = true
+}
+
+tasks.withType {
+ reports {
+ html.required.set(true) // Human readable report
+ xml.required.set(true) // Checkstyle like format for CI tool integrations
+ }
+}
+
+tasks.withType {
+ // weird issue where the baseline tasks can't find the source code
+ source.plus(projectDir)
+
+ // hack around https://github.com/detekt/detekt/issues/6167
+ doLast {
+ Class.forName("io.gitlab.arturbosch.detekt.invoke.DetektInvoker").kotlin.let { detektInvoker ->
+ val invokerInstance = detektInvoker.companionObject!!.memberFunctions.find { it.name == "create" }!!.call(detektInvoker.companionObjectInstance, false)
+ val invokeCliMethod = detektInvoker.memberFunctions.find { it.name == "invokeCli" }
+ val jdkHomeArgumentClass = Class.forName("io.gitlab.arturbosch.detekt.invoke.JdkHomeArgument").kotlin
+ val jdkHomeArgument = jdkHomeArgumentClass.constructors.first().call(jdkHome)
+ val jdkHomeArgs = jdkHomeArgumentClass.memberFunctions.find { it.name == "toArgument" }!!.call(jdkHomeArgument) as List
+ val taskArgs = this::class.memberProperties.find { it.name == "arguments" }!!.call(this) as List
+
+ val cliArgs = taskArgs + jdkHomeArgs
+ val ignoreFailures = ignoreFailures.getOrElse(false)
+ val classpath = detektClasspath.plus(pluginClasspath)
+ val taskName = name
+ invokeCliMethod!!.call(invokerInstance, cliArgs, classpath, taskName, ignoreFailures)
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-generate-sdks.gradle.kts b/buildSrc/src/main/kotlin/toolkit-generate-sdks.gradle.kts
new file mode 100644
index 0000000000..d4e14b5774
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-generate-sdks.gradle.kts
@@ -0,0 +1,41 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import software.aws.toolkits.gradle.sdk.GenerateSdk
+import software.aws.toolkits.gradle.sdk.GenerateSdkExtension
+import software.aws.toolkits.gradle.jvmTarget
+
+val sdkGenerator = project.extensions.create("sdkGenerator")
+
+plugins {
+ java
+}
+
+sourceSets {
+ main {
+ java {
+ setSrcDirs(listOf(sdkGenerator.srcDir()))
+ }
+ }
+
+ test {
+ java {
+ setSrcDirs(emptyList())
+ }
+ }
+}
+
+java {
+ val target = project.jvmTarget().get()
+ sourceCompatibility = target
+ targetCompatibility = target
+}
+
+tasks.withType().configureEach {
+ options.encoding = "UTF-8"
+}
+
+val generateTask = tasks.register("generateSdks")
+tasks.named("compileJava") {
+ dependsOn(generateTask)
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-integration-testing.gradle.kts b/buildSrc/src/main/kotlin/toolkit-integration-testing.gradle.kts
new file mode 100644
index 0000000000..84c393c481
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-integration-testing.gradle.kts
@@ -0,0 +1,53 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+plugins {
+ id("java")
+ id("idea")
+ id("toolkit-testing")
+}
+
+val integrationTests: SourceSet = sourceSets.maybeCreate("integrationTest")
+sourceSets {
+ integrationTests.apply {
+ java.setSrcDirs(listOf("it"))
+ resources.srcDirs(listOf("it-resources"))
+
+ compileClasspath += main.get().output + test.get().output
+ runtimeClasspath += main.get().output + test.get().output
+ }
+}
+
+configurations.getByName("integrationTestCompileClasspath") {
+ extendsFrom(configurations.getByName(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME))
+ isCanBeResolved = true
+}
+configurations.getByName("integrationTestRuntimeClasspath") {
+ extendsFrom(configurations.getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME))
+ isCanBeResolved = true
+}
+
+// Add the integration test source set to test jar
+val testJar = tasks.named("testJar") {
+ from(integrationTests.output)
+}
+
+idea {
+ module {
+ testSourceDirs = testSourceDirs + integrationTests.java.srcDirs
+ testResourceDirs = testResourceDirs + integrationTests.resources.srcDirs
+ }
+}
+
+tasks.register("integrationTest") {
+ group = LifecycleBasePlugin.VERIFICATION_GROUP
+ description = "Runs the integration tests."
+ testClassesDirs = integrationTests.output.classesDirs
+ classpath = integrationTests.runtimeClasspath
+
+ mustRunAfter(tasks.test)
+}
+
+tasks.check {
+ dependsOn(integrationTests.compileJavaTaskName, integrationTests.getCompileTaskName("kotlin"))
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-intellij-subplugin.gradle.kts b/buildSrc/src/main/kotlin/toolkit-intellij-subplugin.gradle.kts
new file mode 100644
index 0000000000..fb30156810
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-intellij-subplugin.gradle.kts
@@ -0,0 +1,282 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.testing.jacoco.plugins.JacocoTaskExtension.Output
+import org.jetbrains.intellij.tasks.DownloadRobotServerPluginTask
+import org.jetbrains.intellij.tasks.PatchPluginXmlTask
+import org.jetbrains.intellij.tasks.RunIdeForUiTestTask
+import org.jetbrains.intellij.utils.OpenedPackages
+import software.aws.toolkits.gradle.buildMetadata
+import software.aws.toolkits.gradle.ciOnly
+import software.aws.toolkits.gradle.findFolders
+import software.aws.toolkits.gradle.intellij.IdeFlavor
+import software.aws.toolkits.gradle.intellij.IdeVersions
+import software.aws.toolkits.gradle.intellij.ToolkitIntelliJExtension
+import software.aws.toolkits.gradle.isCi
+
+val toolkitIntelliJ = project.extensions.create("intellijToolkit")
+
+val ideProfile = IdeVersions.ideProfile(project)
+val toolkitVersion: String by project
+val remoteRobotPort: String by project
+
+// please check changelog generation logic if this format is changed
+version = "$toolkitVersion-${ideProfile.shortName}"
+
+plugins {
+ id("toolkit-kotlin-conventions")
+ id("toolkit-testing")
+ id("org.jetbrains.intellij")
+}
+
+// Add our source sets per IDE profile version (i.e. src-211)
+sourceSets {
+ main {
+ java.srcDirs(findFolders(project, "src", ideProfile))
+ resources.srcDirs(findFolders(project, "resources", ideProfile))
+ }
+ test {
+ java.srcDirs(findFolders(project, "tst", ideProfile))
+ resources.srcDirs(findFolders(project, "tst-resources", ideProfile))
+ }
+
+ plugins.withType {
+ maybeCreate("integrationTest").apply {
+ java.srcDirs(findFolders(project, "it", ideProfile))
+ resources.srcDirs(findFolders(project, "it-resources", ideProfile))
+ }
+ }
+}
+
+configurations {
+ runtimeClasspath {
+ // Exclude dependencies that ship with iDE
+ exclude(group = "org.slf4j")
+ exclude(group = "org.jetbrains.kotlin")
+ exclude(group = "org.jetbrains.kotlinx")
+
+ // Exclude dependencies we don't use to make plugin smaller
+ exclude(group = "software.amazon.awssdk", module = "netty-nio-client")
+ }
+
+ testRuntimeClasspath {
+ // Conflicts with CRT in test classpath
+ exclude(group = "software.amazon.awssdk", module = "netty-nio-client")
+ }
+
+ // TODO: https://github.com/gradle/gradle/issues/15383
+ val versionCatalog = extensions.getByType().named("libs")
+ dependencies {
+ testImplementation(platform(versionCatalog.findLibrary("junit5-bom").get()))
+ testImplementation(versionCatalog.findLibrary("junit5-jupiterApi").get())
+
+ testRuntimeOnly(versionCatalog.findLibrary("junit5-jupiterEngine").get())
+ testRuntimeOnly(versionCatalog.findLibrary("junit5-jupiterVintage").get())
+ }
+
+ all {
+ if (name.startsWith("detekt")) {
+ return@all
+ }
+
+ resolutionStrategy.eachDependency {
+ if (requested.group == "org.jetbrains.kotlinx" && requested.name.startsWith("kotlinx-coroutines")) {
+ useVersion(versionCatalog.findVersion("kotlinCoroutines").get().toString())
+ because("resolve kotlinx-coroutines version conflicts in favor of local version catalog")
+ }
+
+ if (requested.group == "org.jetbrains.kotlin" && requested.name.startsWith("kotlin")) {
+ useVersion(versionCatalog.findVersion("kotlin").get().toString())
+ because("resolve kotlin version conflicts in favor of local version catalog")
+ }
+ }
+ }
+}
+
+tasks.processResources {
+ // needed because both rider and ultimate include plugin-datagrip.xml which we are fine with
+ duplicatesStrategy = DuplicatesStrategy.WARN
+}
+
+// Run after the project has been evaluated so that the extension (intellijToolkit) has been configured
+intellij {
+ pluginName.set("aws-toolkit-jetbrains")
+
+ localPath.set(toolkitIntelliJ.localPath())
+ version.set(toolkitIntelliJ.version())
+
+ plugins.set(toolkitIntelliJ.productProfile().map { it.plugins.toMutableList() })
+
+ downloadSources.set(toolkitIntelliJ.ideFlavor.map { it == IdeFlavor.IC && !project.isCi() })
+ instrumentCode.set(toolkitIntelliJ.ideFlavor.map { it == IdeFlavor.IC || it == IdeFlavor.IU })
+}
+
+tasks.jar {
+ archiveBaseName.set(toolkitIntelliJ.ideFlavor.map { "aws-toolkit-jetbrains-$it" })
+}
+
+tasks.withType().all {
+ sinceBuild.set(toolkitIntelliJ.ideProfile().map { it.sinceVersion })
+ untilBuild.set(toolkitIntelliJ.ideProfile().map { it.untilVersion })
+}
+
+// attach the current commit hash on local builds
+if (!project.isCi()){
+ val buildMetadata = buildMetadata()
+ tasks.withType().all {
+ version.set("${project.version}+$buildMetadata")
+ }
+
+ tasks.buildPlugin {
+ archiveClassifier.set(buildMetadata)
+ }
+}
+
+// Disable building the settings search cache since it 1. fails the build, 2. gets run on the final packaged plugin
+tasks.buildSearchableOptions {
+ enabled = false
+}
+
+// https://github.com/JetBrains/gradle-intellij-plugin/blob/829786d5d196ab942d7e6eb3e472ac0af776d3fa/src/main/kotlin/org/jetbrains/intellij/tasks/RunIdeBase.kt#L315
+val openedPackages = OpenedPackages + listOf(
+ // very noisy in UI tests
+ "--add-opens=java.desktop/javax.swing.text=ALL-UNNAMED",
+) + with(OperatingSystem.current()) {
+ when {
+ isWindows -> listOf(
+ "--add-opens=java.base/sun.nio.fs=ALL-UNNAMED",
+ )
+ else -> emptyList()
+ }
+}
+
+tasks.withType().all {
+ systemProperty("log.dir", intellij.sandboxDir.map { "$it-test/logs" }.get())
+ systemProperty("testDataPath", project.rootDir.resolve("testdata").absolutePath)
+ val jetbrainsCoreTestResources = project(":jetbrains-core").projectDir.resolve("tst-resources")
+ // FIX_WHEN_MIN_IS_221: log4j 1.2 removed in 221
+ systemProperty("log4j.configuration", jetbrainsCoreTestResources.resolve("log4j.xml"))
+ systemProperty("idea.log.config.properties.file", jetbrainsCoreTestResources.resolve("toolkit-test-log.properties"))
+ systemProperty("org.gradle.project.ideProfileName", ideProfile.name)
+
+ jvmArgs(openedPackages)
+
+ useJUnitPlatform()
+}
+
+tasks.withType {
+ systemProperty("aws.toolkits.enableTelemetry", false)
+}
+
+tasks.runIde {
+ systemProperty("aws.toolkit.developerMode", true)
+ systemProperty("ide.plugins.snapshot.on.unload.fail", true)
+ systemProperty("memory.snapshots.path", project.rootDir)
+ systemProperty("idea.auto.reload.plugins", false)
+
+ val alternativeIde = providers.environmentVariable("ALTERNATIVE_IDE")
+ if (alternativeIde.isPresent) {
+ // remove the trailing slash if there is one or else it will not work
+ val value = alternativeIde.get()
+ val path = File(value.trimEnd('/'))
+ if (path.exists()) {
+ ideDir.set(path)
+ } else {
+ throw GradleException("ALTERNATIVE_IDE path not found $value")
+ }
+ }
+}
+
+// TODO: https://github.com/gradle/gradle/issues/15383
+val versionCatalog = extensions.getByType().named("libs")
+tasks.withType {
+ version.set(versionCatalog.findVersion("intellijRemoteRobot").get().requiredVersion)
+}
+
+// Enable coverage for the UI test target IDE
+ciOnly {
+ extensions.getByType().applyTo(tasks.withType())
+}
+tasks.withType().all {
+ systemProperty("robot-server.port", remoteRobotPort)
+ // mac magic
+ systemProperty("ide.mac.message.dialogs.as.sheets", "false")
+ systemProperty("jbScreenMenuBar.enabled", "false")
+ systemProperty("apple.laf.useScreenMenuBar", "false")
+ systemProperty("ide.mac.file.chooser.native", "false")
+
+ systemProperty("jb.consents.confirmation.enabled", "false")
+ // This does some magic in EndUserAgreement.java to make it not show the privacy policy
+ systemProperty("jb.privacy.policy.text", "")
+ systemProperty("ide.show.tips.on.startup.default.value", false)
+
+ systemProperty("aws.telemetry.skip_prompt", "true")
+ systemProperty("aws.suppress_deprecation_prompt", true)
+ systemProperty("idea.trust.all.projects", "true")
+
+ // These are experiments to enable for UI tests
+ systemProperty("aws.experiment.connectedLocalTerminal", true)
+ systemProperty("aws.experiment.dynamoDb", true)
+
+ debugOptions {
+ enabled.set(true)
+ suspend.set(false)
+ }
+
+ jvmArgs(openedPackages)
+
+ ciOnly {
+ configure {
+ // sync with testing-subplugin
+ // don't instrument sdk, icons, etc.
+ includes = listOf("software.aws.toolkits.*")
+ excludes = listOf("software.aws.toolkits.telemetry.*")
+
+ // 221+ uses a custom classloader and jacoco fails to find classes
+ isIncludeNoLocationClasses = true
+
+ output = Output.TCP_CLIENT // Dump to our jacoco server instead of to a file
+ }
+ }
+}
+
+// weird implicit dependency issue, maybe with how the task graph works?
+// or because tests are on the ide classpath for some reason?
+tasks.named("classpathIndexCleanup") {
+ dependsOn(tasks.named("compileIntegrationTestKotlin"))
+}
+
+configurations.instrumentedJar.configure {
+ // when the "instrumentedJar" configuration is selected, gradle is unable to resolve configurations needed by jacoco
+ // to calculate coverage, so we declare these as seconary artifacts on the primary "instrumentedJar" implicit variant
+ outgoing.variants {
+ create("instrumentedClasses") {
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
+ attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
+ attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
+ }
+
+ artifact(tasks.instrumentCode) {
+ type = ArtifactTypeDefinition.JVM_CLASS_DIRECTORY
+ }
+ }
+
+ listOf("coverageDataElements", "mainSourceElements").forEach { implicitVariant ->
+ val configuration = configurations.getByName(implicitVariant)
+ create(implicitVariant) {
+ attributes {
+ configuration.attributes.keySet().forEach {
+ attribute(it as Attribute, configuration.attributes.getAttribute(it)!!)
+ }
+ }
+
+ configuration.artifacts.forEach {
+ artifact(it)
+ }
+ }
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-jacoco-report.gradle.kts b/buildSrc/src/main/kotlin/toolkit-jacoco-report.gradle.kts
new file mode 100644
index 0000000000..271dd95261
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-jacoco-report.gradle.kts
@@ -0,0 +1,80 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+// Taken from https://docs.gradle.org/current/userguide/structuring_software_products.html
+
+plugins {
+ id("java-base")
+ id("jacoco")
+}
+// TODO: https://github.com/gradle/gradle/issues/15383
+val versionCatalog = extensions.getByType().named("libs")
+jacoco {
+ // need to probe resolved dependencies directly if moved to rich version declaration
+ toolVersion = versionCatalog.findVersion("jacoco").get().toString()
+}
+
+// Configurations to declare dependencies
+val aggregateCoverage by configurations.creating {
+ isVisible = false
+ isCanBeResolved = false
+ isCanBeConsumed = false
+}
+
+// Resolvable configuration to resolve the classes of all dependencies
+val classPath by configurations.creating {
+ isVisible = false
+ isCanBeResolved = true
+ isCanBeConsumed = false
+ extendsFrom(aggregateCoverage)
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
+ attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
+ attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
+ }
+}
+
+// A resolvable configuration to collect source code
+val sourcesPath by configurations.creating {
+ isVisible = false
+ isCanBeResolved = true
+ isCanBeConsumed = false
+ extendsFrom(aggregateCoverage)
+ attributes {
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.VERIFICATION))
+ attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
+ attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, objects.named(VerificationType.MAIN_SOURCES))
+ }
+}
+
+// A resolvable configuration to collect JaCoCo coverage data
+val coverageDataPath by configurations.creating {
+ isVisible = false
+ isCanBeResolved = true
+ isCanBeConsumed = false
+ extendsFrom(aggregateCoverage)
+ attributes {
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
+ attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("jacoco-coverage-data"))
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+ }
+}
+
+// Register a code coverage report task to generate the aggregated report
+tasks.register("coverageReport") {
+ additionalClassDirs(
+ classPath.filter { it.isDirectory }.asFileTree.matching {
+ include("**/software/aws/toolkits/**")
+ exclude("**/software/aws/toolkits/telemetry/**")
+ }
+ )
+
+ additionalSourceDirs(sourcesPath.incoming.artifactView { lenient(true) }.files)
+ executionData(coverageDataPath.incoming.artifactView { lenient(true) }.files.filter { it.exists() && it.extension == "exec" })
+
+ reports {
+ html.required.set(true)
+ xml.required.set(true)
+ }
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-kotlin-conventions.gradle.kts b/buildSrc/src/main/kotlin/toolkit-kotlin-conventions.gradle.kts
new file mode 100644
index 0000000000..a73fec92a8
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-kotlin-conventions.gradle.kts
@@ -0,0 +1,86 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import software.aws.toolkits.gradle.jvmTarget
+import software.aws.toolkits.gradle.kotlinTarget
+
+plugins {
+ id("java")
+ kotlin("jvm")
+ id("toolkit-detekt")
+}
+
+// TODO: https://github.com/gradle/gradle/issues/15383
+val versionCatalog = extensions.getByType().named("libs")
+dependencies {
+ implementation(versionCatalog.findBundle("kotlin").get())
+ implementation(versionCatalog.findLibrary("kotlin-coroutines").get())
+
+ testImplementation(versionCatalog.findLibrary("kotlin-test").get())
+}
+
+sourceSets {
+ main {
+ java {
+ setSrcDirs(listOf("src"))
+ }
+ resources {
+ setSrcDirs(listOf("resources"))
+ }
+ }
+
+ test {
+ java {
+ setSrcDirs(listOf("tst"))
+ }
+ resources {
+ setSrcDirs(listOf("tst-resources"))
+ }
+ }
+}
+
+val javaVersion = project.jvmTarget().get()
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
+
+tasks.withType().all {
+ kotlinOptions {
+ jvmTarget = javaVersion.majorVersion
+ apiVersion = project.kotlinTarget().get()
+ languageVersion = project.kotlinTarget().get()
+ freeCompilerArgs = listOf("-Xjvm-default=all")
+ }
+}
+
+tasks.withType().configureEach {
+ jvmTarget = javaVersion.majorVersion
+ dependsOn(":detekt-rules:assemble")
+ include("**/*.kt")
+ exclude("build/**")
+ exclude("**/*.Generated.kt")
+ exclude("**/TelemetryDefinitions.kt")
+}
+
+tasks.withType().configureEach {
+ jvmTarget = javaVersion.majorVersion
+ dependsOn(":detekt-rules:assemble")
+ include("**/*.kt")
+ exclude("build/**")
+ exclude("**/*.Generated.kt")
+ exclude("**/TelemetryDefinitions.kt")
+}
+
+project.afterEvaluate {
+ tasks.check {
+ dependsOn(tasks.detekt, tasks.detektMain, tasks.detektTest)
+
+ tasks.findByName("detektIntegrationTest")?.let {
+ dependsOn(it)
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/toolkit-testing.gradle.kts b/buildSrc/src/main/kotlin/toolkit-testing.gradle.kts
new file mode 100644
index 0000000000..1d02d707d7
--- /dev/null
+++ b/buildSrc/src/main/kotlin/toolkit-testing.gradle.kts
@@ -0,0 +1,102 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import software.aws.toolkits.gradle.ciOnly
+
+plugins {
+ id("java") // Needed for referencing "implementation" configuration
+ id("jacoco")
+ id("org.gradle.test-retry")
+ id("com.adarshr.test-logger")
+}
+
+// TODO: https://github.com/gradle/gradle/issues/15383
+val versionCatalog = extensions.getByType().named("libs")
+dependencies {
+ testImplementation(versionCatalog.findBundle("mockito").get())
+ testImplementation(versionCatalog.findLibrary("assertj").get())
+
+ // Don't add a test framework by default since we use junit4, junit5, and testng depending on project
+}
+
+jacoco {
+ // need to probe resolved dependencies directly if moved to rich version declaration
+ toolVersion = versionCatalog.findVersion("jacoco").get().toString()
+}
+
+// TODO: Can we model this using https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
+val testArtifacts by configurations.creating
+val testJar = tasks.register("testJar") {
+ archiveBaseName.set("${project.name}-test")
+ from(sourceSets.test.get().output)
+}
+
+// Silly but allows higher throughput of the build because we can start compiling / testing other modules while the tests run
+// This works because the sourceSet 'integrationTest' extends 'test', so it won't compile until after 'test' is compiled, but the
+// task graph goes 'compileTest*' -> 'test' -> 'compileIntegrationTest*' -> 'testJar'.
+// By flipping the order of the graph slightly, we can unblock downstream consumers of the testJar to start running tasks while this project
+// can be executing the 'test' task.
+tasks.test {
+ mustRunAfter(testJar)
+}
+
+artifacts {
+ add("testArtifacts", testJar)
+}
+
+tasks.withType().all {
+ ciOnly {
+ retry {
+ failOnPassedAfterRetry.set(false)
+ maxFailures.set(5)
+ maxRetries.set(2)
+ }
+ }
+
+ reports {
+ junitXml.required.set(true)
+ html.required.set(true)
+ }
+
+ testlogger {
+ showFullStackTraces = true
+ showStandardStreams = true
+ showPassedStandardStreams = false
+ showSkippedStandardStreams = true
+ showFailedStandardStreams = true
+ }
+
+ configure {
+ // sync with intellij-subplugin
+ // don't instrument sdk, icons, etc.
+ includes = listOf("software.aws.toolkits.*")
+ excludes = listOf("software.aws.toolkits.telemetry.*")
+
+ // 221+ uses a custom classloader and jacoco fails to find classes
+ isIncludeNoLocationClasses = true
+ }
+}
+
+// Jacoco configs taken from official Gradle docs: https://docs.gradle.org/current/userguide/structuring_software_products.html
+
+// Do not generate reports for individual projects, see toolkit-jacoco-report plugin
+tasks.jacocoTestReport.configure {
+ enabled = false
+}
+
+// Share the coverage data to be aggregated for the whole product
+// this can be removed once we're using jvm-test-suites properly
+configurations.create("coverageDataElements") {
+ isVisible = false
+ isCanBeResolved = false
+ isCanBeConsumed = true
+ extendsFrom(configurations.implementation.get())
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
+ attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("jacoco-coverage-data"))
+ }
+ tasks.withType {
+ outgoing.artifact(extensions.getByType().destinationFile!!)
+ }
+}
diff --git a/buildSrc/tst/SourceUtilsTest.kt b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/SourceUtilsTest.kt
similarity index 79%
rename from buildSrc/tst/SourceUtilsTest.kt
rename to buildSrc/src/test/kotlin/software/aws/toolkits/gradle/SourceUtilsTest.kt
index ffff559f32..12ba1e2f96 100644
--- a/buildSrc/tst/SourceUtilsTest.kt
+++ b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/SourceUtilsTest.kt
@@ -1,6 +1,8 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
+package software.aws.toolkits.gradle
+
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -17,14 +19,19 @@ class SourceUtilsTest(private val folderName: String, private val expected: Bool
arrayOf("tst-201", true),
arrayOf("tst-190+", true),
arrayOf("tst-201+", true),
+ arrayOf("tst-201-202", true),
+ arrayOf("tst-193-201", true),
+ arrayOf("tst-193-202", true),
arrayOf("tst-resources", false),
arrayOf("tst-resources-201", false),
arrayOf("tst-192", false),
arrayOf("tst-202", false),
arrayOf("tst-202+", false),
+ arrayOf("src-201", false),
arrayOf("random", false),
- arrayOf("src-tst", false)
+ arrayOf("src-tst", false),
+ arrayOf("tst-192-193", false)
)
}
diff --git a/buildSrc/tst/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt
similarity index 92%
rename from buildSrc/tst/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt
rename to buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt
index 4cfab03746..163ad6ac72 100644
--- a/buildSrc/tst/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt
+++ b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt
@@ -1,22 +1,22 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
-import com.nhaarman.mockitokotlin2.argumentCaptor
-import com.nhaarman.mockitokotlin2.inOrder
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
import org.assertj.core.api.Assertions.assertThat
import org.intellij.lang.annotations.Language
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.mockito.ArgumentMatchers.anyString
-import toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry
-import toolkits.gradle.changelog.ChangeType.BUGFIX
-import toolkits.gradle.changelog.ChangeType.FEATURE
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.inOrder
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import software.aws.toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry
+import software.aws.toolkits.gradle.changelog.ChangeType.BUGFIX
+import software.aws.toolkits.gradle.changelog.ChangeType.FEATURE
import java.nio.file.Path
import java.time.LocalDate
@@ -77,7 +77,7 @@ class ChangeLogGeneratorTest {
)
val writer = mock()
- val sut = ChangeLogGenerator(listOf(writer))
+ val sut = ChangeLogGenerator(listOf(writer), mock())
sut.addReleasedChanges(listOf(first, third, second))
sut.close()
@@ -148,7 +148,7 @@ class ChangeLogGeneratorTest {
"""
)
- val sut = ChangeLogGenerator(mock())
+ val sut = ChangeLogGenerator(mock(), mock())
sut.addReleasedChanges(listOf(first, second))
}
@@ -171,7 +171,7 @@ class ChangeLogGeneratorTest {
val firstWriter = mock()
val secondWriter = mock()
- val sut = ChangeLogGenerator(listOf(firstWriter, secondWriter))
+ val sut = ChangeLogGenerator(listOf(firstWriter, secondWriter), mock())
sut.addReleasedChanges(listOf(entry))
sut.close()
@@ -184,7 +184,7 @@ class ChangeLogGeneratorTest {
@Test
fun basicWrite() {
val writer = mock()
- val sut = ChangeLogGenerator(listOf(writer))
+ val sut = ChangeLogGenerator(listOf(writer), mock())
val first = createFile(
"""
@@ -246,7 +246,7 @@ class ChangeLogGeneratorTest {
@Test
fun canHandleMarkdown() {
val writer = mock()
- val sut = ChangeLogGenerator(listOf(writer))
+ val sut = ChangeLogGenerator(listOf(writer), mock())
val first = createFile(
"""
@@ -289,7 +289,7 @@ class ChangeLogGeneratorTest {
@Test
fun canHandleMultiLine() {
val writer = mock()
- val sut = ChangeLogGenerator(listOf(writer))
+ val sut = ChangeLogGenerator(listOf(writer), mock())
val first = createFile(
"""
diff --git a/buildSrc/tst/toolkits/gradle/changelog/GitStagerTest.kt b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/GitStagerTest.kt
similarity index 98%
rename from buildSrc/tst/toolkits/gradle/changelog/GitStagerTest.kt
rename to buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/GitStagerTest.kt
index e273dd747c..a398707533 100644
--- a/buildSrc/tst/toolkits/gradle/changelog/GitStagerTest.kt
+++ b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/GitStagerTest.kt
@@ -1,7 +1,7 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
import org.assertj.core.api.Assertions.assertThat
import org.eclipse.jgit.api.Git
diff --git a/buildSrc/tst/toolkits/gradle/changelog/GithubWriterTest.kt b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt
similarity index 90%
rename from buildSrc/tst/toolkits/gradle/changelog/GithubWriterTest.kt
rename to buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt
index 22b51e417f..79d566c8b2 100644
--- a/buildSrc/tst/toolkits/gradle/changelog/GithubWriterTest.kt
+++ b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt
@@ -1,13 +1,13 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
-import toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry
+import software.aws.toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry
import java.time.LocalDate
class GithubWriterTest {
@@ -64,7 +64,9 @@ class GithubWriterTest {
sut.writeLine(
renderEntry(
ReleaseEntry(
- LocalDate.of(2017, 2, 1), "2.0.0-preview-3", listOf(
+ LocalDate.of(2017, 2, 1),
+ "2.0.0-preview-3",
+ listOf(
Entry(
ChangeType.FEATURE,
"A feature with some an issue link #45 or (#12) but not regular #hash"
diff --git a/buildSrc/tst/toolkits/gradle/changelog/JetBrainsWriterTest.kt b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt
similarity index 93%
rename from buildSrc/tst/toolkits/gradle/changelog/JetBrainsWriterTest.kt
rename to buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt
index e147c8dbc3..47d2751712 100644
--- a/buildSrc/tst/toolkits/gradle/changelog/JetBrainsWriterTest.kt
+++ b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt
@@ -1,13 +1,13 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
-import toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry
+import software.aws.toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry
import java.time.LocalDate
class JetBrainsWriterTest {
@@ -103,7 +103,9 @@ class JetBrainsWriterTest {
sut.writeLine(
renderEntry(
ReleaseEntry(
- LocalDate.of(2017, 2, 1), "2.0.0-preview-3", listOf(
+ LocalDate.of(2017, 2, 1),
+ "2.0.0-preview-3",
+ listOf(
Entry(
ChangeType.FEATURE,
"A feature with some *code* sample\n```java\nhello();\n```"
@@ -136,7 +138,9 @@ class JetBrainsWriterTest {
sut.writeLine(
renderEntry(
ReleaseEntry(
- LocalDate.of(2017, 2, 1), "2.0.0-preview-3", listOf(
+ LocalDate.of(2017, 2, 1),
+ "2.0.0-preview-3",
+ listOf(
Entry(
ChangeType.FEATURE,
"A feature with some an issue link #45 or (#12) but not regular #hash"
diff --git a/buildSrc/tst/toolkits/gradle/changelog/ReleaseCreatorTest.kt b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt
similarity index 85%
rename from buildSrc/tst/toolkits/gradle/changelog/ReleaseCreatorTest.kt
rename to buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt
index a51dfc04c8..a96e42dd20 100644
--- a/buildSrc/tst/toolkits/gradle/changelog/ReleaseCreatorTest.kt
+++ b/buildSrc/src/test/kotlin/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt
@@ -1,8 +1,9 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-package toolkits.gradle.changelog
+package software.aws.toolkits.gradle.changelog
+import org.mockito.kotlin.mock
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
@@ -30,7 +31,7 @@ class ReleaseCreatorTest {
"type": "bugfix",
"description": "Some bugfix"
}
- """.trimIndent()
+ """.trimIndent()
)
}
@@ -41,11 +42,11 @@ class ReleaseCreatorTest {
"type": "feature",
"description": "Some feature"
}
- """.trimIndent()
+ """.trimIndent()
)
}
- val sut = ReleaseCreator(listOf(firstFile, secondFile), nextReleaseFile)
+ val sut = ReleaseCreator(listOf(firstFile, secondFile), nextReleaseFile, mock())
sut.create("2.0.0", date)
@@ -62,7 +63,7 @@ class ReleaseCreatorTest {
"description" : "Some bugfix"
} ]
}
- """.trimIndent()
+ """.trimIndent()
)
assertThat(firstFile).doesNotExist()
@@ -73,12 +74,6 @@ class ReleaseCreatorTest {
fun exitingReleaseVersionThrows() {
val nextReleaseFile = folder.newFolder().resolve("2.0.0.json")
nextReleaseFile.createNewFile()
- ReleaseCreator(listOf(folder.newFile()), nextReleaseFile)
- }
-
- @Test(expected = RuntimeException::class)
- fun noChangesThrows() {
- val nextReleaseFile = folder.newFolder().resolve("2.0.0.json")
- ReleaseCreator(listOf(), nextReleaseFile)
+ ReleaseCreator(listOf(folder.newFile()), nextReleaseFile, mock())
}
}
diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLogPlugin.kt b/buildSrc/src/toolkits/gradle/changelog/ChangeLogPlugin.kt
deleted file mode 100644
index 4e50a47816..0000000000
--- a/buildSrc/src/toolkits/gradle/changelog/ChangeLogPlugin.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package toolkits.gradle.changelog
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import toolkits.gradle.changelog.tasks.CreateRelease
-import toolkits.gradle.changelog.tasks.NewChange
-
-@Suppress("unused") // Plugin is created by buildSrc/build.gradle
-class ChangeLogPlugin : Plugin {
- override fun apply(project: Project) {
- project.tasks.register("createRelease", CreateRelease::class.java) {
- it.description = "Generates a release entry from unreleased changelog entries"
- }
-
- project.tasks.register("newChange", NewChange::class.java) {
- it.description = "Creates a new change entry for inclusion in the Change Log"
- }
-
- project.tasks.register("newFeature", NewChange::class.java) {
- it.description = "Creates a new feature change entry for inclusion in the Change Log"
- it.defaultChangeType = ChangeType.FEATURE
- }
-
- project.tasks.register("newBugFix", NewChange::class.java) {
- it.description = "Creates a new bug-fix change entry for inclusion in the Change Log"
- it.defaultChangeType = ChangeType.BUGFIX
- }
- }
-}
diff --git a/buildSrc/src/toolkits/gradle/changelog/JetBrainsWriter.kt b/buildSrc/src/toolkits/gradle/changelog/JetBrainsWriter.kt
deleted file mode 100644
index 986ca9d226..0000000000
--- a/buildSrc/src/toolkits/gradle/changelog/JetBrainsWriter.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package toolkits.gradle.changelog
-
-import org.commonmark.node.AbstractVisitor
-import org.commonmark.node.Heading
-import org.commonmark.parser.Parser
-import org.commonmark.renderer.html.HtmlRenderer
-import java.io.File
-import java.lang.Math.max
-import java.lang.Math.min
-
-class JetBrainsWriter(private val changeNotesFile: File, issueUrl: String? = null) : ChangeLogWriter(issueUrl) {
- private val sb = StringBuilder()
-
- override fun append(line: String) {
- sb.append(line)
- }
-
- override fun close() {
- val renderer = HtmlRenderer.builder()
- .softbreak("
")
- .build()
- val parser = Parser.builder()
- .postProcessor {
- it.accept(object : AbstractVisitor() {
- override fun visit(heading: Heading) {
- heading.level = max(1, min(heading.level + 2, 6))
- }
- })
-
- it
- }
- .build()
- val htmlVersionError = renderer.render(parser.parse(sb.toString()))
-
- changeNotesFile.writeText("""
-
-
-
-
-
- """.trimIndent())
- }
-
- override fun toString(): String = "JetBrainsWriter(file=$changeNotesFile)"
-}
diff --git a/buildSrc/src/toolkits/gradle/changelog/ReleaseCreator.kt b/buildSrc/src/toolkits/gradle/changelog/ReleaseCreator.kt
deleted file mode 100644
index 3fee200808..0000000000
--- a/buildSrc/src/toolkits/gradle/changelog/ReleaseCreator.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package toolkits.gradle.changelog
-
-import java.io.File
-import java.time.LocalDate
-
-class ReleaseCreator(private val unreleasedFiles: Collection, private val nextReleaseFile: File) {
- init {
- if (nextReleaseFile.exists()) {
- throw RuntimeException("Release file $nextReleaseFile already exists!")
- }
- if (unreleasedFiles.isEmpty()) {
- throw RuntimeException("No unreleased changes!")
- }
- }
-
- fun create(version: String, date: LocalDate = LocalDate.now()) {
- val entriesByType = unreleasedFiles.map { readFile(it) }.groupBy { it.type }
- val entries = ChangeType.values().flatMap { entriesByType.getOrDefault(it, emptyList()) }
- val release = ReleaseEntry(date, version, entries)
-
- MAPPER.writerWithDefaultPrettyPrinter().writeValue(nextReleaseFile, release)
- unreleasedFiles.forEach { it.delete() }
- }
-}
diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/CreateRelease.kt b/buildSrc/src/toolkits/gradle/changelog/tasks/CreateRelease.kt
deleted file mode 100644
index 8ca71867f1..0000000000
--- a/buildSrc/src/toolkits/gradle/changelog/tasks/CreateRelease.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package toolkits.gradle.changelog.tasks
-
-import org.gradle.api.file.RegularFileProperty
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.TaskAction
-import toolkits.gradle.changelog.ReleaseCreator
-import java.time.LocalDate
-import java.time.format.DateTimeFormatter
-
-open class CreateRelease : ChangeLogTask() {
- @Input
- val releaseDate: Property = project.objects.property(String::class.java).convention(DateTimeFormatter.ISO_DATE.format(LocalDate.now()))
-
- @Input
- val releaseVersion: Property = project.objects.property(String::class.java).convention(project.provider {
- (project.version as String).substringBeforeLast('-')
- })
-
- @OutputFile
- val releaseFile: RegularFileProperty = project.objects.fileProperty().convention(changesDirectory.file(releaseVersion.map { "$it.json" }))
-
- @TaskAction
- fun create() {
- val releaseDate = DateTimeFormatter.ISO_DATE.parse(releaseDate.get()).let {
- LocalDate.from(it)
- }
-
- val releaseEntries = nextReleaseDirectory.jsonFiles()
-
- val creator = ReleaseCreator(releaseEntries.files, releaseFile.get().asFile)
- creator.create(releaseVersion.get(), releaseDate)
- if (git != null) {
- git.stage(releaseFile.get().asFile.absoluteFile)
- git.stage(nextReleaseDirectory.get().asFile.absoluteFile)
- }
- }
-}
diff --git a/buildSrc/src/toolkits/gradle/sdk/GenerateSdk.kt b/buildSrc/src/toolkits/gradle/sdk/GenerateSdk.kt
deleted file mode 100644
index d94f280fe9..0000000000
--- a/buildSrc/src/toolkits/gradle/sdk/GenerateSdk.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package toolkits.gradle.sdk
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.InputDirectory
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-import software.amazon.awssdk.codegen.C2jModels
-import software.amazon.awssdk.codegen.CodeGenerator
-import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig
-import software.amazon.awssdk.codegen.model.service.Paginators
-import software.amazon.awssdk.codegen.model.service.ServiceModel
-import software.amazon.awssdk.codegen.utils.ModelLoaderUtils
-import java.io.File
-
-/* ktlint-disable custom-ktlint-rules:log-not-lazy */
-open class GenerateSdk : DefaultTask() {
- @InputDirectory
- lateinit var c2jFolder: File
-
- @OutputDirectory
- lateinit var outputDir: File
-
- @TaskAction
- fun generate() {
- outputDir.deleteRecursively()
-
- logger.info("Generating SDK from $c2jFolder")
- val models = C2jModels.builder()
- .serviceModel(loadServiceModel())
- .paginatorsModel(loadPaginatorsModel())
- .customizationConfig(loadCustomizationConfig())
- .build()
-
- CodeGenerator.builder()
- .models(models)
- .sourcesDirectory(outputDir.absolutePath)
- .fileNamePrefix(models.serviceModel().metadata.serviceId)
- .build()
- .execute()
- }
-
- private fun loadServiceModel(): ServiceModel? =
- ModelLoaderUtils.loadModel(ServiceModel::class.java, File(c2jFolder, "service-2.json"))
-
- private fun loadPaginatorsModel(): Paginators? {
- val paginatorsFile = File(c2jFolder, "paginators-1.json")
- if (paginatorsFile.exists())
- return ModelLoaderUtils.loadModel(Paginators::class.java, paginatorsFile)
- return null
- }
-
- private fun loadCustomizationConfig(): CustomizationConfig = ModelLoaderUtils.loadOptionalModel(
- CustomizationConfig::class.java,
- File(c2jFolder, "customization.config")
- ).orElse(CustomizationConfig.create())
-}
diff --git a/buildspec/linuxIntegrationTests.yml b/buildspec/linuxIntegrationTests.yml
index 79c627fa6d..a77886db9f 100644
--- a/buildspec/linuxIntegrationTests.yml
+++ b/buildspec/linuxIntegrationTests.yml
@@ -2,7 +2,7 @@ version: 0.2
cache:
paths:
- - '/root/.gradle/caches/**/*'
+# - '/root/.gradle/caches/**/*'
- '/root/.gradle/wrapper/**/*'
env:
@@ -13,34 +13,58 @@ env:
phases:
install:
- runtime-versions:
- java: corretto11
- docker: 19
- dotnet: 3.1
-
commands:
- - apt-get update
- - apt-get install -y jq python2.7 python-pip python3.6 python3.7 python3.8 python3-pip python3-distutils
- - aws sts assume-role --role-arn $ASSUME_ROLE_ARN --role-session-name integ-test > creds.json
- - export KEY_ID=`jq -r '.Credentials.AccessKeyId' creds.json`
- - export SECRET=`jq -r '.Credentials.SecretAccessKey' creds.json`
- - export TOKEN=`jq -r '.Credentials.SessionToken' creds.json`
- - pip3 install --user --upgrade aws-sam-cli
- - pip3 install --upgrade awscli
- pip3 install cfn-lint
+ - sed -i 's/latest\/download/download\/v1.98.0/g' /usr/local/bin/installSam.sh
+ - installSam.sh --update
+ - goenv versions --bare | xargs -I'{}' goenv uninstall -f '{}'
+ - goenv install 1.21.3
+ - goenv global 1.21.3
+ - startDocker.sh
+ # login to DockerHub so we don't get throttled
+ - export DOCKER_USERNAME=`echo $DOCKER_HUB_TOKEN | jq -r '.username'`
+ - export DOCKER_PASSWORD=`echo $DOCKER_HUB_TOKEN | jq -r '.password'`
+ - docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD || true
+ - DOTNET_ROOT="/root/.dotnet" /usr/local/bin/dotnet-install.sh --channel 6.0
+ - export PATH="${PATH}:${DOTNET_ROOT}"
+ - export PATH="$PATH:$HOME/.dotnet/tools"
+ - dotnet codeartifact-creds install
+ # should probably be managed as an extension/rule in any tests that need a screen available
+ - /usr/bin/Xvfb :22 -screen 0 1920x1080x24 &
build:
commands:
- - export SAM_CLI_EXEC=`which sam`
- - echo "SAM CLI location $SAM_CLI_EXEC"
- - $SAM_CLI_EXEC --version
+ - |
+ if [ "$CODEARTIFACT_DOMAIN_NAME" ] && [ "$CODEARTIFACT_REPO_NAME" ]; then
+ CODEARTIFACT_URL=$(aws codeartifact get-repository-endpoint --domain $CODEARTIFACT_DOMAIN_NAME --repository $CODEARTIFACT_REPO_NAME --format maven --query repositoryEndpoint --output text)
+ CODEARTIFACT_NUGET_URL=$(aws codeartifact get-repository-endpoint --domain $CODEARTIFACT_DOMAIN_NAME --repository $CODEARTIFACT_REPO_NAME --format nuget --query repositoryEndpoint --output text)
+ CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain $CODEARTIFACT_DOMAIN_NAME --query authorizationToken --output text --duration-seconds 3600)
+ fi
+
+ - AWS_CONFIG_FILE=`mktemp`
+ - |
+ >$AWS_CONFIG_FILE echo "[default]
+ role_arn=$ASSUME_ROLE_ARN
+ credential_source=EcsContainer"
+
- chmod +x gradlew
- - env AWS_ACCESS_KEY_ID=$KEY_ID AWS_SECRET_ACCESS_KEY=$SECRET AWS_SESSION_TOKEN=$TOKEN ./gradlew integrationTest coverageReport --info --full-stacktrace --console plain
+ - DISPLAY=:22 ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME integrationTest coverageReport -x :jetbrains-rider:integrationTest --info --console plain
+ - |
+ if [ $(docker ps -q | wc -l) -gt 0 ]; then
+ echo 'Docker containers were not completely cleaned up!';
+ docker ps;
+ for container in $(docker ps -q); do
+ echo $container;
+ docker exec -i $container sh -c 'tail -n +1 /tmp/logs/*';
+ done;
+
+ exit 1;
+ fi
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') # Encode `#` in the URL because otherwise the url is clipped in the Codecov.io site
- CI_BUILD_ID="${CODEBUILD_BUILD_ID}"
- - test -n "$CODE_COV_TOKEN" && curl -s https://codecov.io/bash > codecov.sh || true # this sometimes times out but we don't want to fail the build
- - test -n "$CODE_COV_TOKEN" && bash ./codecov.sh -t $CODE_COV_TOKEN -F integtest || true
+ - test -n "$CODE_COV_TOKEN" && curl -Os https://uploader.codecov.io/latest/linux/codecov && chmod +x codecov || true # this sometimes times out but we don't want to fail the build
+ - test -n "$CODE_COV_TOKEN" && test -n "$CODEBUILD_BUILD_SUCCEEDING" && ./codecov -t $CODE_COV_TOKEN -F integtest || true
post_build:
commands:
diff --git a/buildspec/linuxTests.yml b/buildspec/linuxTests.yml
index 8672637db9..5d1b1e8d25 100644
--- a/buildspec/linuxTests.yml
+++ b/buildspec/linuxTests.yml
@@ -2,7 +2,7 @@ version: 0.2
cache:
paths:
- - '/root/.gradle/caches/**/*'
+# - '/root/.gradle/caches/**/*'
- '/root/.gradle/wrapper/**/*'
env:
@@ -12,20 +12,31 @@ env:
phases:
install:
- runtime-versions:
- java: corretto11
- dotnet: 3.1
+ commands:
+ - useradd codebuild-user
+ - dnf install -y acl
+ - chown -R codebuild-user:codebuild-user /codebuild/output
+ - setfacl -m d:o::rwx,o::rwx /root
build:
commands:
+ - |
+ if [ "$CODEARTIFACT_DOMAIN_NAME" ] && [ "$CODEARTIFACT_REPO_NAME" ]; then
+ CODEARTIFACT_URL=$(aws codeartifact get-repository-endpoint --domain $CODEARTIFACT_DOMAIN_NAME --repository $CODEARTIFACT_REPO_NAME --format maven --query repositoryEndpoint --output text)
+ CODEARTIFACT_NUGET_URL=$(aws codeartifact get-repository-endpoint --domain $CODEARTIFACT_DOMAIN_NAME --repository $CODEARTIFACT_REPO_NAME --format nuget --query repositoryEndpoint --output text)
+ CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain $CODEARTIFACT_DOMAIN_NAME --query authorizationToken --output text --duration-seconds 3600)
+ su codebuild-user -c "dotnet codeartifact-creds install"
+ fi
+
- chmod +x gradlew
- - ./gradlew check coverageReport --info --full-stacktrace --console plain
- - ./gradlew buildPlugin
+ - su codebuild-user -c "./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME check coverageReport --info --console plain --continue"
+ - ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME buildPlugin
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') # Encode `#` in the URL because otherwise the url is clipped in the Codecov.io site
- CI_BUILD_ID="${CODEBUILD_BUILD_ID}"
- - test -n "$CODE_COV_TOKEN" && curl -s https://codecov.io/bash > codecov.sh || true # this sometimes times out but we don't want to fail the build
- - test -n "$CODE_COV_TOKEN" && bash ./codecov.sh -t $CODE_COV_TOKEN -F unittest || true
+ - test -n "$CODE_COV_TOKEN" && curl -Os https://uploader.codecov.io/latest/linux/codecov && chmod +x codecov || true # this sometimes times out but we don't want to fail the build
+ - test -n "$CODE_COV_TOKEN" && test -n "$CODEBUILD_BUILD_SUCCEEDING" && ./codecov -t $CODE_COV_TOKEN -F unittest || true
+ - test -n "$CODE_COV_TOKEN" && test -n "$CODEBUILD_BUILD_SUCCEEDING" && ./codecov -t $CODE_COV_TOKEN -F codewhisperer || true
post_build:
commands:
@@ -36,7 +47,7 @@ phases:
- rsync -rmq --include='*/' --include '**/build/idea-sandbox/system*/log/**' --exclude='*' . $TEST_ARTIFACTS/ || true
- rsync -rmq --include='*/' --include '**/build/reports/**' --exclude='*' . $TEST_ARTIFACTS/ || true
- rsync -rmq --include='*/' --include '**/test-results/**/*.xml' --exclude='*' . $TEST_ARTIFACTS/test-reports || true
- - cp -r ./build/distributions/*.zip $BUILD_ARTIFACTS/ || true
+ - cp -r ./intellij/build/distributions/*.zip $BUILD_ARTIFACTS/ || touch $BUILD_ARTIFACTS/build_failed
reports:
unit-test:
diff --git a/buildspec/linuxUiTests.yml b/buildspec/linuxUiTests.yml
index ed4d130f87..98e99c1d60 100644
--- a/buildspec/linuxUiTests.yml
+++ b/buildspec/linuxUiTests.yml
@@ -1,72 +1,73 @@
version: 0.2
-#cache:
-# paths:
-# - 'gradle-home/caches/**/*'
-# - 'gradle-home/wrapper/**/*'
+cache:
+ paths:
+# - '/root/.gradle/caches/**/*'
+# - '/root/.gradle/wrapper/**/*'
env:
variables:
CI: true
- RECORD_UI: true
LOCAL_ENV_RUN: true
- GRADLE_USER_HOME: gradle-home
AWS_STS_REGIONAL_ENDPOINTS: regional
+ DISPLAY: :99
+ SCREEN_WIDTH: 1920
+ SCREEN_HEIGHT: 1080
+ SCREEN_DEPTH: 24
+
phases:
install:
- runtime-versions:
- java: corretto11
- dotnet: 3.1
-
commands:
- - apt-get update
- - apt-get install -y xvfb icewm procps ffmpeg libswt-gtk-3-java
- - mkdir -p /tmp/.aws
- - aws sts assume-role --role-arn $ASSUME_ROLE_ARN --role-session-name ui-test > /tmp/.aws/creds.json
- - export KEY_ID=`jq -r '.Credentials.AccessKeyId' /tmp/.aws/creds.json`
- - export SECRET=`jq -r '.Credentials.SecretAccessKey' /tmp/.aws/creds.json`
- - export TOKEN=`jq -r '.Credentials.SessionToken' /tmp/.aws/creds.json`
- - |
- >/tmp/.aws/credentials echo "[default]
- aws_access_key_id=$KEY_ID
- aws_secret_access_key=$SECRET
- aws_session_token=$TOKEN"
- - pip3 install --user --upgrade aws-sam-cli
+ - dnf install -y marco mate-media
+ - startDesktop.sh
+
+ # login to DockerHub so we don't get throttled
+ - export DOCKER_USERNAME=`echo $DOCKER_HUB_TOKEN | jq -r '.username'`
+ - export DOCKER_PASSWORD=`echo $DOCKER_HUB_TOKEN | jq -r '.password'`
+ - docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD || true
+ - export PATH="$PATH:$HOME/.dotnet/tools"
+ - dotnet codeartifact-creds install
build:
commands:
- - export SAM_CLI_EXEC=`which sam`
- - echo "SAM CLI location $SAM_CLI_EXEC"
- - $SAM_CLI_EXEC --version
- - Xvfb :99 -screen 0 1920x1080x24 &
- - export DISPLAY=:99
- - while [ ! -e /tmp/.X11-unix/X99 ]; do sleep 0.1; done
- - icewm &
- - chmod +x gradlew
- - ./gradlew buildPlugin --console plain --info
- - >
- if [ "$RECORD_UI" ]; then
- ffmpeg -loglevel warning -f x11grab -video_size 1920x1080 -i :99 -codec:v libx264 -r 12 /tmp/screen_recording.mp4 &
+ - |
+ if [ "$CODEARTIFACT_DOMAIN_NAME" ] && [ "$CODEARTIFACT_REPO_NAME" ]; then
+ CODEARTIFACT_URL=$(aws codeartifact get-repository-endpoint --domain $CODEARTIFACT_DOMAIN_NAME --repository $CODEARTIFACT_REPO_NAME --format maven --query repositoryEndpoint --output text)
+ CODEARTIFACT_NUGET_URL=$(aws codeartifact get-repository-endpoint --domain $CODEARTIFACT_DOMAIN_NAME --repository $CODEARTIFACT_REPO_NAME --format nuget --query repositoryEndpoint --output text)
+ CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain $CODEARTIFACT_DOMAIN_NAME --query authorizationToken --output text --duration-seconds 3600)
fi
- - env AWS_ACCESS_KEY_ID=$KEY_ID AWS_SECRET_ACCESS_KEY=$SECRET AWS_SESSION_TOKEN=$TOKEN ./gradlew uiTestCore coverageReport --console plain --info
+
+ - AWS_CONFIG_FILE=`mktemp`
+ - |
+ >$AWS_CONFIG_FILE echo "[default]
+ role_arn=$ASSUME_ROLE_ARN
+ credential_source=EcsContainer"
+
+ - chmod +x gradlew
+ - ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME buildPlugin --console plain --info
+
+ - ffmpeg -loglevel quiet -nostdin -f x11grab -video_size ${SCREEN_WIDTH}x${SCREEN_HEIGHT} -i ${DISPLAY} -codec:v libx264 -pix_fmt yuv420p -vf drawtext="fontsize=48:box=1:boxcolor=black@0.75:boxborderw=5:fontcolor=white:x=0:y=h-text_h:text='%{gmtime\:%H\\\\\:%M\\\\\:%S}'" -framerate 12 -g 12 /tmp/screen_recording.mp4 &
+ - ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME uiTestCore coverageReport --console plain --info
post_build:
commands:
- TEST_ARTIFACTS="/tmp/testArtifacts"
- mkdir -p $TEST_ARTIFACTS/test-reports
+
+ - pkill -SIGINT ffmpeg && sleep 5
+
- rsync -rmq --include='*/' --include '**/build/idea-sandbox/system*/log/**' --exclude='*' . $TEST_ARTIFACTS/ || true
- rsync -rmq --include='*/' --include '**/build/reports/**' --exclude='*' . $TEST_ARTIFACTS/ || true
- rsync -rmq --include='*/' --include '**/test-results/**/*.xml' --exclude='*' . $TEST_ARTIFACTS/test-reports || true
- - if [ "$RECORD_UI" ]; then pkill -2 ffmpeg; while pgrep ffmpeg > /dev/null; do sleep 1; done; fi
- - if [ "$RECORD_UI" ]; then cp /tmp/screen_recording.mp4 $TEST_ARTIFACTS/; fi
+ - mv /tmp/screen_recording.mp4 $TEST_ARTIFACTS/
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') # Encode `#` in the URL because otherwise the url is clipped in the Codecov.io site
- CI_BUILD_ID="${CODEBUILD_BUILD_ID}"
- - test -n "$CODE_COV_TOKEN" && curl -s https://codecov.io/bash > codecov.sh || true # this sometimes times out but we don't want to fail the build
- - test -n "$CODE_COV_TOKEN" && test -n "$CODEBUILD_BUILD_SUCCEEDING" && bash ./codecov.sh -t $CODE_COV_TOKEN -F uitest || true
+ - test -n "$CODE_COV_TOKEN" && curl -Os https://uploader.codecov.io/latest/linux/codecov && chmod +x codecov || true # this sometimes times out but we don't want to fail the build
+ - test -n "$CODE_COV_TOKEN" && test -n "$CODEBUILD_BUILD_SUCCEEDING" && ./codecov -t $CODE_COV_TOKEN -F uitest || true
reports:
ui-test:
diff --git a/buildspec/windowsTests.yml b/buildspec/windowsTests.yml
index 1ecd03bae3..24d0346c1d 100644
--- a/buildspec/windowsTests.yml
+++ b/buildspec/windowsTests.yml
@@ -7,19 +7,54 @@ env:
phases:
install:
- runtime-versions:
- java: openjdk11
- dotnet: 2.2
-
commands:
+ - |
+ $url = 'https://corretto.aws/downloads/latest/amazon-corretto-17-x64-windows-jdk.msi';
+ Write-Host ('Downloading from {0}' -f $url);
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
+ Invoke-WebRequest -Uri $url -OutFile 'corretto_jdk_build.msi';
+ Start-Process 'corretto_jdk_build.msi' -PassThru | Wait-Process
+ - |
+ $javaName = "C:\Program Files\Amazon Corretto" | ForEach-Object {
+ ls $_ | Sort-Object -Descending -Property Name | Select-Object -first 1 -expandproperty Name
+ }
+ $JAVA_HOME = "C:\Program Files\Amazon Corretto\$javaName"
- |
if(-Not($Env:CODE_COV_TOKEN -eq $null)) {
- choco install -y --no-progress codecov
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
+ Invoke-WebRequest -Uri https://uploader.codecov.io/latest/windows/codecov.exe -Outfile codecov.exe
}
+ - dotnet --list-sdks
+ - |
+ Invoke-WebRequest 'https://dot.net/v1/dotnet-install.ps1' -OutFile 'dotnet-install.ps1';
+ ./dotnet-install.ps1 -Verbose -InstallDir 'C:\Program Files\dotnet' -Channel '5.0'
+ - |
+ $DOTNET_ROOT = "$Env:USERPROFILE\.dotnet"
+ $Env:PATH = "$Env:PATH;$DOTNET_ROOT;$DOTNET_ROOT\tools"
+ dotnet tool install -g AWS.CodeArtifact.NuGet.CredentialProvider
+ dotnet codeartifact-creds install
+ - dotnet --list-sdks
build:
commands:
- - ./gradlew check coverageReport --info --full-stacktrace --console plain
+ - |
+ # See https://github.com/NuGet/NuGet.Client/pull/4259
+ $Env:NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY = "3,1000"
+
+ $Env:JAVA_HOME = $JAVA_HOME
+ if ($Env:CODEARTIFACT_DOMAIN_NAME -and $Env:CODEARTIFACT_REPO_NAME) {
+ $Env:CODEARTIFACT_URL=aws codeartifact get-repository-endpoint --domain $Env:CODEARTIFACT_DOMAIN_NAME --repository $Env:CODEARTIFACT_REPO_NAME --format maven --query repositoryEndpoint --output text
+ $Env:CODEARTIFACT_NUGET_URL=aws codeartifact get-repository-endpoint --domain $Env:CODEARTIFACT_DOMAIN_NAME --repository $Env:CODEARTIFACT_REPO_NAME --format nuget --query repositoryEndpoint --output text
+ $Env:CODEARTIFACT_AUTH_TOKEN=aws codeartifact get-authorization-token --domain $Env:CODEARTIFACT_DOMAIN_NAME --query authorizationToken --output text --duration-seconds 3600
+ }
+
+ # Rider is very expensive (spikes our CI jobs to 50% CPU, so let it do the prep work in parallel, but run tests later
+ ./gradlew -PideProfileName="$Env:ALTERNATIVE_IDE_PROFILE_NAME" check :jetbrains-rider:compileTestKotlin -x :jetbrains-rider:test --info --console plain --continue
+ if ($LastExitCode -ne 0) {
+ Write-Host "Command failed with exit code $LastExitCode"
+ exit -1
+ }
+ # ./gradlew -PideProfileName="$Env:ALTERNATIVE_IDE_PROFILE_NAME" :jetbrains-rider:check coverageReport --info --console plain
post_build:
commands:
@@ -48,8 +83,8 @@ phases:
$env:VCS_COMMIT_ID=$Env:CODEBUILD_RESOLVED_SOURCE_VERSION;
$env:CI_BUILD_URL=[uri]::EscapeUriString($Env:CODEBUILD_BUILD_URL);
$env:CI_BUILD_ID=$Env:CODEBUILD_BUILD_ID;
- codecov -t $Env:CODE_COV_TOKEN `
- --flag unittest `
+ .\codecov.exe -t $Env:CODE_COV_TOKEN `
+ --flags unittest `
-f "build/reports/jacoco/coverageReport/coverageReport.xml" `
-c $Env:CODEBUILD_RESOLVED_SOURCE_VERSION
}
diff --git a/codecov.yml b/codecov.yml
index fafed1135e..cd234eeca1 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -4,6 +4,7 @@
codecov:
notify:
require_ci_to_pass: no
+ max_report_age: off
coverage:
precision: 2
@@ -20,6 +21,12 @@ coverage:
only_pulls: true
flags:
- "unittest"
+ codewhisperer:
+ target: 75%
+ paths:
+ - "**/src/software/aws/toolkits/jetbrains/services/codewhisperer/*"
+ flags:
+ - "codewhisperer"
patch:
default:
threshold: 1
@@ -28,6 +35,7 @@ coverage:
unittest:
threshold: 1
only_pulls: true
+ informational: true
flags:
- "unittest"
changes: no
@@ -35,8 +43,13 @@ coverage:
comment: false
ignore:
- - "ktlint-rules/**/*"
+ - "detekt-rules/**/*"
- "resources/**/*"
- - "telemetry-client/**/*"
+ - "sdk-codegen/**/*"
- "jetbrains-rider/**/*.Generated.kt"
- "**/TelemetryDefinitions.kt"
+
+flags:
+ codewhisperer:
+ paths:
+ - "**/src/software/aws/toolkits/jetbrains/services/codewhisperer/"
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 50778eb78f..af988342d7 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -1,24 +1,31 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-val awsSdkVersion: String by project
-val jacksonVersion: String by project
-val coroutinesVersion: String by project
+plugins {
+ id("toolkit-kotlin-conventions")
+ id("toolkit-testing")
+ id("toolkit-integration-testing")
+}
dependencies {
api(project(":resources"))
- api(project(":telemetry-client"))
- api("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
- api("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")
- api("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion")
- api("software.amazon.awssdk:cognitoidentity:$awsSdkVersion")
- api("software.amazon.awssdk:ecs:$awsSdkVersion")
- api("software.amazon.awssdk:s3:$awsSdkVersion")
- api("software.amazon.awssdk:sso:$awsSdkVersion")
- api("software.amazon.awssdk:ssooidc:$awsSdkVersion")
- api("software.amazon.awssdk:sts:$awsSdkVersion")
+ api(project(":sdk-codegen"))
+
+ api(libs.aws.cognitoidentity)
+ api(libs.aws.ecr)
+ api(libs.aws.ecs)
+ api(libs.aws.lambda)
+ api(libs.aws.s3)
+ api(libs.aws.sso)
+ api(libs.aws.ssooidc)
+ api(libs.aws.sts)
+ api(libs.bundles.jackson)
- compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
+ testImplementation(libs.junit4)
+
+ testRuntimeOnly(libs.junit5.jupiterVintage)
+}
- testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
+tasks.test {
+ useJUnitPlatform()
}
diff --git a/core/detekt-baseline-integrationTest.xml b/core/detekt-baseline-integrationTest.xml
new file mode 100644
index 0000000000..b9d892d26e
--- /dev/null
+++ b/core/detekt-baseline-integrationTest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+ NoNameShadowing:BucketUtilsTest.kt$BucketUtilsTest${ it.status(BucketVersioningStatus.ENABLED) }
+
+
diff --git a/core/detekt-baseline-main.xml b/core/detekt-baseline-main.xml
new file mode 100644
index 0000000000..2674934bb2
--- /dev/null
+++ b/core/detekt-baseline-main.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ SleepInsteadOfDelay:Waiter.kt$sleep(delay.toMillis())
+ UseCheckOrError:LambdaRuntime.kt$LambdaRuntime$throw IllegalStateException("LambdaRuntime has no runtime or override string")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("$serviceId in ${region.partitionId} lacks a partitionEndpoint")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("$serviceId is not global in ${region.partitionId}")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("Partition data is missing for ${region.partitionId}")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("Unknown service $serviceId in ${region.partitionId}")
+
+
diff --git a/core/detekt-baseline-test.xml b/core/detekt-baseline-test.xml
new file mode 100644
index 0000000000..538e8f4c56
--- /dev/null
+++ b/core/detekt-baseline-test.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ UnsafeCallOnNullableType:EnvironmentVariableHelper.kt$EnvironmentVariableHelper$getField(System.getenv().javaClass, System.getenv(), "m")!!
+ UnsafeCallOnNullableType:PartitionParserTest.kt$PartitionParserTest$PartitionParser.parse(BundledResources.ENDPOINTS_FILE)!!
+ UnsafeCallOnNullableType:ZipUtilsTest.kt$ZipUtilsTest$zipFile!!
+
+
diff --git a/core/detekt-baseline.xml b/core/detekt-baseline.xml
new file mode 100644
index 0000000000..8e5f932438
--- /dev/null
+++ b/core/detekt-baseline.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ UseCheckOrError:LambdaRuntime.kt$LambdaRuntime$throw IllegalStateException("LambdaRuntime has no runtime or override string")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("$serviceId in ${region.partitionId} lacks a partitionEndpoint")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("$serviceId is not global in ${region.partitionId}")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("Partition data is missing for ${region.partitionId}")
+ UseCheckOrError:ToolkitRegionProvider.kt$ToolkitRegionProvider$throw IllegalStateException("Unknown service $serviceId in ${region.partitionId}")
+
+
diff --git a/core/it/software/aws/toolkits/core/s3/BucketUtilsTest.kt b/core/it/software/aws/toolkits/core/s3/BucketUtilsTest.kt
index b9bf4b4fe3..50ca0c2340 100644
--- a/core/it/software/aws/toolkits/core/s3/BucketUtilsTest.kt
+++ b/core/it/software/aws/toolkits/core/s3/BucketUtilsTest.kt
@@ -14,12 +14,12 @@ import software.amazon.awssdk.services.s3.model.PutObjectRequest
import software.aws.toolkits.core.rules.S3TemporaryBucketRule
class BucketUtilsTest {
- private val usEast2Client = S3Client.builder().region(Region.US_EAST_2).build()
+ private val usEast1Client = S3Client.builder().region(Region.US_EAST_1).build()
private val euWest2Client = S3Client.builder().region(Region.EU_WEST_2).build()
@Rule
@JvmField
- val usEast2TempBucket = S3TemporaryBucketRule(usEast2Client)
+ val usEast1TempBucket = S3TemporaryBucketRule(usEast1Client)
@Rule
@JvmField
@@ -33,16 +33,16 @@ class BucketUtilsTest {
@Test
fun deleteABucketWithObjects() {
createAndDeleteBucket { bucket ->
- usEast2Client.putObject(PutObjectRequest.builder().bucket(bucket).key("hello").build(), RequestBody.fromString(""))
+ usEast1Client.putObject(PutObjectRequest.builder().bucket(bucket).key("hello").build(), RequestBody.fromString(""))
}
}
@Test
fun deleteABucketWithVersionedObjects() {
createAndDeleteBucket { bucket ->
- usEast2Client.putBucketVersioning { it.bucket(bucket).versioningConfiguration { it.status(BucketVersioningStatus.ENABLED) } }
- usEast2Client.putObject(PutObjectRequest.builder().bucket(bucket).key("hello").build(), RequestBody.fromString(""))
- usEast2Client.putObject(PutObjectRequest.builder().bucket(bucket).key("hello").build(), RequestBody.fromString(""))
+ usEast1Client.putBucketVersioning { it.bucket(bucket).versioningConfiguration { it.status(BucketVersioningStatus.ENABLED) } }
+ usEast1Client.putObject(PutObjectRequest.builder().bucket(bucket).key("hello").build(), RequestBody.fromString(""))
+ usEast1Client.putObject(PutObjectRequest.builder().bucket(bucket).key("hello").build(), RequestBody.fromString(""))
}
}
@@ -50,20 +50,20 @@ class BucketUtilsTest {
fun canGetRegionBucketWithRegionNotSameAsClient() {
val bucket = euWest2TempBucket.createBucket()
- assertThat(usEast2Client.regionForBucket(bucket)).isEqualTo("eu-west-2")
+ assertThat(usEast1Client.regionForBucket(bucket)).isEqualTo("eu-west-2")
}
@Test
fun canGetRegionInSameRegionAsClient() {
- val bucket = usEast2TempBucket.createBucket()
+ val bucket = usEast1TempBucket.createBucket()
- assertThat(usEast2Client.regionForBucket(bucket)).isEqualTo("us-east-2")
+ assertThat(usEast1Client.regionForBucket(bucket)).isEqualTo("us-east-1")
}
private fun createAndDeleteBucket(populateBucket: (String) -> Unit) {
- val bucket = usEast2TempBucket.createBucket()
+ val bucket = usEast1TempBucket.createBucket()
populateBucket(bucket)
- usEast2Client.deleteBucketAndContents(bucket)
- assertThat(usEast2Client.listBuckets().buckets().map { it.name() }).doesNotContain(bucket)
+ usEast1Client.deleteBucketAndContents(bucket)
+ assertThat(usEast1Client.listBuckets().buckets().map { it.name() }).doesNotContain(bucket)
}
}
diff --git a/core/src/software/aws/toolkits/core/ConnectionSettings.kt b/core/src/software/aws/toolkits/core/ConnectionSettings.kt
new file mode 100644
index 0000000000..9ae2272ff0
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/ConnectionSettings.kt
@@ -0,0 +1,41 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core
+
+import software.aws.toolkits.core.credentials.ToolkitBearerTokenProvider
+import software.aws.toolkits.core.credentials.ToolkitCredentialsProvider
+import software.aws.toolkits.core.credentials.toEnvironmentVariables
+import software.aws.toolkits.core.region.AwsRegion
+
+sealed interface ClientConnectionSettings {
+ val region: AwsRegion
+ val providerId: String
+
+ /**
+ * Copies bean with the region replaced
+ */
+ fun withRegion(region: AwsRegion): ClientConnectionSettings
+}
+
+data class ConnectionSettings(val credentials: ToolkitCredentialsProvider, override val region: AwsRegion) : ClientConnectionSettings {
+ override val providerId: String
+ get() = credentials.id
+
+ override fun withRegion(region: AwsRegion) = copy(region = region)
+}
+
+data class TokenConnectionSettings(
+ val tokenProvider: ToolkitBearerTokenProvider,
+ override val region: AwsRegion
+) : ClientConnectionSettings {
+ override val providerId: String
+ get() = tokenProvider.id
+
+ override fun withRegion(region: AwsRegion) = copy(region = region)
+}
+
+val ConnectionSettings.shortName get() = "${credentials.shortName}@${region.id}"
+
+fun ConnectionSettings.toEnvironmentVariables(): Map = region.toEnvironmentVariables() +
+ credentials.resolveCredentials().toEnvironmentVariables()
diff --git a/core/src/software/aws/toolkits/core/ToolkitClientCustomizer.kt b/core/src/software/aws/toolkits/core/ToolkitClientCustomizer.kt
new file mode 100644
index 0000000000..cb2f2cffc0
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/ToolkitClientCustomizer.kt
@@ -0,0 +1,34 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core
+
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
+import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider
+import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder
+import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration
+
+/**
+ * Used to override/add behavior during AWS SDK Client creation.
+ *
+ * Example usage to add a local development endpoint for a particular service:
+ *
+ * ```
+ * class MyDevEndpointCustomizer : AwsClientCustomizer {
+ * override fun customize(credentialProvider: AwsCredentialsProvider, regionId: String, builder: AwsClientBuilder<*, *>) {
+ * if (builder is LambdaClientBuilder && connection.region.id == "us-west-2") {
+ * builder.endpointOverride(URI.create("http://localhost:8888"))
+ * }
+ * }
+ * }
+ * ```
+ */
+fun interface ToolkitClientCustomizer {
+ fun customize(
+ credentialProvider: AwsCredentialsProvider?,
+ tokenProvider: SdkTokenProvider?,
+ regionId: String,
+ builder: AwsClientBuilder<*, *>,
+ clientOverrideConfiguration: ClientOverrideConfiguration.Builder
+ )
+}
diff --git a/core/src/software/aws/toolkits/core/ToolkitClientManager.kt b/core/src/software/aws/toolkits/core/ToolkitClientManager.kt
index d6e49661e2..4253d4a940 100644
--- a/core/src/software/aws/toolkits/core/ToolkitClientManager.kt
+++ b/core/src/software/aws/toolkits/core/ToolkitClientManager.kt
@@ -5,21 +5,24 @@ package software.aws.toolkits.core
import org.jetbrains.annotations.TestOnly
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
+import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider
+import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner
+import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder
import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder
import software.amazon.awssdk.core.SdkClient
+import software.amazon.awssdk.core.client.builder.SdkSyncClientBuilder
+import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption
+import software.amazon.awssdk.core.retry.RetryMode
import software.amazon.awssdk.http.SdkHttpClient
import software.amazon.awssdk.regions.Region
-import software.amazon.awssdk.services.s3.S3ClientBuilder
-import software.amazon.awssdk.services.s3.internal.handlers.CreateBucketInterceptor
-import software.amazon.awssdk.services.s3.internal.handlers.DecodeUrlEncodedResponseInterceptor
-import software.amazon.awssdk.services.s3.internal.handlers.DisableDoubleUrlEncodingInterceptor
-import software.amazon.awssdk.services.s3.internal.handlers.EnableChunkedEncodingInterceptor
-import software.amazon.awssdk.services.s3.internal.handlers.EndpointAddressInterceptor
-import software.amazon.awssdk.services.s3.internal.handlers.PutObjectInterceptor
+import software.amazon.awssdk.utils.SdkAutoCloseable
+import software.aws.toolkits.core.clients.nullDefaultProfileFile
import software.aws.toolkits.core.credentials.ToolkitCredentialsProvider
import software.aws.toolkits.core.region.AwsRegion
import software.aws.toolkits.core.region.ToolkitRegionProvider
+import software.aws.toolkits.core.utils.getLogger
+import software.aws.toolkits.core.utils.warn
import java.lang.reflect.Modifier
import java.net.URI
import java.util.concurrent.ConcurrentHashMap
@@ -30,136 +33,182 @@ import kotlin.reflect.KClass
*/
abstract class ToolkitClientManager {
data class AwsClientKey(
- val credentialProviderId: String,
+ val providerId: String,
val region: AwsRegion,
val serviceClass: KClass
)
private val cachedClients = ConcurrentHashMap()
- protected abstract val sdkHttpClient: SdkHttpClient
protected abstract val userAgent: String
- inline fun getClient(
- credentialsProviderOverride: ToolkitCredentialsProvider? = null,
- regionOverride: AwsRegion? = null
- ): T = this.getClient(T::class, credentialsProviderOverride, regionOverride)
+ protected abstract fun sdkHttpClient(): SdkHttpClient
- @Suppress("UNCHECKED_CAST")
- open fun getClient(
- clz: KClass,
- credentialsProviderOverride: ToolkitCredentialsProvider? = null,
- regionOverride: AwsRegion? = null
- ): T {
- val credProvider = credentialsProviderOverride ?: getCredentialsProvider()
- val region = regionOverride ?: getRegion()
+ inline fun getClient(credProvider: ToolkitCredentialsProvider, region: AwsRegion): T =
+ this.getClient(T::class, ConnectionSettings(credProvider, region))
+
+ inline fun getClient(connection: ClientConnectionSettings<*>): T = this.getClient(T::class, connection)
+ fun getClient(sdkClass: KClass, connection: ClientConnectionSettings<*>): T {
val key = AwsClientKey(
- credentialProviderId = credProvider.id,
- region = region,
- serviceClass = clz
+ providerId = connection.providerId,
+ region = connection.region,
+ serviceClass = sdkClass
)
- val serviceId = key.serviceClass.java.getField("SERVICE_NAME").get(null) as String
- if (serviceId !in GLOBAL_SERVICE_BLACKLIST && getRegionProvider().isServiceGlobal(region, serviceId)) {
- val globalRegion = getRegionProvider().getGlobalRegionForService(region, serviceId)
- return cachedClients.computeIfAbsent(key.copy(region = globalRegion)) { createNewClient(it, globalRegion, credProvider) } as T
+ val serviceId = key.serviceClass.java.getField("SERVICE_METADATA_ID").get(null) as String
+ if (serviceId !in GLOBAL_SERVICE_DENY_LIST && getRegionProvider().isServiceGlobal(connection.region, serviceId)) {
+ val globalRegion = getRegionProvider().getGlobalRegionForService(connection.region, serviceId)
+ @Suppress("UNCHECKED_CAST")
+ return cachedClients.computeIfAbsent(key.copy(region = globalRegion)) {
+ createNewClient(sdkClass, connection.withRegion(region = globalRegion))
+ } as T
}
- return cachedClients.computeIfAbsent(key) { createNewClient(it, region, credProvider) } as T
+ @Suppress("UNCHECKED_CAST")
+ return cachedClients.computeIfAbsent(key) { createNewClient(sdkClass, connection) } as T
+ }
+
+ private fun createNewClient(sdkClass: KClass, connection: ClientConnectionSettings<*>): T = when (connection) {
+ is ConnectionSettings -> constructAwsClient(
+ sdkClass = sdkClass,
+ credProvider = connection.credentials,
+ region = Region.of(connection.region.id),
+ )
+
+ is TokenConnectionSettings -> constructAwsClient(
+ sdkClass = sdkClass,
+ tokenProvider = connection.tokenProvider,
+ region = Region.of(connection.region.id),
+ )
}
/**
- * Get the current active credential provider for the toolkit
+ * Constructs a new low-level AWS client whose lifecycle is **NOT** managed centrally. Caller is responsible for shutting down the client
+ */
+ inline fun createUnmanagedClient(
+ credProvider: AwsCredentialsProvider,
+ region: Region,
+ endpointOverride: String? = null,
+ clientCustomizer: ToolkitClientCustomizer? = null
+ ): T = createUnmanagedClient(T::class, credProvider, region, endpointOverride, clientCustomizer)
+
+ /**
+ * Constructs a new low-level AWS client whose lifecycle is **NOT** managed centrally. Caller is responsible for shutting down the client
*/
- protected abstract fun getCredentialsProvider(): ToolkitCredentialsProvider
+ fun createUnmanagedClient(
+ sdkClass: KClass,
+ credProvider: AwsCredentialsProvider,
+ region: Region,
+ endpointOverride: String?,
+ clientCustomizer: ToolkitClientCustomizer? = null
+ ): T = constructAwsClient(sdkClass, credProvider = credProvider, region = region, endpointOverride = endpointOverride, clientCustomizer = clientCustomizer)
protected abstract fun getRegionProvider(): ToolkitRegionProvider
/**
- * Get the current active region for the toolkit
+ * Allow implementations to apply customizations to clients before they are built
*/
- protected abstract fun getRegion(): AwsRegion
+ protected open fun globalClientCustomizer(
+ credentialProvider: AwsCredentialsProvider?,
+ tokenProvider: SdkTokenProvider?,
+ regionId: String,
+ builder: AwsClientBuilder<*, *>,
+ clientOverrideConfiguration: ClientOverrideConfiguration.Builder
+ ) {}
/**
- * Calls [AutoCloseable.close] if client implements [AutoCloseable] and clears the cache
+ * Calls [SdkAutoCloseable.close] on all managed clients and clears the cache
*/
protected fun shutdown() {
- cachedClients.values.mapNotNull { it as? AutoCloseable }.forEach { it.close() }
+ cachedClients.values.forEach { it.close() }
+ cachedClients.clear()
}
protected fun invalidateSdks(providerId: String) {
- cachedClients.keys.removeIf { it.credentialProviderId == providerId }
+ val invalidClients = cachedClients.entries.filter { it.key.providerId == providerId }.toSet()
+ cachedClients.entries.removeAll(invalidClients)
+ invalidClients.forEach { it.value.close() }
}
- /**
- * Used by [software.aws.toolkits.jetbrains.core.MockClientManager]
- */
- @TestOnly
- protected fun clear() = cachedClients.clear()
-
- @TestOnly
- fun cachedClients() = cachedClients
-
- /**
- * Creates a new client for the requested [AwsClientKey]
- */
- @Suppress("UNCHECKED_CAST")
- protected open fun createNewClient(
- key: AwsClientKey,
- region: AwsRegion = key.region,
- credProvider: ToolkitCredentialsProvider = getCredentialsProvider()
+ protected open fun constructAwsClient(
+ sdkClass: KClass,
+ credProvider: AwsCredentialsProvider? = null,
+ tokenProvider: SdkTokenProvider? = null,
+ region: Region,
+ endpointOverride: String? = null,
+ clientCustomizer: ToolkitClientCustomizer? = null
): T {
- val sdkClass = key.serviceClass as KClass
- return createNewClient(sdkClass, sdkHttpClient, Region.of(region.id), credProvider, userAgent)
- }
+ checkNotNull(credProvider ?: tokenProvider) { "Either a credential provider or a bearer token provider must be provided" }
+
+ val builderMethod = sdkClass.java.methods.find {
+ it.name == "builder" && Modifier.isStatic(it.modifiers) && Modifier.isPublic(it.modifiers)
+ } ?: throw IllegalArgumentException("Expected service interface to have a public static `builder()` method.")
+
+ val builder = builderMethod.invoke(null) as AwsDefaultClientBuilder<*, *>
+
+ @Suppress("UNCHECKED_CAST")
+ return builder
+ .region(region)
+ .apply {
+ if (this is SdkSyncClientBuilder<*, *>) {
+ // async clients use CRT, and keeps trying to shut down our apache client even though it doesn't respect our client settings
+ // so only set this for sync clients
+ httpClient(sdkHttpClient())
+ }
- companion object {
- private val GLOBAL_SERVICE_BLACKLIST = setOf(
- // sts is regionalized but does not identify as such in metadata
- "sts"
- )
+ val clientOverrideConfig = ClientOverrideConfiguration.builder()
- fun createNewClient(
- sdkClass: KClass,
- httpClient: SdkHttpClient,
- region: Region,
- credProvider: AwsCredentialsProvider,
- userAgent: String,
- endpointOverride: String? = null
- ): T {
- val builderMethod = sdkClass.java.methods.find {
- it.name == "builder" && Modifier.isStatic(it.modifiers) && Modifier.isPublic(it.modifiers)
- } ?: throw IllegalArgumentException("Expected service interface to have a public static `builder()` method.")
+ if (credProvider != null) {
+ credentialsProvider(credProvider)
+ }
- val builder = builderMethod.invoke(null) as AwsDefaultClientBuilder<*, *>
+ if (tokenProvider != null) {
+ val tokenMethod = builderMethod.returnType.methods.find {
+ it.name == "tokenProvider" &&
+ it.parameterCount == 1 &&
+ it.parameters[0].type.name == "software.amazon.awssdk.auth.token.credentials.SdkTokenProvider"
+ }
- @Suppress("UNCHECKED_CAST")
- return builder
- .httpClient(httpClient)
- .credentialsProvider(credProvider)
- .region(region)
- .overrideConfiguration {
- it.putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_PREFIX, userAgent)
- if (builder is S3ClientBuilder) {
- // TODO: Remove after SDK code-gens these instead of uses class loader
- it.addExecutionInterceptor(EndpointAddressInterceptor())
- it.addExecutionInterceptor(CreateBucketInterceptor())
- it.addExecutionInterceptor(PutObjectInterceptor())
- it.addExecutionInterceptor(EnableChunkedEncodingInterceptor())
- it.addExecutionInterceptor(DisableDoubleUrlEncodingInterceptor())
- it.addExecutionInterceptor(DecodeUrlEncodedResponseInterceptor())
+ if (tokenMethod == null) {
+ LOG.warn { "Ignoring bearer provider parameter for ${sdkClass.qualifiedName} since it's not a supported client attribute" }
+ } else {
+ tokenMethod.invoke(this, tokenProvider)
+ clientOverrideConfig.nullDefaultProfileFile()
+ // TODO: why do we need this?
+ clientOverrideConfig.putAdvancedOption(SdkAdvancedClientOption.SIGNER, BearerTokenSigner())
}
}
- .also { _ ->
- endpointOverride?.let {
- builder.endpointOverride(URI.create(it))
- }
- if (builder is S3ClientBuilder) {
- builder.serviceConfiguration { it.pathStyleAccessEnabled(true) }
- }
+
+ clientOverrideConfig.let { configuration ->
+ configuration.putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_PREFIX, userAgent)
+ configuration.retryPolicy(RetryMode.STANDARD)
}
- .build() as T
- }
+
+ endpointOverride?.let {
+ endpointOverride(URI.create(it))
+ }
+
+ globalClientCustomizer(credProvider, tokenProvider, region.id(), this, clientOverrideConfig)
+
+ clientCustomizer?.let {
+ it.customize(credProvider, tokenProvider, region.id(), this, clientOverrideConfig)
+ }
+
+ // TODO: ban overrideConfiguration outside of here
+ overrideConfiguration(clientOverrideConfig.build())
+ }
+ .build() as T
+ }
+
+ @TestOnly
+ fun cachedClients() = cachedClients
+
+ companion object {
+ private val LOG = getLogger()
+ private val GLOBAL_SERVICE_DENY_LIST = setOf(
+ // sts is regionalized but does not identify as such in metadata
+ "sts"
+ )
}
}
diff --git a/core/src/software/aws/toolkits/core/clients/ClientBuilderUtils.kt b/core/src/software/aws/toolkits/core/clients/ClientBuilderUtils.kt
new file mode 100644
index 0000000000..c6b533b6a5
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/clients/ClientBuilderUtils.kt
@@ -0,0 +1,25 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.clients
+
+import software.amazon.awssdk.core.client.builder.SdkClientBuilder
+import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration
+import software.amazon.awssdk.profiles.ProfileFile
+import java.io.InputStream
+
+fun ClientOverrideConfiguration.Builder.nullDefaultProfileFile() = defaultProfileFile(
+ ProfileFile.builder()
+ .content(InputStream.nullInputStream())
+ .type(ProfileFile.Type.CONFIGURATION)
+ .build()
+)
+
+/**
+ * Only use if this is the only [overrideConfiguration] block used by the [SdkClientBuilder]
+ */
+fun SdkClientBuilder<*, C>.nullDefaultProfileFile() = apply {
+ overrideConfiguration {
+ it.nullDefaultProfileFile()
+ }
+}
diff --git a/core/src/software/aws/toolkits/core/clients/SdkClientProvider.kt b/core/src/software/aws/toolkits/core/clients/SdkClientProvider.kt
new file mode 100644
index 0000000000..9159a844e3
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/clients/SdkClientProvider.kt
@@ -0,0 +1,10 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.clients
+
+import software.amazon.awssdk.http.SdkHttpClient
+
+interface SdkClientProvider {
+ fun sharedSdkClient(): SdkHttpClient
+}
diff --git a/core/src/software/aws/toolkits/core/credentials/AwsCredentials.kt b/core/src/software/aws/toolkits/core/credentials/AwsCredentials.kt
new file mode 100644
index 0000000000..0bf0f45ff9
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/credentials/AwsCredentials.kt
@@ -0,0 +1,43 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.credentials
+
+import software.amazon.awssdk.auth.credentials.AwsCredentials
+import software.amazon.awssdk.auth.credentials.AwsSessionCredentials
+import software.amazon.awssdk.core.SdkSystemSetting
+
+private val CREDENTIAL_ENVIRONMENT_VARIABLES = setOf(
+ SdkSystemSetting.AWS_ACCESS_KEY_ID.environmentVariable(),
+ SdkSystemSetting.AWS_SECRET_ACCESS_KEY.environmentVariable(),
+ SdkSystemSetting.AWS_SESSION_TOKEN.environmentVariable()
+)
+
+fun AwsCredentials.toEnvironmentVariables(): Map {
+ val map = mutableMapOf()
+ map[SdkSystemSetting.AWS_ACCESS_KEY_ID.environmentVariable()] = this.accessKeyId()
+ map[SdkSystemSetting.AWS_SECRET_ACCESS_KEY.environmentVariable()] = this.secretAccessKey()
+
+ if (this is AwsSessionCredentials) {
+ map[SdkSystemSetting.AWS_SESSION_TOKEN.environmentVariable()] = this.sessionToken()
+ }
+
+ return map
+}
+
+fun AwsCredentials.mergeWithExistingEnvironmentVariables(existing: MutableMap, replace: Boolean = false) {
+ mergeWithExistingEnvironmentVariables(existing.keys, existing::remove, existing::putAll, replace)
+}
+
+fun AwsCredentials.mergeWithExistingEnvironmentVariables(
+ existingKeys: Collection,
+ removeKey: (String) -> Unit,
+ putValues: (Map) -> Unit,
+ replace: Boolean = false
+) {
+ val envVars = toEnvironmentVariables()
+ if (replace || existingKeys.none { it in CREDENTIAL_ENVIRONMENT_VARIABLES }) {
+ CREDENTIAL_ENVIRONMENT_VARIABLES.forEach { removeKey(it) }
+ putValues(envVars)
+ }
+}
diff --git a/core/src/software/aws/toolkits/core/credentials/CredentialProviderFactory.kt b/core/src/software/aws/toolkits/core/credentials/CredentialProviderFactory.kt
index c8a3aad744..6d5ebe605b 100644
--- a/core/src/software/aws/toolkits/core/credentials/CredentialProviderFactory.kt
+++ b/core/src/software/aws/toolkits/core/credentials/CredentialProviderFactory.kt
@@ -4,7 +4,6 @@
package software.aws.toolkits.core.credentials
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
-import software.amazon.awssdk.http.SdkHttpClient
import software.aws.toolkits.core.region.AwsRegion
/**
@@ -16,6 +15,11 @@ interface CredentialProviderFactory {
*/
val id: String
+ /**
+ * ID used to indicate where credentials are stored or retrieved from
+ */
+ val credentialSourceId: CredentialSourceId
+
/**
* Invoked on creation of the factory to update the credential system with what [CredentialIdentifier] this factory
* is capable of creating. The provided [credentialLoadCallback] is capable of being invoked multiple times in the case that
@@ -28,7 +32,6 @@ interface CredentialProviderFactory {
*/
fun createAwsCredentialProvider(
providerId: CredentialIdentifier,
- region: AwsRegion,
- sdkHttpClientSupplier: () -> SdkHttpClient
+ region: AwsRegion
): AwsCredentialsProvider
}
diff --git a/core/src/software/aws/toolkits/core/credentials/CredentialsChangeEvent.kt b/core/src/software/aws/toolkits/core/credentials/CredentialsChangeEvent.kt
index 0d7eadaf56..f2a2899a2e 100644
--- a/core/src/software/aws/toolkits/core/credentials/CredentialsChangeEvent.kt
+++ b/core/src/software/aws/toolkits/core/credentials/CredentialsChangeEvent.kt
@@ -8,9 +8,13 @@ package software.aws.toolkits.core.credentials
* to give an accurate representation of the state of the credentials system
*/
data class CredentialsChangeEvent(
- val added: List,
- val modified: List,
- val removed: List
+ val added: List = emptyList(),
+ val modified: List = emptyList(),
+ val removed: List = emptyList(),
+
+ val ssoAdded: List = emptyList(),
+ val ssoModified: List = emptyList(),
+ val ssoRemoved: List = emptyList()
)
typealias CredentialsChangeListener = (changeEvent: CredentialsChangeEvent) -> Unit
diff --git a/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProvider.kt b/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProvider.kt
index bacd0519cb..747330d9b1 100644
--- a/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProvider.kt
+++ b/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProvider.kt
@@ -4,6 +4,27 @@
package software.aws.toolkits.core.credentials
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
+import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider
+import software.aws.toolkits.resources.message
+
+enum class CredentialType {
+ StaticProfile,
+ StaticSessionProfile,
+ CredentialProcessProfile,
+ AssumeRoleProfile,
+ AssumeMfaRoleProfile,
+ SsoProfile,
+ Ec2Metadata,
+ EcsMetadata
+}
+
+enum class CredentialSourceId {
+ SharedCredentials,
+ SdkStore,
+ Ecs,
+ Ec2,
+ EnvVars
+}
/**
* Represents a possible credential provider that can be used within the toolkit.
@@ -33,13 +54,29 @@ interface CredentialIdentifier {
*/
val factoryId: String
+ /**
+ * The type of credential
+ */
+ val credentialType: CredentialType?
+
/**
* Some ID types (e.g. Profile) have a concept of a default region, this is optional.
*/
val defaultRegionId: String? get() = null
}
-abstract class CredentialIdentifierBase : CredentialIdentifier {
+interface SsoSessionBackedCredentialIdentifier {
+ val sessionIdentifier: String
+}
+
+interface SsoSessionIdentifier {
+ val id: String
+ val startUrl: String
+ val ssoRegion: String
+ val scopes: Set
+}
+
+abstract class CredentialIdentifierBase(override val credentialType: CredentialType?) : CredentialIdentifier {
final override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -56,9 +93,17 @@ abstract class CredentialIdentifierBase : CredentialIdentifier {
final override fun toString(): String = "${this::class.simpleName}(id='$id')"
}
-class ToolkitCredentialsProvider(private val identifier: CredentialIdentifier, delegate: AwsCredentialsProvider) : AwsCredentialsProvider by delegate {
- val id: String = identifier.id
- val displayName = identifier.displayName
+interface ToolkitAuthenticationProvider {
+ val id: String
+ val displayName: String
+}
+
+class ToolkitCredentialsProvider(
+ val identifier: CredentialIdentifier,
+ val delegate: AwsCredentialsProvider
+) : ToolkitAuthenticationProvider, AwsCredentialsProvider by delegate {
+ override val id: String = identifier.id
+ override val displayName = identifier.displayName
val shortName = identifier.shortName
override fun equals(other: Any?): Boolean {
@@ -76,3 +121,29 @@ class ToolkitCredentialsProvider(private val identifier: CredentialIdentifier, d
override fun toString(): String = "${this::class.simpleName}(identifier='$identifier')"
}
+
+// TODO: try to get rid of this because it's really annoying casting the delegate everywhere
+interface ToolkitBearerTokenProviderDelegate : SdkTokenProvider, ToolkitAuthenticationProvider
+
+class ToolkitBearerTokenProvider(val delegate: ToolkitBearerTokenProviderDelegate) : SdkTokenProvider by delegate, ToolkitAuthenticationProvider by delegate {
+ companion object {
+ // TODO: is there a better place for this
+ fun ssoIdentifier(startUrl: String, region: String = DEFAULT_SSO_REGION) = "sso;$region;$startUrl"
+
+ // TODO: For AWS Builder ID, we only have startUrl for now instead of each users' metadata data i.e. Email address
+ fun ssoDisplayName(startUrl: String) = if (startUrl == SONO_URL) {
+ message("aws_builder_id.service_name")
+ } else {
+ message("iam_identity_center.service_name", extractOrgID(startUrl))
+ }
+
+ fun diskSessionIdentifier(profileName: String) = "diskSessionProfile;$profileName"
+ fun diskSessionDisplayName(profileName: String) = "IAM Identity Center Session ($profileName)"
+ }
+}
+
+private const val SONO_URL = "https://view.awsapps.com/start"
+
+const val DEFAULT_SSO_REGION = "us-east-1"
+
+private fun extractOrgID(startUrl: String) = startUrl.removePrefix("https://").removeSuffix(".awsapps.com/start")
diff --git a/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProviderManager.kt b/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProviderManager.kt
index ea649b49c3..e7adcbac58 100644
--- a/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProviderManager.kt
+++ b/core/src/software/aws/toolkits/core/credentials/ToolkitCredentialsProviderManager.kt
@@ -10,6 +10,11 @@ interface ToolkitCredentialsChangeListener {
fun providerAdded(identifier: CredentialIdentifier) {}
fun providerModified(identifier: CredentialIdentifier) {}
fun providerRemoved(identifier: CredentialIdentifier) {}
+ fun providerRemoved(providerId: String) {}
+
+ fun ssoSessionAdded(identifier: SsoSessionIdentifier) {}
+ fun ssoSessionModified(identifier: SsoSessionIdentifier) {}
+ fun ssoSessionRemoved(identifier: SsoSessionIdentifier) {}
}
class CredentialProviderNotFoundException : RuntimeException {
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/AccessToken.kt b/core/src/software/aws/toolkits/core/credentials/sso/AccessToken.kt
deleted file mode 100644
index 58ed8b8774..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/AccessToken.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import software.amazon.awssdk.services.sso.SsoClient
-import software.amazon.awssdk.services.ssooidc.SsoOidcClient
-import java.time.Instant
-
-/**
- * Access token returned from [SsoOidcClient.createToken] used to retrieve AWS Credentials from [SsoClient.getRoleCredentials].
- */
-data class AccessToken(
- val startUrl: String,
- val region: String,
- val accessToken: String,
- val expiresAt: Instant
-)
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/Authorization.kt b/core/src/software/aws/toolkits/core/credentials/sso/Authorization.kt
deleted file mode 100644
index d40b3a9b3e..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/Authorization.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import software.amazon.awssdk.services.ssooidc.SsoOidcClient
-import java.time.Instant
-
-/**
- * Returned by [SsoOidcClient.startDeviceAuthorization] that contains the required data to construct the user visible SSO login flow.
- */
-data class Authorization(
- val deviceCode: String,
- val userCode: String,
- val verificationUri: String,
- val verificationUriComplete: String,
- val expiresAt: Instant,
- val pollInterval: Long
-)
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/ClientRegistration.kt b/core/src/software/aws/toolkits/core/credentials/sso/ClientRegistration.kt
deleted file mode 100644
index 5b59372ad1..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/ClientRegistration.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import software.amazon.awssdk.services.ssooidc.SsoOidcClient
-import java.time.Instant
-
-/**
- * Client registration that represents the toolkit returned from [SsoOidcClient.registerClient].
- *
- * It should be persisted for reuse through many authentication requests.
- */
-data class ClientRegistration(
- val clientId: String,
- val clientSecret: String,
- val expiresAt: Instant
-)
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/DiskCache.kt b/core/src/software/aws/toolkits/core/credentials/sso/DiskCache.kt
deleted file mode 100644
index 19af16e629..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/DiskCache.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.DeserializationFeature
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-import com.fasterxml.jackson.databind.module.SimpleModule
-import com.fasterxml.jackson.databind.util.StdDateFormat
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
-import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
-import software.aws.toolkits.core.utils.deleteIfExists
-import software.aws.toolkits.core.utils.filePermissions
-import software.aws.toolkits.core.utils.inputStreamIfExists
-import software.aws.toolkits.core.utils.outputStream
-import software.aws.toolkits.core.utils.toHexString
-import software.aws.toolkits.core.utils.touch
-import software.aws.toolkits.core.utils.tryOrNull
-import java.nio.file.Path
-import java.nio.file.Paths
-import java.nio.file.attribute.PosixFilePermission
-import java.security.MessageDigest
-import java.time.Clock
-import java.time.Instant
-import java.time.ZoneOffset
-import java.time.format.DateTimeFormatter.ISO_INSTANT
-import java.time.temporal.ChronoUnit
-import java.util.TimeZone
-
-/**
- * Caches the [AccessToken] to disk to allow it to be re-used with other tools such as the CLI.
- */
-class DiskCache(
- private val cacheDir: Path = Paths.get(System.getProperty("user.home"), ".aws", "sso", "cache"),
- private val clock: Clock = Clock.systemUTC()
-) : SsoCache {
- private val objectMapper = jacksonObjectMapper().also {
- it.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
-
- it.registerModule(JavaTimeModule())
- val customDateModule = SimpleModule()
- customDateModule.addDeserializer(Instant::class.java, CliCompatibleInstantDeserializer())
- it.registerModule(customDateModule) // Override the Instant deserializer with custom one
- it.dateFormat = StdDateFormat().withTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC))
- }
-
- override fun loadClientRegistration(ssoRegion: String): ClientRegistration? {
- val inputStream = clientRegistrationCache(ssoRegion).inputStreamIfExists() ?: return null
- return tryOrNull {
- val clientRegistration = objectMapper.readValue(inputStream)
- if (clientRegistration.expiresAt.isNotExpired()) {
- clientRegistration
- } else {
- null
- }
- }
- }
-
- override fun saveClientRegistration(ssoRegion: String, registration: ClientRegistration) {
- val registrationCache = clientRegistrationCache(ssoRegion)
- registrationCache.touch()
- registrationCache.filePermissions(setOf(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE))
-
- registrationCache.outputStream().use {
- objectMapper.writeValue(it, registration)
- }
- }
-
- override fun invalidateClientRegistration(ssoRegion: String) {
- clientRegistrationCache(ssoRegion).deleteIfExists()
- }
-
- override fun loadAccessToken(ssoUrl: String): AccessToken? {
- val cacheFile = accessTokenCache(ssoUrl)
- val inputStream = cacheFile.inputStreamIfExists() ?: return null
-
- return tryOrNull {
- val clientRegistration = objectMapper.readValue(inputStream)
- // Use same expiration logic as client registration even though RFC/SEP does not specify it.
- // This prevents a cache entry being returned as valid and then expired when we go to use it.
- if (clientRegistration.expiresAt.isNotExpired()) {
- clientRegistration
- } else {
- null
- }
- }
- }
-
- override fun saveAccessToken(ssoUrl: String, accessToken: AccessToken) {
- val accessTokenCache = accessTokenCache(ssoUrl)
- accessTokenCache.touch()
- accessTokenCache.filePermissions(setOf(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE))
-
- accessTokenCache.outputStream().use {
- objectMapper.writeValue(it, accessToken)
- }
- }
-
- override fun invalidateAccessToken(ssoUrl: String) {
- accessTokenCache(ssoUrl).deleteIfExists()
- }
-
- private fun clientRegistrationCache(ssoRegion: String): Path = cacheDir.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json")
-
- private fun accessTokenCache(ssoUrl: String): Path {
- val digest = MessageDigest.getInstance("SHA-1")
- val sha = digest.digest(ssoUrl.toByteArray(Charsets.UTF_8)).toHexString()
- val fileName = "$sha.json"
- return cacheDir.resolve(fileName)
- }
-
- // If the item is going to expire in the next 15 mins, we must treat it as already expired
- private fun Instant.isNotExpired(): Boolean = this.isAfter(Instant.now(clock).plus(15, ChronoUnit.MINUTES))
-
- private class CliCompatibleInstantDeserializer : StdDeserializer(Instant::class.java) {
- override fun deserialize(parser: JsonParser, context: DeserializationContext): Instant {
- val dateString = parser.valueAsString
-
- // CLI appends UTC, which Java refuses to parse. Convert it to a Z
- val sanitized = if (dateString.endsWith("UTC")) {
- dateString.dropLast(3) + 'Z'
- } else {
- dateString
- }
-
- return ISO_INSTANT.parse(sanitized) { Instant.from(it) }
- }
- }
-}
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/SsoAccessTokenProvider.kt b/core/src/software/aws/toolkits/core/credentials/sso/SsoAccessTokenProvider.kt
deleted file mode 100644
index b14f90f576..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/SsoAccessTokenProvider.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import kotlinx.coroutines.delay
-import software.amazon.awssdk.services.ssooidc.SsoOidcClient
-import software.amazon.awssdk.services.ssooidc.model.AuthorizationPendingException
-import software.amazon.awssdk.services.ssooidc.model.InvalidClientException
-import software.amazon.awssdk.services.ssooidc.model.SlowDownException
-import java.time.Clock
-import java.time.Duration
-import java.time.Instant
-
-/**
- * Takes care of creating/refreshing the SSO access token required to fetch SSO-based credentials.
- */
-class SsoAccessTokenProvider(
- private val ssoUrl: String,
- private val ssoRegion: String,
- private val onPendingToken: SsoLoginCallback,
- private val cache: SsoCache,
- private val client: SsoOidcClient,
- private val clock: Clock = Clock.systemUTC()
-) {
- suspend fun accessToken(): AccessToken {
- cache.loadAccessToken(ssoUrl)?.let {
- return it
- }
-
- val token = pollForToken()
-
- cache.saveAccessToken(ssoUrl, token)
-
- return token
- }
-
- private fun registerClient(): ClientRegistration {
- cache.loadClientRegistration(ssoRegion)?.let {
- return it
- }
-
- // Based on botocore: https://github.com/boto/botocore/blob/5dc8ee27415dc97cfff75b5bcfa66d410424e665/botocore/utils.py#L1753
- val registerResponse = client.registerClient {
- it.clientType(CLIENT_REGISTRATION_TYPE)
- it.clientName("aws-toolkit-jetbrains-${Instant.now(clock)}")
- }
-
- val registeredClient = ClientRegistration(
- registerResponse.clientId(),
- registerResponse.clientSecret(),
- Instant.ofEpochSecond(registerResponse.clientSecretExpiresAt())
- )
-
- cache.saveClientRegistration(ssoRegion, registeredClient)
-
- return registeredClient
- }
-
- private fun authorizeClient(clientId: ClientRegistration): Authorization {
- // Should not be cached, only good for 1 token and short lived
- val authorizationResponse = try {
- client.startDeviceAuthorization {
- it.startUrl(ssoUrl)
- it.clientId(clientId.clientId)
- it.clientSecret(clientId.clientSecret)
- }
- } catch (e: InvalidClientException) {
- cache.invalidateClientRegistration(ssoRegion)
- throw e
- }
-
- return Authorization(
- authorizationResponse.deviceCode(),
- authorizationResponse.userCode(),
- authorizationResponse.verificationUri(),
- authorizationResponse.verificationUriComplete(),
- Instant.now(clock).plusSeconds(authorizationResponse.expiresIn().toLong()),
- authorizationResponse.interval()?.toLong()
- ?: DEFAULT_INTERVAL_SECS
- )
- }
-
- private suspend fun pollForToken(): AccessToken {
- val registration = registerClient()
- val authorization = authorizeClient(registration)
-
- onPendingToken.tokenPending(authorization)
-
- var backOffTime = Duration.ofSeconds(authorization.pollInterval)
-
- while (true) {
- try {
- val tokenResponse = client.createToken {
- it.clientId(registration.clientId)
- it.clientSecret(registration.clientSecret)
- it.grantType(GRANT_TYPE)
- it.deviceCode(authorization.deviceCode)
- }
-
- val expirationTime = Instant.now(clock).plusSeconds(tokenResponse.expiresIn().toLong())
-
- onPendingToken.tokenRetrieved()
-
- return AccessToken(
- ssoUrl,
- ssoRegion,
- tokenResponse.accessToken(),
- expirationTime
- )
- } catch (e: SlowDownException) {
- backOffTime = backOffTime.plusSeconds(SLOW_DOWN_DELAY_SECS)
- } catch (e: AuthorizationPendingException) {
- // Do nothing, keep polling
- } catch (e: Exception) {
- onPendingToken.tokenRetrievalFailure(e)
- throw e
- }
-
- delay(backOffTime.toMillis())
- }
- }
-
- fun invalidate() {
- cache.invalidateAccessToken(ssoUrl)
- }
-
- private companion object {
- const val CLIENT_REGISTRATION_TYPE = "public"
- const val GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code"
- // Default number of seconds to poll for token, https://tools.ietf.org/html/draft-ietf-oauth-device-flow-15#section-3.5
- const val DEFAULT_INTERVAL_SECS = 5L
- const val SLOW_DOWN_DELAY_SECS = 5L
- }
-}
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/SsoCache.kt b/core/src/software/aws/toolkits/core/credentials/sso/SsoCache.kt
deleted file mode 100644
index 4650e3d35b..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/SsoCache.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-interface SsoCache {
- fun loadClientRegistration(ssoRegion: String): ClientRegistration?
- fun saveClientRegistration(ssoRegion: String, registration: ClientRegistration)
- fun invalidateClientRegistration(ssoRegion: String)
-
- fun loadAccessToken(ssoUrl: String): AccessToken?
- fun saveAccessToken(ssoUrl: String, accessToken: AccessToken)
- fun invalidateAccessToken(ssoUrl: String)
-}
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/SsoCredentialProvider.kt b/core/src/software/aws/toolkits/core/credentials/sso/SsoCredentialProvider.kt
deleted file mode 100644
index ddbf393891..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/SsoCredentialProvider.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import software.amazon.awssdk.auth.credentials.AwsCredentials
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
-import software.amazon.awssdk.auth.credentials.AwsSessionCredentials
-import software.amazon.awssdk.services.sso.SsoClient
-import software.amazon.awssdk.services.ssooidc.model.AccessDeniedException
-import software.amazon.awssdk.utils.cache.CachedSupplier
-import software.amazon.awssdk.utils.cache.RefreshResult
-import java.time.Duration
-import java.time.Instant
-
-/**
- * [AwsCredentialsProvider] that contains all the needed hooks to perform an end to end flow of an SSO-based credential.
- *
- * This credential provider will trigger an SSO login if required, unlike the low level SDKs.
- */
-class SsoCredentialProvider(
- private val ssoAccount: String,
- private val ssoRole: String,
- private val ssoClient: SsoClient,
- private val ssoAccessTokenProvider: SsoAccessTokenProvider
-) : AwsCredentialsProvider {
- private val sessionCache: CachedSupplier = CachedSupplier.builder(this::refreshCredentials).build()
-
- override fun resolveCredentials(): AwsCredentials = sessionCache.get().credentials
-
- private fun refreshCredentials(): RefreshResult {
- val roleCredentials = try {
- val accessToken = runBlocking(Dispatchers.IO) {
- ssoAccessTokenProvider.accessToken()
- }
-
- ssoClient.getRoleCredentials {
- it.accessToken(accessToken.accessToken)
- it.accountId(ssoAccount)
- it.roleName(ssoRole)
- }
- } catch (e: AccessDeniedException) {
- // OIDC access token was rejected, invalidate the cache and throw
- ssoAccessTokenProvider.invalidate()
- throw e
- }
-
- val awsCredentials = AwsSessionCredentials.create(
- roleCredentials.roleCredentials().accessKeyId(),
- roleCredentials.roleCredentials().secretAccessKey(),
- roleCredentials.roleCredentials().sessionToken()
- )
-
- val expirationTime = Instant.ofEpochMilli(roleCredentials.roleCredentials().expiration())
-
- val ssoCredentials =
- SsoCredentialsHolder(awsCredentials, expirationTime)
-
- return RefreshResult.builder(ssoCredentials)
- .staleTime(expirationTime.minus(Duration.ofMinutes(1)))
- .prefetchTime(expirationTime.minus(Duration.ofMinutes(5)))
- .build()
- }
-
- private data class SsoCredentialsHolder(val credentials: AwsSessionCredentials, val expirationTime: Instant)
-}
diff --git a/core/src/software/aws/toolkits/core/credentials/sso/SsoProfileProperty.kt b/core/src/software/aws/toolkits/core/credentials/sso/SsoProfileProperty.kt
deleted file mode 100644
index 655b8307e2..0000000000
--- a/core/src/software/aws/toolkits/core/credentials/sso/SsoProfileProperty.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-const val SSO_URL = "sso_start_url"
-const val SSO_REGION = "sso_region"
-const val SSO_ACCOUNT = "sso_account_id"
-const val SSO_ROLE_NAME = "sso_role_name"
-
-const val SSO_EXPERIMENTAL_REGISTRY_KEY = "aws.sso.enabled"
diff --git a/core/src/software/aws/toolkits/core/lambda/LambdaArchitecture.kt b/core/src/software/aws/toolkits/core/lambda/LambdaArchitecture.kt
new file mode 100644
index 0000000000..fb897aaa36
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/lambda/LambdaArchitecture.kt
@@ -0,0 +1,31 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.lambda
+
+import software.amazon.awssdk.services.lambda.model.Architecture
+
+enum class LambdaArchitecture(
+ private val architecture: Architecture,
+ val minSam: String? = null,
+) {
+ X86_64(Architecture.X86_64),
+ ARM64(Architecture.ARM64, minSam = "1.33.0");
+
+ override fun toString() = architecture.toString()
+
+ fun toSdkArchitecture() = architecture.validOrNull
+
+ companion object {
+ fun fromValue(value: String?): LambdaArchitecture? = if (value == null) {
+ null
+ } else {
+ values().find { it.toString() == value }
+ }
+
+ fun fromValue(value: Architecture): LambdaArchitecture? = values().find { it.architecture == value }
+
+ val DEFAULT = X86_64
+ val ARM_COMPATIBLE = listOf(X86_64, ARM64)
+ }
+}
diff --git a/core/src/software/aws/toolkits/core/lambda/LambdaRuntime.kt b/core/src/software/aws/toolkits/core/lambda/LambdaRuntime.kt
new file mode 100644
index 0000000000..3be8757e3b
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/lambda/LambdaRuntime.kt
@@ -0,0 +1,50 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.lambda
+
+import software.amazon.awssdk.services.lambda.model.Runtime
+import software.aws.toolkits.core.lambda.LambdaArchitecture.Companion.ARM_COMPATIBLE
+
+enum class LambdaRuntime(
+ private val runtime: Runtime?,
+ val minSamInit: String? = null,
+ val minSamDebugging: String? = null,
+ val architectures: List? = listOf(LambdaArchitecture.DEFAULT),
+ private val runtimeOverride: String? = null
+) {
+ GO1_X(
+ Runtime.GO1_X,
+ // Although go sam debugging was supported before 1.18.1, it does not work on 1.13.0-1.16.0
+ // and 1.17.0 broke the arguments
+ minSamDebugging = "1.18.1"
+ ),
+ NODEJS14_X(Runtime.NODEJS14_X, minSamDebugging = "1.17.0", minSamInit = "1.17.0", architectures = ARM_COMPATIBLE),
+ NODEJS16_X(Runtime.NODEJS16_X, minSamDebugging = "1.49.0", minSamInit = "1.49.0", architectures = ARM_COMPATIBLE),
+ NODEJS18_X(Runtime.NODEJS18_X, minSamDebugging = "1.65.0", minSamInit = "1.65.0", architectures = ARM_COMPATIBLE),
+ JAVA8(Runtime.JAVA8),
+ JAVA8_AL2(Runtime.JAVA8_AL2, minSamDebugging = "1.2.0", architectures = ARM_COMPATIBLE),
+ JAVA11(Runtime.JAVA11, architectures = ARM_COMPATIBLE),
+ JAVA17(Runtime.JAVA17, minSamDebugging = "1.81.0", minSamInit = "1.81.0", architectures = ARM_COMPATIBLE),
+ PYTHON3_7(Runtime.PYTHON3_7),
+ PYTHON3_8(Runtime.PYTHON3_8, architectures = ARM_COMPATIBLE),
+ PYTHON3_9(Runtime.PYTHON3_9, minSamDebugging = "1.28.0", minSamInit = "1.28.0", architectures = ARM_COMPATIBLE),
+ PYTHON3_10(Runtime.PYTHON3_10, minSamDebugging = "1.78.0", minSamInit = "1.78.0", architectures = ARM_COMPATIBLE),
+ PYTHON3_11(Runtime.PYTHON3_11, minSamDebugging = "1.87.0", minSamInit = "1.87.0", architectures = ARM_COMPATIBLE),
+ DOTNET5_0(null, minSamInit = "1.16.0", runtimeOverride = "dotnet5.0"),
+ DOTNET6_0(Runtime.DOTNET6, minSamDebugging = "1.40.1", minSamInit = "1.40.1", architectures = ARM_COMPATIBLE);
+
+ override fun toString() = runtime?.toString() ?: runtimeOverride ?: throw IllegalStateException("LambdaRuntime has no runtime or override string")
+
+ fun toSdkRuntime() = runtime.validOrNull
+
+ companion object {
+ fun fromValue(value: String?): LambdaRuntime? = if (value == null) {
+ null
+ } else {
+ values().find { it.toString() == value }
+ }
+
+ fun fromValue(value: Runtime): LambdaRuntime? = values().find { it.runtime == value }
+ }
+}
diff --git a/core/src/software/aws/toolkits/core/lambda/LambdaSampleEventProvider.kt b/core/src/software/aws/toolkits/core/lambda/LambdaSampleEventProvider.kt
index 11824bfa04..a12e9eeefc 100644
--- a/core/src/software/aws/toolkits/core/lambda/LambdaSampleEventProvider.kt
+++ b/core/src/software/aws/toolkits/core/lambda/LambdaSampleEventProvider.kt
@@ -4,15 +4,22 @@
package software.aws.toolkits.core.lambda
import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationFeature
+import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
+import org.slf4j.LoggerFactory
+import software.aws.toolkits.core.utils.RemoteResolveParser
import software.aws.toolkits.core.utils.RemoteResource
import software.aws.toolkits.core.utils.RemoteResourceResolver
import software.aws.toolkits.core.utils.inputStream
import software.aws.toolkits.core.utils.readText
+import software.aws.toolkits.core.utils.tryOrNull
+import java.io.InputStream
import java.time.Duration
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
@@ -28,9 +35,12 @@ class LambdaSampleEventProvider(private val resourceResolver: RemoteResourceReso
if (value != null) {
return CompletableFuture.completedFuture(value)
} else {
- return resourceResolver.resolve(LambdaSampleEventManifestResource).thenApply {
- val resolved = mapper.readValue(it.inputStream()).requests.map {
- LambdaSampleEvent(it.name) { resourceResolver.resolve(LambdaSampleEventResource(it.filename)).thenApply { it?.readText() } }
+ return resourceResolver.resolve(LambdaSampleEventManifestResource).thenApply { resource ->
+ val resolved = mapper.readValue(resource.inputStream()).requests.map { request ->
+ LambdaSampleEvent(request.name) {
+ resourceResolver.resolve(LambdaSampleEventResource(request.filename))
+ .thenApply { it?.readText() }
+ }
}
manifest.set(resolved)
resolved
@@ -44,25 +54,59 @@ open class LambdaSampleEvent(val name: String, private val contentProvider: () -
override fun toString() = name
}
-private data class LambdaSampleEventManifest(
+data class LambdaSampleEventManifest(
@JsonProperty(value = "request")
@JacksonXmlElementWrapper(useWrapping = false)
val requests: List
)
-private data class LambdaSampleEventRequest(
+data class LambdaSampleEventRequest(
val filename: String,
val name: String
)
internal val LambdaSampleEventManifestResource = LambdaSampleEventResource("manifest.xml")
+object LambdaManifestValidator : RemoteResolveParser {
+ private val LOG = LoggerFactory.getLogger(LambdaManifestValidator::class.java)
+ override fun canBeParsed(data: InputStream): Boolean {
+ val mapper = XmlMapper().registerKotlinModule().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ val result = LOG.tryOrNull("Failed to parse Requests") {
+ mapper.readValue(data)
+ }
+
+ return result?.requests?.isNotEmpty() ?: false
+ }
+}
internal data class LambdaSampleEventResource(val filename: String) : RemoteResource {
override val urls: List = listOf(
- "http://vstoolkit.amazonwebservices.com/LambdaSampleFunctions/SampleRequests/$filename",
- "https://s3.amazonaws.com/aws-vs-toolkit/LambdaSampleFunctions/SampleRequests/$filename"
+ "https://aws-vs-toolkit.s3.amazonaws.com/LambdaSampleFunctions/SampleRequests/$filename"
)
override val name: String = "lambda-sample-event-$filename"
override val ttl: Duration = Duration.ofDays(7)
+ override val remoteResolveParser: RemoteResolveParser? = resolveParserForGivenFile(filename.substringAfterLast('.', ""))
+}
+object LambdaSampleEventJsonValidator : RemoteResolveParser {
+ private val LOG = LoggerFactory.getLogger(LambdaSampleEventJsonValidator::class.java)
+
+ private val mapper = jacksonObjectMapper()
+ .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)
+ .disable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS)
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .enable(JsonParser.Feature.ALLOW_COMMENTS)
+
+ override fun canBeParsed(data: InputStream): Boolean {
+ val jsonMapper: Map = HashMap()
+ val result = LOG.tryOrNull("Failed to parse Lambda Sample Event request") {
+ this.mapper.readValue(data, jsonMapper.javaClass)
+ }
+ return result?.isNotEmpty() ?: false
+ }
}
+fun resolveParserForGivenFile(extension: String): RemoteResolveParser? =
+ when (extension) {
+ "xml" -> LambdaManifestValidator
+ "json" -> LambdaSampleEventJsonValidator
+ else -> null
+ }
diff --git a/core/src/software/aws/toolkits/core/lambda/LambdaUtils.kt b/core/src/software/aws/toolkits/core/lambda/LambdaUtils.kt
new file mode 100644
index 0000000000..4c7a0755bd
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/lambda/LambdaUtils.kt
@@ -0,0 +1,10 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.lambda
+
+import software.amazon.awssdk.services.lambda.model.Architecture
+import software.amazon.awssdk.services.lambda.model.Runtime
+
+val Runtime?.validOrNull: Runtime? get() = this?.takeUnless { it == Runtime.UNKNOWN_TO_SDK_VERSION }
+val Architecture?.validOrNull: Architecture? get() = this?.takeUnless { it == Architecture.UNKNOWN_TO_SDK_VERSION }
diff --git a/core/src/software/aws/toolkits/core/region/AwsRegion.kt b/core/src/software/aws/toolkits/core/region/AwsRegion.kt
index 1d51ab4ced..61df7aac45 100644
--- a/core/src/software/aws/toolkits/core/region/AwsRegion.kt
+++ b/core/src/software/aws/toolkits/core/region/AwsRegion.kt
@@ -35,3 +35,18 @@ data class AwsRegion(val id: String, val name: String, val partitionId: String)
private fun String.trimPrefixAndRemoveBrackets(prefix: String) = this.removePrefix(prefix).replace("(", "").replace(")", "").trim()
}
}
+
+fun AwsRegion.mergeWithExistingEnvironmentVariables(existing: MutableMap, replace: Boolean = false) {
+ mergeWithExistingEnvironmentVariables(existing.keys, existing::putAll, replace)
+}
+
+fun AwsRegion.mergeWithExistingEnvironmentVariables(
+ existingKeys: Collection,
+ putValues: (Map) -> Unit,
+ replace: Boolean = false
+) {
+ val regionEnvs = this.toEnvironmentVariables()
+ if (replace || regionEnvs.keys.none { it in existingKeys }) {
+ putValues(regionEnvs)
+ }
+}
diff --git a/core/src/software/aws/toolkits/core/region/Partitions.kt b/core/src/software/aws/toolkits/core/region/Partitions.kt
index bbb2b0e88d..3dc885dc10 100644
--- a/core/src/software/aws/toolkits/core/region/Partitions.kt
+++ b/core/src/software/aws/toolkits/core/region/Partitions.kt
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.slf4j.LoggerFactory
+import software.aws.toolkits.core.utils.RemoteResolveParser
import software.aws.toolkits.core.utils.RemoteResource
import software.aws.toolkits.core.utils.tryOrNull
import software.aws.toolkits.resources.BundledResources
@@ -44,13 +45,18 @@ object PartitionParser {
.enable(JsonParser.Feature.ALLOW_COMMENTS)
fun parse(inputStream: InputStream): Partitions? = LOG.tryOrNull("Failed to parse Partitions") {
- mapper.readValue(inputStream, Partitions::class.java)
+ mapper.readValue(inputStream, Partitions::class.java)
+ }
+}
+object EndpointsJsonValidator : RemoteResolveParser {
+ override fun canBeParsed(data: InputStream): Boolean {
+ return PartitionParser.parse(data)?.partitions?.isNotEmpty() ?: return false
}
}
-
object ServiceEndpointResource : RemoteResource {
override val urls: List = listOf("https://idetoolkits.amazonwebservices.com/endpoints.json")
override val name: String = "service-endpoints.json"
override val ttl: Duration? = Duration.ofHours(24)
override val initialValue: (() -> InputStream)? = { BundledResources.ENDPOINTS_FILE }
+ override val remoteResolveParser: EndpointsJsonValidator = EndpointsJsonValidator
}
diff --git a/core/src/software/aws/toolkits/core/region/ToolkitRegionProvider.kt b/core/src/software/aws/toolkits/core/region/ToolkitRegionProvider.kt
index 2c9ad4c126..4b1bebf72e 100644
--- a/core/src/software/aws/toolkits/core/region/ToolkitRegionProvider.kt
+++ b/core/src/software/aws/toolkits/core/region/ToolkitRegionProvider.kt
@@ -9,7 +9,11 @@ import java.util.concurrent.ConcurrentHashMap
* An SPI to provide regions supported by this toolkit
*/
abstract class ToolkitRegionProvider {
- protected data class PartitionData(val description: String, val services: Map, val regions: Map)
+ protected data class PartitionData(
+ val description: String,
+ val services: Map,
+ val regions: Map
+ )
protected abstract fun partitionData(): Map
@@ -20,6 +24,11 @@ abstract class ToolkitRegionProvider {
*/
fun allRegions(): Map = partitionData().flatMap { it.value.regions.asIterable() }.associate { it.key to it.value }
+ /**
+ * Returns a map of region ID([AwsRegion.id]) to [AwsRegion], filtering by if the service is supported
+ */
+ fun allRegionsForService(serviceId: String): Map = allRegions().filter { isServiceSupported(it.value, serviceId) }
+
/**
* Returns a map of region ID([AwsRegion.id]) to [AwsRegion] for the specified partition
*/
@@ -46,8 +55,7 @@ abstract class ToolkitRegionProvider {
open fun isServiceGlobal(region: AwsRegion, serviceId: String): Boolean {
val partition = partitionData()[region.partitionId] ?: throw IllegalStateException("Partition data is missing for ${region.partitionId}")
- val service = partition.services[serviceId] ?: throw IllegalStateException("Unknown service $serviceId in ${region.partitionId}")
- return service.isGlobal
+ return partition.services[serviceId]?.isGlobal == true
}
fun getGlobalRegionForService(region: AwsRegion, serviceId: String): AwsRegion {
diff --git a/core/src/software/aws/toolkits/core/s3/BucketUtils.kt b/core/src/software/aws/toolkits/core/s3/BucketUtils.kt
index f2ffedf1e0..ce4c27a03a 100644
--- a/core/src/software/aws/toolkits/core/s3/BucketUtils.kt
+++ b/core/src/software/aws/toolkits/core/s3/BucketUtils.kt
@@ -3,10 +3,10 @@
package software.aws.toolkits.core.s3
+import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest
import software.amazon.awssdk.services.s3.model.ObjectIdentifier
-import software.amazon.awssdk.services.s3.model.S3Exception
fun S3Client.deleteBucketAndContents(bucket: String) {
this.listObjectVersionsPaginator(ListObjectVersionsRequest.builder().bucket(bucket).build()).forEach { resp ->
@@ -18,18 +18,14 @@ fun S3Client.deleteBucketAndContents(bucket: String) {
if (versions.isEmpty()) {
return@forEach
}
- this.deleteObjects { it.bucket(bucket).delete { it.objects(versions) } }
+ this.deleteObjects { it.bucket(bucket).delete { obj -> obj.objects(versions) } }
}
this.deleteBucket { it.bucket(bucket) }
}
-fun S3Client.regionForBucket(bucketName: String): String? = try {
- this.headBucket { it.bucket(bucketName) }
- .sdkHttpResponse()
- .headers()[BUCKET_REGION_HEADER]?.first() ?: throw IllegalStateException("Failed to get bucket header")
-} catch (e: S3Exception) {
- e.awsErrorDetails().sdkHttpResponse().firstMatchingHeader(BUCKET_REGION_HEADER).orElseGet { null } ?: throw e
-}
-
-private const val BUCKET_REGION_HEADER = "x-amz-bucket-region"
+fun S3Client.regionForBucket(bucketName: String): String = this.getBucketLocation {
+ it.bucket(bucketName)
+}.locationConstraintAsString()
+ .takeIf { it.isNotEmpty() } // getBucketLocation returns an explicit empty string location contraint for us-east-1
+ ?: Region.US_EAST_1.id()
diff --git a/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt b/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt
index 2f85fa51f9..d92ce37601 100644
--- a/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt
+++ b/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt
@@ -31,6 +31,7 @@ interface MetricEvent {
val name: String
val value: Double
val unit: MetricUnit
+ val passive: Boolean
val metadata: Map
interface Builder {
@@ -40,6 +41,8 @@ interface MetricEvent {
fun unit(unit: MetricUnit): Builder
+ fun passive(value: Boolean): Builder
+
fun metadata(key: String, value: String): Builder
fun metadata(key: String, value: Boolean): Builder = metadata(key, value.toString())
@@ -61,7 +64,7 @@ interface MetricEvent {
fun String.replaceIllegal(replacement: String = "") = this.replace(illegalCharsRegex, replacement)
-class DefaultMetricEvent internal constructor(
+data class DefaultMetricEvent internal constructor(
override val createTime: Instant,
override val awsAccount: String,
override val awsRegion: String,
@@ -72,7 +75,7 @@ class DefaultMetricEvent internal constructor(
private var createTime: Instant = Instant.now()
private var awsAccount: String = METADATA_NA
private var awsRegion: String = METADATA_NA
- private var data: MutableCollection = mutableListOf()
+ private val data: MutableCollection = mutableListOf()
override fun createTime(createTime: Instant): MetricEvent.Builder {
this.createTime = createTime
@@ -105,19 +108,19 @@ class DefaultMetricEvent internal constructor(
const val METADATA_NA = "n/a"
const val METADATA_NOT_SET = "not-set"
const val METADATA_INVALID = "invalid"
-
- private val LOG = getLogger()
}
- class DefaultDatum(
+ data class DefaultDatum(
override val name: String,
override val value: Double,
override val unit: MetricUnit,
+ override val passive: Boolean,
override val metadata: Map
) : MetricEvent.Datum {
class BuilderImpl(private var name: String) : MetricEvent.Datum.Builder {
private var value: Double = 0.0
private var unit: MetricUnit = MetricUnit.NONE
+ private var passive: Boolean = false
private val metadata: MutableMap = HashMap()
override fun name(name: String): MetricEvent.Datum.Builder {
@@ -135,17 +138,17 @@ class DefaultMetricEvent internal constructor(
return this
}
+ override fun passive(value: Boolean): MetricEvent.Datum.Builder {
+ this.passive = value
+ return this
+ }
+
override fun metadata(key: String, value: String): MetricEvent.Datum.Builder {
if (metadata.containsKey(key)) {
LOG.warn { "Attempted to add multiple pieces of metadata with the same key" }
return this
}
- if (metadata.size > MAX_METADATA_ENTRIES) {
- LOG.warn { "Each metric datum may contain a maximum of $MAX_METADATA_ENTRIES metadata entries" }
- return this
- }
-
metadata[key] = value
return this
}
@@ -154,6 +157,7 @@ class DefaultMetricEvent internal constructor(
name.replaceIllegal(),
this.value,
this.unit,
+ this.passive,
this.metadata
)
}
@@ -162,8 +166,6 @@ class DefaultMetricEvent internal constructor(
private val LOG = getLogger()
fun builder(name: String): MetricEvent.Datum.Builder = BuilderImpl(name)
-
- const val MAX_METADATA_ENTRIES: Int = 10
}
}
}
diff --git a/core/src/software/aws/toolkits/core/telemetry/TelemetryPublisher.kt b/core/src/software/aws/toolkits/core/telemetry/TelemetryPublisher.kt
index 5c2004a6ad..eafc77bb28 100644
--- a/core/src/software/aws/toolkits/core/telemetry/TelemetryPublisher.kt
+++ b/core/src/software/aws/toolkits/core/telemetry/TelemetryPublisher.kt
@@ -5,8 +5,8 @@ package software.aws.toolkits.core.telemetry
import software.amazon.awssdk.services.toolkittelemetry.model.Sentiment
-interface TelemetryPublisher {
+interface TelemetryPublisher : AutoCloseable {
suspend fun publish(metricEvents: Collection)
- suspend fun sendFeedback(sentiment: Sentiment, comment: String)
+ suspend fun sendFeedback(sentiment: Sentiment, comment: String, metadata: Map)
}
diff --git a/core/src/software/aws/toolkits/core/utils/CollectionUtils.kt b/core/src/software/aws/toolkits/core/utils/CollectionUtils.kt
new file mode 100644
index 0000000000..1bb1bbb17f
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/utils/CollectionUtils.kt
@@ -0,0 +1,20 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.utils
+
+/**
+ * Removes all items in this collection and replaces them with the items in the [other] collection
+ */
+fun MutableCollection.replace(other: Collection) {
+ clear()
+ addAll(other)
+}
+
+/**
+ * Removes all items in this map and replaces them with the items in the [other] map
+ */
+fun MutableMap.replace(other: Map) {
+ clear()
+ putAll(other)
+}
diff --git a/core/src/software/aws/toolkits/core/utils/ConcurrencyUtils.kt b/core/src/software/aws/toolkits/core/utils/ConcurrencyUtils.kt
deleted file mode 100644
index e0544cbbdb..0000000000
--- a/core/src/software/aws/toolkits/core/utils/ConcurrencyUtils.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.utils
-
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.CompletionStage
-import java.util.concurrent.Executors
-import java.util.concurrent.RunnableFuture
-import java.util.function.Supplier
-
-fun failedFuture(t: Throwable): CompletableFuture = CompletableFuture().also {
- it.completeExceptionally(t)
-}
-
-private val pool = Executors.newCachedThreadPool()
-fun RunnableFuture.toCompletableFuture(): CompletableFuture {
- run()
-
- return CompletableFuture.supplyAsync(Supplier { get() }, pool)
-}
-
-fun Iterable>.allOf(): CompletionStage = CompletableFuture.allOf(*this.map { it.toCompletableFuture() }.toTypedArray())
diff --git a/core/src/software/aws/toolkits/core/utils/LogUtils.kt b/core/src/software/aws/toolkits/core/utils/LogUtils.kt
index ace48dd926..370150496c 100644
--- a/core/src/software/aws/toolkits/core/utils/LogUtils.kt
+++ b/core/src/software/aws/toolkits/core/utils/LogUtils.kt
@@ -1,6 +1,7 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
+@file:Suppress("LazyLog")
package software.aws.toolkits.core.utils
import org.slf4j.Logger
diff --git a/core/src/software/aws/toolkits/core/utils/PathUtils.kt b/core/src/software/aws/toolkits/core/utils/PathUtils.kt
index 06ec32273f..9e209a1a32 100644
--- a/core/src/software/aws/toolkits/core/utils/PathUtils.kt
+++ b/core/src/software/aws/toolkits/core/utils/PathUtils.kt
@@ -3,15 +3,26 @@
package software.aws.toolkits.core.utils
+import org.slf4j.Logger
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.Charset
+import java.nio.file.AccessMode
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
+import java.nio.file.attribute.AclEntry
+import java.nio.file.attribute.AclFileAttributeView
import java.nio.file.attribute.FileTime
import java.nio.file.attribute.PosixFilePermission
+import java.nio.file.attribute.PosixFilePermissions
+import java.nio.file.attribute.UserPrincipal
+import kotlin.io.path.getPosixFilePermissions
+import kotlin.io.path.isRegularFile
+
+val POSIX_OWNER_ONLY_FILE = setOf(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)
+val POSIX_OWNER_ONLY_DIR = setOf(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE)
fun Path.inputStream(): InputStream = Files.newInputStream(this)
fun Path.inputStreamIfExists(): InputStream? = try {
@@ -20,26 +31,184 @@ fun Path.inputStreamIfExists(): InputStream? = try {
null
}
-fun Path.touch() {
- this.createParentDirectories()
+fun Path.touch(restrictToOwner: Boolean = false) {
try {
- Files.createFile(this)
- } catch (_: FileAlreadyExistsException) { }
+ if (!restrictToOwner || !hasPosixFilePermissions()) {
+ Files.createFile(this)
+ } else {
+ Files.createFile(this, PosixFilePermissions.asFileAttribute(POSIX_OWNER_ONLY_FILE))
+ }
+ } catch (_: FileAlreadyExistsException) {}
}
fun Path.outputStream(): OutputStream {
this.createParentDirectories()
return Files.newOutputStream(this)
}
-fun Path.createParentDirectories() = Files.createDirectories(this.parent)
+
+fun Path.createParentDirectories(restrictToOwner: Boolean = false) = if (!restrictToOwner || !hasPosixFilePermissions()) {
+ Files.createDirectories(this.parent)
+} else {
+ Files.createDirectories(this.parent, PosixFilePermissions.asFileAttribute(POSIX_OWNER_ONLY_DIR))
+}
+
fun Path.exists() = Files.exists(this)
fun Path.deleteIfExists() = Files.deleteIfExists(this)
fun Path.lastModified(): FileTime = Files.getLastModifiedTime(this)
fun Path.readText(charset: Charset = Charsets.UTF_8) = toFile().readText(charset)
fun Path.writeText(text: String, charset: Charset = Charsets.UTF_8) = toFile().writeText(text, charset)
+fun Path.appendText(text: String, charset: Charset = Charsets.UTF_8) = toFile().appendText(text, charset)
+
+// Comes from PosixFileAttributeView#name()
+fun Path.hasPosixFilePermissions() = "posix" in this.fileSystem.supportedFileAttributeViews()
fun Path.filePermissions(permissions: Set) {
- // Comes from PosixFileAttributeView#name()
- if ("posix" in this.fileSystem.supportedFileAttributeViews()) {
+ if (hasPosixFilePermissions()) {
Files.setPosixFilePermissions(this, permissions)
}
}
+
+fun Path.tryDirOp(log: Logger, block: Path.() -> Unit) {
+ try {
+ log.debug { "dir op on $this" }
+ block(this)
+ } catch (e: Exception) {
+ if (e !is java.nio.file.AccessDeniedException && e !is kotlin.io.AccessDeniedException) {
+ throw e
+ }
+
+ if (!hasPosixFilePermissions()) {
+ throw tryAugmentExceptionMessage(e, this)
+ }
+
+ log.info(e) { "Attempting to handle ADE for directory operation" }
+ try {
+ var parent = if (this.isRegularFile()) { parent } else { this }
+
+ while (parent != null) {
+ if (!parent.exists()) {
+ log.info { "${parent.toAbsolutePath()}: does not exist yet" }
+ } else {
+ if (tryOrNull { parent.fileSystem.provider().checkAccess(parent, AccessMode.READ, AccessMode.WRITE, AccessMode.EXECUTE) } != null) {
+ log.debug { "$parent has rwx, exiting" }
+ // can assume parent permissions are correct
+ break
+ }
+
+ log.debug { "fixing perms for $parent" }
+ parent.tryFixPerms(log, POSIX_OWNER_ONLY_DIR)
+ }
+
+ parent = parent.parent
+ }
+ } catch (e2: Exception) {
+ log.warn(e2) { "Encountered error while handling ADE for ${e.message}" }
+
+ throw tryAugmentExceptionMessage(e, this)
+ }
+
+ log.info { "Done attempting to handle ADE for directory operation" }
+ block(this)
+ }
+}
+
+fun Path.tryFileOp(log: Logger, block: Path.() -> T) =
+ try {
+ log.debug { "file op on $this" }
+ block(this)
+ } catch (e: Exception) {
+ if (e !is java.nio.file.AccessDeniedException && e !is kotlin.io.AccessDeniedException) {
+ throw e
+ }
+
+ if (!hasPosixFilePermissions()) {
+ throw tryAugmentExceptionMessage(e, this)
+ }
+
+ log.info(e) { "Attempting to handle ADE for file operation" }
+ try {
+ log.debug { "fixing perms for $this" }
+ tryFixPerms(log, POSIX_OWNER_ONLY_FILE)
+ } catch (e2: Exception) {
+ log.warn(e2) { "Encountered error while handling ADE for ${e.message}" }
+
+ throw tryAugmentExceptionMessage(e, this)
+ }
+
+ log.info { "Done attempting to handle ADE for file operation" }
+ block(this)
+ }
+
+private fun Path.tryFixPerms(log: Logger, desiredPermissions: Set) {
+ // TODO: consider handling linux ACLs
+ // only try ops if we own the file
+ // (ab)use invariant that chmod only works if you are root or the file owner
+ val perms = tryOrLogShortException(log) { Files.getPosixFilePermissions(this) }
+ val ownership = tryOrLogShortException(log) { Files.getOwner(this) }
+
+ log.info { "Permissions for ${toAbsolutePath()}: $ownership, $perms" }
+ if (perms != null && ownership != null) {
+ if (ownership.name != "root" && tryOrNull { filePermissions(perms) } != null) {
+ val permissions = perms + desiredPermissions
+ log.info { "Setting perms for ${toAbsolutePath()}: $permissions" }
+ filePermissions(permissions)
+ }
+ }
+}
+
+private fun tryAugmentExceptionMessage(e: Exception, path: Path): Exception {
+ if (e !is java.nio.file.AccessDeniedException && e !is kotlin.io.AccessDeniedException) {
+ return e
+ }
+
+ var potentialProblem = if (path.exists()) { path } else { path.parent }
+ var acls: List? = null
+ var ownership: UserPrincipal? = null
+ while (potentialProblem != null) {
+ acls = tryOrNull { Files.getFileAttributeView(potentialProblem, AclFileAttributeView::class.java).acl }
+ ownership = tryOrNull { Files.getOwner(potentialProblem) }
+
+ if (acls != null || ownership != null) {
+ break
+ }
+
+ potentialProblem = potentialProblem.parent
+ }
+
+ val message = buildString {
+ // $path is automatically added to the front of the exception message
+ appendLine("Exception trying to perform operation")
+
+ if (potentialProblem != null) {
+ append("Potential issue is with $potentialProblem")
+
+ if (ownership != null) {
+ append(", which has owner: $ownership")
+ }
+
+ if (acls != null) {
+ append(", and ACL entries for: ${acls.map { it.principal() }}")
+ }
+
+ val posixPermissions = tryOrNull { PosixFilePermissions.toString(potentialProblem.getPosixFilePermissions()) }
+ if (posixPermissions != null) {
+ append(", and POSIX permissions: $posixPermissions")
+ }
+ }
+ }
+
+ return when (e) {
+ is kotlin.io.AccessDeniedException -> kotlin.io.AccessDeniedException(e.file, e.other, message)
+ is java.nio.file.AccessDeniedException -> java.nio.file.AccessDeniedException(e.file, e.otherFile, message)
+ // should never happen
+ else -> e
+ }.also {
+ it.stackTrace = e.stackTrace
+ }
+}
+
+private fun tryOrLogShortException(log: Logger, block: () -> T) = try {
+ block()
+} catch (e: Exception) {
+ log.warn { "${e::class.simpleName}: ${e.message}" }
+ null
+}
diff --git a/core/src/software/aws/toolkits/core/utils/RemoteResourceResolver.kt b/core/src/software/aws/toolkits/core/utils/RemoteResourceResolver.kt
index dec31059fc..fc0d590ea9 100644
--- a/core/src/software/aws/toolkits/core/utils/RemoteResourceResolver.kt
+++ b/core/src/software/aws/toolkits/core/utils/RemoteResourceResolver.kt
@@ -3,6 +3,7 @@
package software.aws.toolkits.core.utils
+import java.io.FileInputStream
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
@@ -16,6 +17,9 @@ import java.util.concurrent.CompletionStage
interface RemoteResourceResolver {
fun resolve(resource: RemoteResource): CompletionStage
}
+interface RemoteResolveParser {
+ fun canBeParsed(data: InputStream): Boolean
+}
class DefaultRemoteResourceResolver(
private val urlFetcher: UrlFetcher,
@@ -61,7 +65,12 @@ class DefaultRemoteResourceResolver(
when {
downloaded != null -> {
LOG.debug { "Downloaded new file $downloaded, replacing old file $expectedLocation" }
- Files.move(downloaded, expectedLocation, StandardCopyOption.REPLACE_EXISTING)
+ val isParsingSuccess = FileInputStream(downloaded.toFile()).use {
+ resource.remoteResolveParser?.canBeParsed(it) ?: true
+ }
+ if (isParsingSuccess) {
+ Files.move(downloaded, expectedLocation, StandardCopyOption.REPLACE_EXISTING)
+ }
}
current != null -> LOG.debug { "No new file available - re-using current file $current" }
initialValue != null -> {
@@ -105,4 +114,5 @@ interface RemoteResource {
val name: String
val ttl: Duration? get() = null
val initialValue: (() -> InputStream)? get() = null
+ val remoteResolveParser: RemoteResolveParser? get() = null
}
diff --git a/core/src/software/aws/toolkits/core/utils/SensitiveField.kt b/core/src/software/aws/toolkits/core/utils/SensitiveField.kt
new file mode 100644
index 0000000000..f1d2d37c2b
--- /dev/null
+++ b/core/src/software/aws/toolkits/core/utils/SensitiveField.kt
@@ -0,0 +1,43 @@
+// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.utils
+
+import kotlin.reflect.full.hasAnnotation
+import kotlin.reflect.full.memberProperties
+
+@Target(AnnotationTarget.PROPERTY)
+annotation class SensitiveField
+
+fun redactedString(o: Any): String {
+ val clazz = o::class
+ if (!clazz.isData) {
+ error("Only supports redacting data classes")
+ }
+
+ return buildString {
+ append(clazz.simpleName)
+ append("(")
+
+ val properties = o::class.memberProperties
+ properties.forEachIndexed { i, prop ->
+ append(prop.name)
+ append("=")
+ if (prop.hasAnnotation()) {
+ if (prop.getter.call(o) == null) {
+ append("null")
+ } else {
+ append("")
+ }
+ } else {
+ append(prop.getter.call(o))
+ }
+
+ if (i != properties.size - 1) {
+ append(", ")
+ }
+ }
+
+ append(")")
+ }
+}
diff --git a/core/src/software/aws/toolkits/core/utils/StringUtils.kt b/core/src/software/aws/toolkits/core/utils/StringUtils.kt
index 38426ff068..a41a2eee2d 100644
--- a/core/src/software/aws/toolkits/core/utils/StringUtils.kt
+++ b/core/src/software/aws/toolkits/core/utils/StringUtils.kt
@@ -10,3 +10,5 @@ fun String.splitNoBlank(vararg delimiters: Char, ignoreCase: Boolean = false, li
split(*delimiters, ignoreCase = ignoreCase, limit = limit).filter { it.isNotBlank() }
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
+
+fun String.htmlWrap() = """${this.replace("\n", "
")}"""
diff --git a/core/src/software/aws/toolkits/core/utils/Waiter.kt b/core/src/software/aws/toolkits/core/utils/Waiter.kt
index 1fdfc72d17..ec03fc7be5 100644
--- a/core/src/software/aws/toolkits/core/utils/Waiter.kt
+++ b/core/src/software/aws/toolkits/core/utils/Waiter.kt
@@ -4,7 +4,6 @@
package software.aws.toolkits.core.utils
import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
import software.amazon.awssdk.awscore.exception.AwsServiceException
import java.time.Duration
import java.time.Instant
@@ -15,29 +14,6 @@ import kotlin.reflect.KClass
object Waiters {
private val LOG = getLogger()
- /**
- * Creates a waiter that attempts executing the provided [call] until the specified conditions are met.
- *
- * @param T The response type of the [call]
- * @param succeedOn The condition on the response under which the thing we are trying is complete. Defaults to if the call succeeds, we stop wating
- * @param failOn The condition on the response under which the thing we are trying has already failed and further attempts are pointless. Defaults to always try again
- * @param exceptionsToStopOn The exception types that should be considered a success and stop waiting. Default to never stop on any exception
- * @param exceptionsToIgnore The exception types that should be ignored if the thing we are trying throws them. Default to not ignoring any exceptions and let it bubble out
- * @param maxDuration The max amount of time we want to wait for
- * @param call The function we want to keep retrying
- */
- fun waitUntilBlocking(
- succeedOn: (T) -> Boolean = { true },
- failOn: (T) -> Boolean = { false },
- exceptionsToStopOn: Set> = emptySet(),
- exceptionsToIgnore: Set> = emptySet(),
- maxDuration: Duration = Duration.ofMinutes(1),
- // The status pulling method to get the latest resource
- call: () -> T
- ): T? = runBlocking {
- waitUntil(succeedOn, failOn, exceptionsToStopOn, exceptionsToIgnore, maxDuration, call)
- }
-
/**
* Creates a waiter that attempts executing the provided [call] until the specified conditions are met.
*
diff --git a/core/src/software/aws/toolkits/core/utils/ZipUtils.kt b/core/src/software/aws/toolkits/core/utils/ZipUtils.kt
index ff3d3c7cb5..01d184c4ed 100644
--- a/core/src/software/aws/toolkits/core/utils/ZipUtils.kt
+++ b/core/src/software/aws/toolkits/core/utils/ZipUtils.kt
@@ -7,7 +7,6 @@ import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
/**
@@ -41,10 +40,3 @@ fun createTemporaryZipFile(block: (ZipOutputStream) -> Unit): Path {
ZipOutputStream(Files.newOutputStream(file)).use(block)
return file
}
-
-/**
- * Returns a list of the file names in the Zip archive
- */
-fun zipEntries(zipFile: Path): List = ZipFile(zipFile.toFile()).use { zip ->
- zip.entries().asSequence().filterNot { it.isDirectory }.mapNotNull { it.name }.toList()
-}
diff --git a/core/tst-resources/jsonSampleFailure.json b/core/tst-resources/jsonSampleFailure.json
new file mode 100644
index 0000000000..2bbf3fd3c1
--- /dev/null
+++ b/core/tst-resources/jsonSampleFailure.json
@@ -0,0 +1,20 @@
+{
+ "partitions" : [ {
+ "defaults" : {
+ "hostname" : "{service}.{region}.{dnsSuffix}",
+ "protocols" : [ "https" ],
+ "signatureVersions" : [ "v4" ],
+ "variants" : [ {
+ "dnsSuffix" : "amazonaws.com",
+ "hostname" : "{service}-fips.{region}.{dnsSuffix}",
+ "tags" : [ "fips" ]
+ }, {
+ "dnsSuffix" : "api.aws",
+ "hostname" : "{service}-fips.{region}.{dnsSuffix}",
+ "tags" : [ "dualstack", "fips" ]
+ }, {
+ "dnsSuffix" : "api.aws",
+ "hostname" : "{service}.{region}.{dnsSuffix}",
+ "tags" : [ "dualstack" ]
+ } ]
+ },
diff --git a/core/tst-resources/jsonSampleSuccess.json b/core/tst-resources/jsonSampleSuccess.json
new file mode 100644
index 0000000000..2287c5a778
--- /dev/null
+++ b/core/tst-resources/jsonSampleSuccess.json
@@ -0,0 +1,63 @@
+{
+ "partitions" : [ {
+ "defaults" : {
+ "hostname" : "{service}.{region}.{dnsSuffix}",
+ "protocols" : [ "https" ],
+ "signatureVersions" : [ "v4" ],
+ "variants" : [ {
+ "dnsSuffix" : "amazonaws.com",
+ "hostname" : "{service}-fips.{region}.{dnsSuffix}",
+ "tags" : [ "fips" ]
+ }, {
+ "dnsSuffix" : "api.aws",
+ "hostname" : "{service}-fips.{region}.{dnsSuffix}",
+ "tags" : [ "dualstack", "fips" ]
+ }, {
+ "dnsSuffix" : "api.aws",
+ "hostname" : "{service}.{region}.{dnsSuffix}",
+ "tags" : [ "dualstack" ]
+ } ]
+ },
+ "dnsSuffix" : "amazonaws.com",
+ "partition" : "aws",
+ "partitionName" : "AWS Standard",
+ "regionRegex" : "^(us|eu|ap|sa|ca|me|af)\\-\\w+\\-\\d+$",
+ "regions" : {
+ "af-south-1" : {
+ "description" : "Africa (Cape Town)"
+ },
+ "ap-east-1" : {
+ "description" : "Asia Pacific (Hong Kong)"
+ }
+ },
+ "services" : {
+ "a4b" : {
+ "endpoints" : {
+ "us-east-1" : { }
+ }
+ },
+ "access-analyzer" : {
+ "endpoints" : {
+ "af-south-1" : { },
+ "ap-east-1" : { },
+ "ca-central-1" : {
+ "variants" : [ {
+ "hostname" : "access-analyzer-fips.ca-central-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ },
+ "eu-west-2" : { },
+ "eu-west-3" : { },
+ "fips-ca-central-1" : {
+ "credentialScope" : {
+ "region" : "ca-central-1"
+ },
+ "deprecated" : true,
+ "hostname" : "access-analyzer-fips.ca-central-1.amazonaws.com"
+ }
+ }
+ }
+ }
+ } ],
+ "version" : 3
+}
diff --git a/core/tst-resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/tst-resources/mockito-extensions/org.mockito.plugins.MockMaker
deleted file mode 100644
index 1f0955d450..0000000000
--- a/core/tst-resources/mockito-extensions/org.mockito.plugins.MockMaker
+++ /dev/null
@@ -1 +0,0 @@
-mock-maker-inline
diff --git a/core/tst-resources/sampleLambdaEvent.json b/core/tst-resources/sampleLambdaEvent.json
new file mode 100644
index 0000000000..13121b6d8c
--- /dev/null
+++ b/core/tst-resources/sampleLambdaEvent.json
@@ -0,0 +1,4 @@
+{
+ "key1": "value1",
+ "key2": "value2"
+}
diff --git a/core/tst-resources/xmlSampleFailure.xml b/core/tst-resources/xmlSampleFailure.xml
new file mode 100644
index 0000000000..285ca3e577
--- /dev/null
+++ b/core/tst-resources/xmlSampleFailure.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Alexa End Session
+ alexa-end-session.json
+
+
+ Alexa Intent - Answer
+ alexa-intent-answer.json
+
+
diff --git a/core/tst-resources/xmlSampleSuccess.xml b/core/tst-resources/xmlSampleSuccess.xml
new file mode 100644
index 0000000000..7d3c143d94
--- /dev/null
+++ b/core/tst-resources/xmlSampleSuccess.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Alexa End Session
+ alexa-end-session.json
+
+
+ Alexa Intent - Answer
+ alexa-intent-answer.json
+
+
diff --git a/core/tst/software/aws/toolkits/core/credentials/AwsCredentialsExtensionsTest.kt b/core/tst/software/aws/toolkits/core/credentials/AwsCredentialsExtensionsTest.kt
new file mode 100644
index 0000000000..658795da8a
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/credentials/AwsCredentialsExtensionsTest.kt
@@ -0,0 +1,64 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.credentials
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
+import software.amazon.awssdk.auth.credentials.AwsSessionCredentials
+import software.aws.toolkits.core.utils.test.aString
+
+class AwsCredentialsExtensionsTest {
+
+ @Test
+ fun `can convert basic credentials to environment variables`() {
+ val credentials = AwsBasicCredentials.create(aString(), aString())
+ assertThat(credentials.toEnvironmentVariables()).hasSize(2)
+ .containsEntry("AWS_ACCESS_KEY_ID", credentials.accessKeyId())
+ .containsEntry("AWS_SECRET_ACCESS_KEY", credentials.secretAccessKey())
+ }
+
+ @Test
+ fun `can convert session credentials to environment variables`() {
+ val credentials = AwsSessionCredentials.create(aString(), aString(), aString())
+ assertThat(credentials.toEnvironmentVariables()).hasSize(3)
+ .containsEntry("AWS_ACCESS_KEY_ID", credentials.accessKeyId())
+ .containsEntry("AWS_SECRET_ACCESS_KEY", credentials.secretAccessKey())
+ .containsEntry("AWS_SESSION_TOKEN", credentials.sessionToken())
+ }
+
+ @Test
+ fun `can add environment variables to an existing env map`() {
+ val credentials = AwsSessionCredentials.create(aString(), aString(), aString())
+ val env = mutableMapOf()
+
+ credentials.mergeWithExistingEnvironmentVariables(env)
+
+ assertThat(env).containsOnlyKeys("AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN")
+ }
+
+ @Test
+ fun `existing credentials are not replaced by default`() {
+ val credentials = AwsBasicCredentials.create(aString(), aString())
+ val existingToken = aString()
+ val env = mutableMapOf("AWS_SESSION_TOKEN" to existingToken)
+
+ credentials.mergeWithExistingEnvironmentVariables(env)
+
+ assertThat(env).hasSize(1).containsEntry("AWS_SESSION_TOKEN", existingToken)
+ }
+
+ @Test
+ fun `existing credentials can be replaced`() {
+ val credentials = AwsBasicCredentials.create(aString(), aString())
+ val existingToken = aString()
+ val env = mutableMapOf("AWS_SESSION_TOKEN" to existingToken)
+
+ credentials.mergeWithExistingEnvironmentVariables(env, replace = true)
+
+ assertThat(env).hasSize(2)
+ .containsEntry("AWS_ACCESS_KEY_ID", credentials.accessKeyId())
+ .containsEntry("AWS_SECRET_ACCESS_KEY", credentials.secretAccessKey())
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/credentials/Mocks.kt b/core/tst/software/aws/toolkits/core/credentials/Mocks.kt
index a5860bc8b5..2556fe9898 100644
--- a/core/tst/software/aws/toolkits/core/credentials/Mocks.kt
+++ b/core/tst/software/aws/toolkits/core/credentials/Mocks.kt
@@ -3,7 +3,7 @@
package software.aws.toolkits.core.credentials
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
import software.aws.toolkits.core.utils.test.aString
@@ -17,7 +17,7 @@ fun aCredentialsIdentifier(
displayName: String = aString(),
factoryId: String = aString(),
defaultRegionId: String? = null
-) = object : CredentialIdentifierBase() {
+) = object : CredentialIdentifierBase(CredentialType.StaticProfile) {
override val id: String = id
override val displayName: String = displayName
override val factoryId: String = factoryId
diff --git a/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt b/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt
deleted file mode 100644
index 791d64cc1c..0000000000
--- a/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.core.credentials.sso
-
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import software.aws.toolkits.core.region.aRegionId
-import software.aws.toolkits.core.utils.readText
-import software.aws.toolkits.core.utils.writeText
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.attribute.PosixFilePermission
-import java.time.Clock
-import java.time.Instant
-import java.time.ZoneOffset
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
-import java.time.temporal.ChronoUnit
-
-class DiskCacheTest {
- @Rule
- @JvmField
- val tempFolder = TemporaryFolder()
-
- private val now = Instant.now()
- private val clock = Clock.fixed(now, ZoneOffset.UTC)
-
- private val ssoUrl = "https://123456.awsapps.com/start"
- private val ssoRegion = aRegionId()
-
- private lateinit var cacheLocation: Path
- private lateinit var sut: DiskCache
-
- @Before
- fun setUp() {
- cacheLocation = tempFolder.newFolder().toPath()
- sut = DiskCache(cacheLocation, clock)
- }
-
- @Test
- fun nonExistentClientRegistrationReturnsNull() {
- assertThat(sut.loadClientRegistration(ssoRegion)).isNull()
- }
-
- @Test
- fun corruptClientRegistrationReturnsNull() {
- cacheLocation.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json").writeText("badData")
-
- assertThat(sut.loadClientRegistration(ssoRegion)).isNull()
- }
-
- @Test
- fun expiredClientRegistrationReturnsNull() {
- cacheLocation.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json").writeText(
- """
- {
- "clientId": "DummyId",
- "clientSecret": "DummySecret",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(now.minusSeconds(100))}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadClientRegistration(ssoRegion)).isNull()
- }
-
- @Test
- fun clientRegistrationExpiringSoonIsTreatedAsExpired() {
- val expiationTime = now.plus(14, ChronoUnit.MINUTES)
- cacheLocation.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json").writeText(
- """
- {
- "clientId": "DummyId",
- "clientSecret": "DummySecret",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(expiationTime)}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadClientRegistration(ssoRegion)).isNull()
- }
-
- @Test
- fun validClientRegistrationReturnsCorrectly() {
- val expiationTime = now.plus(20, ChronoUnit.MINUTES)
- cacheLocation.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json").writeText(
- """
- {
- "clientId": "DummyId",
- "clientSecret": "DummySecret",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(expiationTime)}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadClientRegistration(ssoRegion))
- .usingRecursiveComparison()
- .isEqualTo(
- ClientRegistration(
- "DummyId",
- "DummySecret",
- expiationTime
- )
- )
- }
-
- @Test
- fun clientRegistrationSavesCorrectly() {
- val expirationTime = DateTimeFormatter.ISO_INSTANT.parse("2020-04-07T21:31:33Z")
- sut.saveClientRegistration(
- ssoRegion,
- ClientRegistration(
- "DummyId",
- "DummySecret",
- Instant.from(expirationTime)
- )
- )
-
- val clientRegistration = cacheLocation.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json")
- if (isUnix()) {
- assertThat(Files.getPosixFilePermissions(clientRegistration)).containsOnly(PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_READ)
- }
- assertThat(clientRegistration.readText())
- .isEqualToIgnoringWhitespace(
- """
- {
- "clientId": "DummyId",
- "clientSecret": "DummySecret",
- "expiresAt": "2020-04-07T21:31:33Z"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun invalidateClientRegistrationDeletesTheFile() {
- val expiationTime = now.plus(20, ChronoUnit.MINUTES)
- val cacheFile = cacheLocation.resolve("aws-toolkit-jetbrains-client-id-$ssoRegion.json")
- cacheFile.writeText(
- """
- {
- "clientId": "DummyId",
- "clientSecret": "DummySecret",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(expiationTime)}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadClientRegistration(ssoRegion)).isNotNull
-
- sut.invalidateClientRegistration(ssoRegion)
-
- assertThat(sut.loadClientRegistration(ssoRegion)).isNull()
- assertThat(cacheFile).doesNotExist()
- }
-
- @Test
- fun nonExistentAccessTokenReturnsNull() {
- assertThat(sut.loadAccessToken(ssoUrl)).isNull()
- }
-
- @Test
- fun corruptAccessTokenReturnsNull() {
- cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json").writeText("badData")
-
- assertThat(sut.loadAccessToken(ssoUrl)).isNull()
- }
-
- @Test
- fun expiredAccessTokenReturnsNull() {
- cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json").writeText(
- """
- {
- "clientId": "$ssoUrl",
- "clientSecret": "$ssoRegion",
- "clientSecret": "DummyAccessToken",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(now.minusSeconds(100))}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadAccessToken(ssoUrl)).isNull()
- }
-
- @Test
- fun accessTokenExpiringSoonIsTreatedAsExpired() {
- val expiationTime = now.plus(14, ChronoUnit.MINUTES)
- cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json").writeText(
- """
- {
- "startUrl": "$ssoUrl",
- "region": "$ssoRegion",
- "accessToken": "DummyAccessToken",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(expiationTime)}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadAccessToken(ssoUrl)).isNull()
- }
-
- @Test
- fun validAccessTokenReturnsCorrectly() {
- val expiationTime = now.plus(20, ChronoUnit.MINUTES)
- cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json").writeText(
- """
- {
- "startUrl": "$ssoUrl",
- "region": "$ssoRegion",
- "accessToken": "DummyAccessToken",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(expiationTime)}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadAccessToken(ssoUrl))
- .usingRecursiveComparison()
- .isEqualTo(
- AccessToken(
- ssoUrl,
- ssoRegion,
- "DummyAccessToken",
- expiationTime
- )
- )
- }
-
- @Test
- fun validAccessTokenFromCliReturnsCorrectly() {
- cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json").writeText(
- """
- {
- "startUrl": "$ssoUrl",
- "region": "$ssoRegion",
- "accessToken": "DummyAccessToken",
- "expiresAt": "2999-06-10T00:50:40UTC"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadAccessToken(ssoUrl))
- .usingRecursiveComparison()
- .isEqualTo(
- AccessToken(
- ssoUrl,
- ssoRegion,
- "DummyAccessToken",
- ZonedDateTime.of(2999, 6, 10, 0, 50, 40, 0, ZoneOffset.UTC).toInstant()
- )
- )
- }
-
- @Test
- fun accessTokenSavesCorrectly() {
- val expirationTime = DateTimeFormatter.ISO_INSTANT.parse("2020-04-07T21:31:33Z")
- sut.saveAccessToken(
- ssoUrl,
- AccessToken(
- ssoUrl,
- ssoRegion,
- "DummyAccessToken",
- Instant.from(expirationTime)
- )
- )
-
- val accessTokenCache = cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json")
- if (isUnix()) {
- assertThat(Files.getPosixFilePermissions(accessTokenCache)).containsOnly(PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_READ)
- }
-
- assertThat(accessTokenCache.readText())
- .isEqualToIgnoringWhitespace(
- """
- {
- "startUrl": "$ssoUrl",
- "region": "$ssoRegion",
- "accessToken": "DummyAccessToken",
- "expiresAt": "2020-04-07T21:31:33Z"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun accessTokenInvalidationDeletesFile() {
- val expiationTime = now.plus(20, ChronoUnit.MINUTES)
- val cacheFile = cacheLocation.resolve("c1ac99f782ad92755c6de8647b510ec247330ad1.json")
- cacheFile.writeText(
- """
- {
- "startUrl": "$ssoUrl",
- "region": "$ssoRegion",
- "accessToken": "DummyAccessToken",
- "expiresAt": "${DateTimeFormatter.ISO_INSTANT.format(expiationTime)}"
- }
- """.trimIndent()
- )
-
- assertThat(sut.loadAccessToken(ssoUrl)).isNotNull
-
- sut.invalidateAccessToken(ssoUrl)
-
- assertThat(sut.loadAccessToken(ssoUrl)).isNull()
- assertThat(cacheFile).doesNotExist()
- }
-
- private fun isUnix() = !System.getProperty("os.name").toLowerCase().startsWith("windows")
-}
diff --git a/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt b/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt
index ec83119a0e..04ac131d1b 100644
--- a/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt
+++ b/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt
@@ -3,13 +3,13 @@
package software.aws.toolkits.core.lambda
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import software.aws.toolkits.core.utils.RemoteResourceResolver
import java.util.concurrent.CompletableFuture
@@ -37,20 +37,22 @@ class LambdaSampleEventProviderTest {
second.json
- """.trimIndent()
+ """.trimIndent()
)
- val firstContent = """
+ val firstContent =
+ """
{
"hello": "world"
}
- """.trimIndent()
+ """.trimIndent()
firstFile.writeText(firstContent)
- val secondContent = """
+ val secondContent =
+ """
["hello"]
- """.trimIndent()
+ """.trimIndent()
secondFile.writeText(secondContent)
@@ -86,7 +88,7 @@ class LambdaSampleEventProviderTest {
first.json
- """.trimIndent()
+ """.trimIndent()
)
val resourceResolver = mock {
diff --git a/core/tst/software/aws/toolkits/core/parser/EndpointsJsonValidatorTest.kt b/core/tst/software/aws/toolkits/core/parser/EndpointsJsonValidatorTest.kt
new file mode 100644
index 0000000000..bcccc65d6e
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/parser/EndpointsJsonValidatorTest.kt
@@ -0,0 +1,24 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.parser
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+import software.aws.toolkits.core.region.EndpointsJsonValidator
+
+class EndpointsJsonValidatorTest {
+ @Test
+ fun `endpoints json file parsing succeeds`() {
+ EndpointsJsonValidatorTest::class.java.getResourceAsStream("/jsonSampleSuccess.json").use {
+ assertThat(EndpointsJsonValidator.canBeParsed(it)).isTrue
+ }
+ }
+
+ @Test
+ fun `endpoints json file parsing fails`() {
+ EndpointsJsonValidatorTest::class.java.getResourceAsStream("/jsonSampleFailure.json").use {
+ assertThat(EndpointsJsonValidator.canBeParsed(it)).isFalse
+ }
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/parser/LambdaManifestValidatorTest.kt b/core/tst/software/aws/toolkits/core/parser/LambdaManifestValidatorTest.kt
new file mode 100644
index 0000000000..da0d4ca4f2
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/parser/LambdaManifestValidatorTest.kt
@@ -0,0 +1,25 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.parser
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+import software.aws.toolkits.core.lambda.LambdaManifestValidator
+
+class LambdaManifestValidatorTest {
+
+ @Test
+ fun `manifest xml file parsing succeeds`() {
+ LambdaManifestValidatorTest::class.java.getResourceAsStream("/xmlSampleSuccess.xml").use {
+ assertThat(LambdaManifestValidator.canBeParsed(it)).isTrue
+ }
+ }
+
+ @Test
+ fun `manifest xml file parsing fails`() {
+ LambdaManifestValidatorTest::class.java.getResourceAsStream("/xmlSampleFailure.xml").use {
+ assertThat(LambdaManifestValidator.canBeParsed(it)).isFalse
+ }
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/parser/LambdaSampleEventJsonValidatorTest.kt b/core/tst/software/aws/toolkits/core/parser/LambdaSampleEventJsonValidatorTest.kt
new file mode 100644
index 0000000000..63b80d58ef
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/parser/LambdaSampleEventJsonValidatorTest.kt
@@ -0,0 +1,23 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.parser
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+import software.aws.toolkits.core.lambda.LambdaSampleEventJsonValidator
+class LambdaSampleEventJsonValidatorTest {
+ @Test
+ fun `lambda sample event json file parsing succeeds`() {
+ LambdaSampleEventJsonValidatorTest::class.java.getResourceAsStream("/sampleLambdaEvent.json").use {
+ assertThat(LambdaSampleEventJsonValidator.canBeParsed(it)).isTrue
+ }
+ }
+
+ @Test
+ fun `lambda sample event json file parsing fails`() {
+ LambdaSampleEventJsonValidatorTest::class.java.getResourceAsStream("/jsonSampleFailure.json").use {
+ assertThat(LambdaSampleEventJsonValidator.canBeParsed(it)).isFalse
+ }
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/region/AwsRegionTest.kt b/core/tst/software/aws/toolkits/core/region/AwsRegionTest.kt
index cfce0d283d..e1dc7704d5 100644
--- a/core/tst/software/aws/toolkits/core/region/AwsRegionTest.kt
+++ b/core/tst/software/aws/toolkits/core/region/AwsRegionTest.kt
@@ -5,40 +5,99 @@ package software.aws.toolkits.core.region
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
+import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import software.aws.toolkits.core.utils.test.aString
import kotlin.random.Random
-@RunWith(Parameterized::class)
-class AwsRegionTest(private val region: AwsRegion, private val expectedCategory: String, private val expectedDisplayName: String) {
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "{2}")
- fun data(): Collection> = listOf(
- arrayOf(AwsRegion("ap-northeast-1", "Asia Pacific (Tokyo)", "aws"), "Asia Pacific", "Tokyo (ap-northeast-1)"),
- arrayOf(AwsRegion("ca-central-1", "Canada (Central)", "aws"), "North America", "Canada Central (ca-central-1)"),
- arrayOf(AwsRegion("eu-central-1", "EU (Frankfurt)", "aws"), "Europe", "Frankfurt (eu-central-1)"),
- arrayOf(AwsRegion("eu-south-1", "Europe (Milan)", "aws"), "Europe", "Milan (eu-south-1)"),
- arrayOf(AwsRegion("sa-east-1", "South America (Sao Paulo)", "aws"), "South America", "Sao Paulo (sa-east-1)"),
- arrayOf(AwsRegion("us-east-1", "US East (N. Virginia)", "aws"), "North America", "N. Virginia (us-east-1)"),
- arrayOf(AwsRegion("us-west-1", "US West (N. California)", "aws"), "North America", "N. California (us-west-1)"),
- arrayOf(AwsRegion("cn-north-1", "China (Beijing)", "aws"), "China", "Beijing (cn-north-1)"),
- arrayOf(AwsRegion("us-gov-west-1", "AWS GovCloud (US)", "aws"), "North America", "AWS GovCloud US (us-gov-west-1)"),
- arrayOf(AwsRegion("me-south-1", "Middle East (Bahrain)", "aws"), "Middle East", "Bahrain (me-south-1)"),
- arrayOf(AwsRegion("af-south-1", "Africa (Cape Town)", "aws"), "Africa", "Cape Town (af-south-1)")
- )
- }
+@RunWith(Enclosed::class)
+class AwsRegionTest {
+
+ @RunWith(Parameterized::class)
+ class NameAndCategorizationTest(private val region: AwsRegion, private val expectedCategory: String, private val expectedDisplayName: String) {
+ @Test
+ fun `display name should be correct`() {
+ assertThat(region.displayName).isEqualTo(expectedDisplayName)
+ }
- @Test
- fun displayNameShouldMatch() {
- assertThat(region.displayName).isEqualTo(expectedDisplayName)
+ @Test
+ fun `category should match`() {
+ assertThat(region.category).isEqualTo(expectedCategory)
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{2}")
+ fun data(): Collection> = listOf(
+ arrayOf(AwsRegion("ap-northeast-1", "Asia Pacific (Tokyo)", "aws"), "Asia Pacific", "Tokyo (ap-northeast-1)"),
+ arrayOf(AwsRegion("ca-central-1", "Canada (Central)", "aws"), "North America", "Canada Central (ca-central-1)"),
+ arrayOf(AwsRegion("eu-central-1", "EU (Frankfurt)", "aws"), "Europe", "Frankfurt (eu-central-1)"),
+ arrayOf(AwsRegion("eu-south-1", "Europe (Milan)", "aws"), "Europe", "Milan (eu-south-1)"),
+ arrayOf(AwsRegion("sa-east-1", "South America (Sao Paulo)", "aws"), "South America", "Sao Paulo (sa-east-1)"),
+ arrayOf(AwsRegion("us-east-1", "US East (N. Virginia)", "aws"), "North America", "N. Virginia (us-east-1)"),
+ arrayOf(AwsRegion("us-west-1", "US West (N. California)", "aws"), "North America", "N. California (us-west-1)"),
+ arrayOf(AwsRegion("cn-north-1", "China (Beijing)", "aws"), "China", "Beijing (cn-north-1)"),
+ arrayOf(AwsRegion("us-gov-west-1", "AWS GovCloud (US)", "aws"), "North America", "AWS GovCloud US (us-gov-west-1)"),
+ arrayOf(AwsRegion("me-south-1", "Middle East (Bahrain)", "aws"), "Middle East", "Bahrain (me-south-1)"),
+ arrayOf(AwsRegion("af-south-1", "Africa (Cape Town)", "aws"), "Africa", "Cape Town (af-south-1)")
+ )
+ }
}
- @Test
- fun categoryShouldMatch() {
- assertThat(region.category).isEqualTo(expectedCategory)
+ class ExtensionFunctionTests {
+
+ private val region = anAwsRegion()
+
+ @Test
+ fun `mergeWithExistingEnvironmentVariables puts basic settings in the map`() {
+ val env = mutableMapOf()
+
+ region.mergeWithExistingEnvironmentVariables(env)
+
+ assertThat(env).hasSize(2)
+ .containsEntry("AWS_REGION", region.id)
+ .containsEntry("AWS_DEFAULT_REGION", region.id)
+ }
+
+ @Test
+ fun `mergeWithExistingEnvironmentVariables does not replace existing AWS_REGION`() {
+ val existing = aString()
+ val env = mutableMapOf(
+ "AWS_REGION" to existing
+ )
+
+ region.mergeWithExistingEnvironmentVariables(env)
+
+ assertThat(env).hasSize(1).containsEntry("AWS_REGION", existing)
+ }
+
+ @Test
+ fun `mergeWithExistingEnvironmentVariables does not replace existing AWS_DEFAULT_REGION`() {
+ val existing = aString()
+ val env = mutableMapOf(
+ "AWS_DEFAULT_REGION" to existing
+ )
+
+ region.mergeWithExistingEnvironmentVariables(env)
+
+ assertThat(env).hasSize(1).containsEntry("AWS_DEFAULT_REGION", existing)
+ }
+
+ @Test
+ fun `mergeWithExistingEnvironmentVariables can force replace existing`() {
+ val existing = aString()
+ val env = mutableMapOf(
+ "AWS_REGION" to existing,
+ "AWS_DEFAULT_REGION" to existing
+ )
+
+ region.mergeWithExistingEnvironmentVariables(env, replace = true)
+
+ assertThat(env).hasSize(2)
+ .containsEntry("AWS_REGION", region.id)
+ .containsEntry("AWS_DEFAULT_REGION", region.id)
+ }
}
}
@@ -46,7 +105,7 @@ fun anAwsRegion(id: String = aRegionId(), name: String = aString(), partitionId:
fun aRegionId(): String {
val prefix = arrayOf("af", "us", "ca", "eu", "ap", "me", "cn").random()
- val compass = arrayOf("north", "south", "east", "west", "central")
- val count = Random.nextInt(1, 10)
+ val compass = arrayOf("north", "south", "east", "west", "central").random()
+ val count = Random.nextInt(1, 100)
return "$prefix-$compass-$count"
}
diff --git a/core/tst/software/aws/toolkits/core/rules/EcrTemporaryRepositoryRule.kt b/core/tst/software/aws/toolkits/core/rules/EcrTemporaryRepositoryRule.kt
new file mode 100644
index 0000000000..4db7f61370
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/rules/EcrTemporaryRepositoryRule.kt
@@ -0,0 +1,48 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.rules
+
+import org.junit.rules.ExternalResource
+import software.amazon.awssdk.services.ecr.EcrClient
+import software.amazon.awssdk.services.ecr.model.Repository
+import software.amazon.awssdk.services.ecr.model.RepositoryNotFoundException
+import software.aws.toolkits.core.utils.RuleUtils
+
+class EcrTemporaryRepositoryRule(private val ecrClientSupplier: () -> EcrClient) : ExternalResource() {
+ constructor(ecrClient: EcrClient) : this({ ecrClient })
+
+ private val repositories = mutableListOf()
+
+ /**
+ * Creates a temporary repository with the optional prefix (or calling class if prefix is omitted)
+ */
+ fun createRepository(prefix: String = RuleUtils.prefixFromCallingClass()): Repository {
+ val repositoryName: String = RuleUtils.randomName(prefix).lowercase()
+ val client = ecrClientSupplier()
+
+ // note there is no waiter for this
+ val repo = client.createRepository { it.repositoryName(repositoryName) }
+
+ repositories.add(repositoryName)
+
+ return repo.repository()
+ }
+
+ override fun after() {
+ val exceptions = repositories.mapNotNull { deleteRepository(it) }
+ if (exceptions.isNotEmpty()) {
+ throw RuntimeException("Failed to delete all repositories. \n\t- ${exceptions.map { it.message }.joinToString("\n\t- ")}")
+ }
+ }
+
+ private fun deleteRepository(repository: String): Exception? = try {
+ ecrClientSupplier().deleteRepository { it.repositoryName(repository).force(true) }
+ null
+ } catch (e: Exception) {
+ when (e) {
+ is RepositoryNotFoundException -> null
+ else -> RuntimeException("Failed to delete repository: $repository - ${e.message}", e)
+ }
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt b/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt
index a339a2674c..166de73b8f 100644
--- a/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt
+++ b/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt
@@ -14,6 +14,7 @@ import java.security.PrivilegedAction
class EnvironmentVariableHelper : ExternalResource() {
private val originalEnvironmentVariables = System.getenv().toMap()
private val modifiableMap = getProcessEnvMap() ?: getEnvMap()
+
@Volatile
private var mutated = false
@@ -36,9 +37,11 @@ class EnvironmentVariableHelper : ExternalResource() {
private fun getField(processEnvironment: Class<*>, obj: Any?, fieldName: String): MutableMap? = try {
val declaredField = processEnvironment.getDeclaredField(fieldName)
- AccessController.doPrivileged(PrivilegedAction {
- declaredField.isAccessible = true
- })
+ AccessController.doPrivileged(
+ PrivilegedAction {
+ declaredField.isAccessible = true
+ }
+ )
@Suppress("UNCHECKED_CAST")
declaredField.get(obj) as MutableMap
} catch (_: NoSuchFieldException) {
diff --git a/core/tst/software/aws/toolkits/core/rules/S3TemporaryBucketRule.kt b/core/tst/software/aws/toolkits/core/rules/S3TemporaryBucketRule.kt
index 0a0a2ef0c4..bbb58e8b1c 100644
--- a/core/tst/software/aws/toolkits/core/rules/S3TemporaryBucketRule.kt
+++ b/core/tst/software/aws/toolkits/core/rules/S3TemporaryBucketRule.kt
@@ -8,22 +8,23 @@ import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.NoSuchBucketException
import software.aws.toolkits.core.s3.deleteBucketAndContents
import software.aws.toolkits.core.utils.RuleUtils
-import software.aws.toolkits.core.utils.Waiters.waitUntilBlocking
-class S3TemporaryBucketRule(private val s3Client: S3Client) : ExternalResource() {
+class S3TemporaryBucketRule(private val s3ClientSupplier: () -> S3Client) : ExternalResource() {
+ constructor(s3Client: S3Client) : this({ s3Client })
+
private val buckets = mutableListOf()
/**
* Creates a temporary bucket with the optional prefix (or calling class if prefix is omitted)
*/
fun createBucket(prefix: String = RuleUtils.prefixFromCallingClass()): String {
- val bucketName: String = RuleUtils.randomName(prefix)
- s3Client.createBucket { it.bucket(bucketName) }
+ val bucketName: String = RuleUtils.randomName(prefix).lowercase()
+ val client = s3ClientSupplier()
+
+ client.createBucket { it.bucket(bucketName) }
// Wait for bucket to be ready
- waitUntilBlocking(exceptionsToIgnore = setOf(NoSuchBucketException::class)) {
- s3Client.headBucket { it.bucket(bucketName) }
- }
+ client.waiter().waitUntilBucketExists { it.bucket(bucketName) }
buckets.add(bucketName)
@@ -38,7 +39,7 @@ class S3TemporaryBucketRule(private val s3Client: S3Client) : ExternalResource()
}
private fun deleteBucketAndContents(bucket: String): Exception? = try {
- s3Client.deleteBucketAndContents(bucket)
+ s3ClientSupplier().deleteBucketAndContents(bucket)
null
} catch (e: Exception) {
when (e) {
diff --git a/core/tst/software/aws/toolkits/core/rules/SystemPropertyHelper.kt b/core/tst/software/aws/toolkits/core/rules/SystemPropertyHelper.kt
index 5e767bb136..cfeca9ccb6 100644
--- a/core/tst/software/aws/toolkits/core/rules/SystemPropertyHelper.kt
+++ b/core/tst/software/aws/toolkits/core/rules/SystemPropertyHelper.kt
@@ -7,7 +7,7 @@ import org.junit.rules.ExternalResource
import java.util.Properties
/**
- * A utility that can temporarily forcibly set environment variables and
+ * A utility that can temporarily forcibly set system properties and
* then allows resetting them to the original values.
*/
class SystemPropertyHelper : ExternalResource() {
diff --git a/core/tst/software/aws/toolkits/core/telemetry/TelemetryBatcherTest.kt b/core/tst/software/aws/toolkits/core/telemetry/TelemetryBatcherTest.kt
index e533fb903e..bceeff9ebc 100644
--- a/core/tst/software/aws/toolkits/core/telemetry/TelemetryBatcherTest.kt
+++ b/core/tst/software/aws/toolkits/core/telemetry/TelemetryBatcherTest.kt
@@ -3,18 +3,18 @@
package software.aws.toolkits.core.telemetry
-import com.nhaarman.mockitokotlin2.argumentCaptor
-import com.nhaarman.mockitokotlin2.doAnswer
-import com.nhaarman.mockitokotlin2.doThrow
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.stub
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verifyBlocking
-import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyCollection
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verifyBlocking
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.stubbing.Answer
import software.amazon.awssdk.core.exception.SdkServiceException
import java.util.concurrent.CountDownLatch
@@ -125,7 +125,7 @@ class TelemetryBatcherTest {
batcher.enqueue(createEmptyMetricEvent())
batcher.flush(false)
- verifyZeroInteractions(publisher)
+ verifyNoMoreInteractions(publisher)
assertThat(batcher.eventQueue).isEmpty()
}
@@ -139,7 +139,7 @@ class TelemetryBatcherTest {
batcher.flush(false)
- verifyZeroInteractions(publisher)
+ verifyNoMoreInteractions(publisher)
assertThat(batcher.eventQueue).isEmpty()
}
diff --git a/core/tst/software/aws/toolkits/core/utils/CollectionUtilsTest.kt b/core/tst/software/aws/toolkits/core/utils/CollectionUtilsTest.kt
new file mode 100644
index 0000000000..2636084204
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/utils/CollectionUtilsTest.kt
@@ -0,0 +1,27 @@
+// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.utils
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+
+class CollectionUtilsTest {
+
+ @Test
+ fun `collection items are replaced`() {
+ val source = mutableListOf("hello")
+
+ source.replace(listOf("world"))
+
+ assertThat(source).containsOnly("world")
+ }
+
+ @Test
+ fun `map entries are replaced`() {
+ val source = mutableMapOf("foo" to "bar")
+ source.replace(mapOf("hello" to "world"))
+
+ assertThat(source).containsOnlyKeys("hello")
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/utils/DelegateSdkConsumers.kt b/core/tst/software/aws/toolkits/core/utils/DelegateSdkConsumers.kt
index 69c85a3e07..b59d463bef 100644
--- a/core/tst/software/aws/toolkits/core/utils/DelegateSdkConsumers.kt
+++ b/core/tst/software/aws/toolkits/core/utils/DelegateSdkConsumers.kt
@@ -3,34 +3,52 @@
package software.aws.toolkits.core.utils
-import com.nhaarman.mockitokotlin2.KStubbing
-import com.nhaarman.mockitokotlin2.withSettings
import org.mockito.Mockito
import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.KStubbing
+import org.mockito.kotlin.withSettings
import org.mockito.stubbing.Answer
+import software.amazon.awssdk.core.SdkClient
import software.amazon.awssdk.core.SdkRequest
import kotlin.reflect.full.isSubclassOf
-class DelegateSdkConsumers : Answer {
+/**
+ * Answer that inspects the target invocation and determines if it should call the real method or respond with a mock answer.
+ * This is tied to the implementation of the Java SDK V2's generated client interfaces where lambda based calls use a default implementation to delegate
+ * eventually down to a method that takes a built SdkRequest type. The final concrete method is coded to always throw an exception so that is the method that we
+ * will mock.
+ *
+ * This will handle "simple" methods that they generate as well such as ListBuckets that takes 0 arguments.
+ */
+class DelegateSdkConsumers(private val sdkClass: Class<*>) : Answer {
override fun answer(invocation: InvocationOnMock): Any? {
val method = invocation.method
- return if (method.isDefault &&
- method?.parameters?.getOrNull(0)?.type?.kotlin?.isSubclassOf(SdkRequest::class) != true
- ) {
+ return if (method.name == "waiter") {
+ createWaiter(invocation.method.returnType, invocation)
+ } else if (method.isDefault && method?.parameters?.getOrNull(0)?.type?.kotlin?.isSubclassOf(SdkRequest::class) != true) {
invocation.callRealMethod()
} else {
Mockito.RETURNS_DEFAULTS.answer(invocation)
}
}
+
+ private fun createWaiter(waiterType: Class<*>, invocation: InvocationOnMock): Any? {
+ val builder = waiterType.getDeclaredMethod("builder").invoke(null)
+ with(builder::class.java) {
+ getDeclaredMethod("client", sdkClass).invoke(builder, invocation.mock)
+ return getDeclaredMethod("build").invoke(builder)
+ }
+ }
}
-inline fun delegateMock(): T = Mockito.mock(
+inline fun delegateMock(verboseLogging: Boolean = false): T = Mockito.mock(
T::class.java,
withSettings(
- defaultAnswer = DelegateSdkConsumers()
+ verboseLogging = verboseLogging,
+ defaultAnswer = DelegateSdkConsumers(T::class.java)
)
)
-inline fun delegateMock(stubbing: KStubbing.(T) -> Unit): T = delegateMock().apply {
+inline fun delegateMock(stubbing: KStubbing.(T) -> Unit): T = delegateMock().apply {
KStubbing(this).stubbing(this)
}
diff --git a/core/tst/software/aws/toolkits/core/utils/IntegrationTestCredentials.kt b/core/tst/software/aws/toolkits/core/utils/IntegrationTestCredentials.kt
new file mode 100644
index 0000000000..db5648b40e
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/utils/IntegrationTestCredentials.kt
@@ -0,0 +1,39 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.utils
+
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
+import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider
+import software.amazon.awssdk.services.sts.StsClient
+import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider
+import software.amazon.awssdk.services.sts.model.AssumeRoleRequest
+import software.amazon.awssdk.utils.SdkAutoCloseable
+import java.util.UUID
+
+/**
+ * Creates an [AwsCredentialsProvider] meant to be used in integration tests
+ *
+ * If the environment variable `ASSUME_ROLE_ARN` is set, it will be assumed using the default credential chain as the source credentials.
+ * If it is not set, we will just use the default credential provider chain
+ */
+fun createIntegrationTestCredentialProvider(): AwsCredentialsProvider {
+ // TODO: Finish https://github.com/aws/aws-toolkit-jetbrains/pull/2193 and revert back to Default Chain
+ val defaultCredentials = ContainerCredentialsProvider.builder().build()
+
+ return System.getenv("ASSUME_ROLE_ARN")?.takeIf { it.isNotEmpty() }?.let { role ->
+ val sessionName = UUID.randomUUID().toString()
+ val stsClient = StsClient.builder().credentialsProvider(defaultCredentials).build()
+ val credentialsProvider = StsAssumeRoleCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .refreshRequest(AssumeRoleRequest.builder().roleArn(role).roleSessionName(sessionName).build())
+ .build()
+
+ // Wrap this in SdkAutoClosable so we have a hook to close this STS client else IntelliJ will say we thread leak
+ return object : AwsCredentialsProvider by credentialsProvider, SdkAutoCloseable {
+ override fun close() {
+ stsClient.close()
+ }
+ }
+ } ?: defaultCredentials
+}
diff --git a/core/tst/software/aws/toolkits/core/utils/LogUtilsTest.kt b/core/tst/software/aws/toolkits/core/utils/LogUtilsTest.kt
index d39b86bfe7..472117e9c2 100644
--- a/core/tst/software/aws/toolkits/core/utils/LogUtilsTest.kt
+++ b/core/tst/software/aws/toolkits/core/utils/LogUtilsTest.kt
@@ -1,18 +1,19 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
+@file:Suppress("LazyLog")
package software.aws.toolkits.core.utils
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.reset
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.verifyZeroInteractions
-import com.nhaarman.mockitokotlin2.whenever
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
import org.slf4j.Logger
import org.slf4j.event.Level
@@ -45,7 +46,7 @@ class LogUtilsTest {
@Test
fun nullableIsNotOkInTryOrThrow() {
- val exception = catch { log.tryOrThrow("message") { mightBeNull(shouldBeNull = true) } }
+ val exception = catch { log.tryOrThrow("message") { mightBeNull(shouldBeNull = true) } }
verify(log).error(any(), eq(exception))
}
@@ -59,13 +60,13 @@ class LogUtilsTest {
@Test
fun nullableIsOkInTryOrThrowNullable() {
log.tryOrThrowNullable("message") { null }
- verifyZeroInteractions(log)
+ verifyNoMoreInteractions(log)
}
@Test
fun nullIsOkInTryOrNull() {
log.tryOrNull("message") { null }
- verifyZeroInteractions(log)
+ verifyNoMoreInteractions(log)
}
@Test
@@ -174,5 +175,6 @@ class LogUtilsTest {
"hello"
}
+ @Suppress("FunctionOnlyReturningConstant")
private fun willNeverBeNull(): String = "hello"
}
diff --git a/core/tst/software/aws/toolkits/core/utils/RemoteResourceResolverTest.kt b/core/tst/software/aws/toolkits/core/utils/RemoteResourceResolverTest.kt
index 02dc511b94..2c80b39542 100644
--- a/core/tst/software/aws/toolkits/core/utils/RemoteResourceResolverTest.kt
+++ b/core/tst/software/aws/toolkits/core/utils/RemoteResourceResolverTest.kt
@@ -3,17 +3,18 @@
package software.aws.toolkits.core.utils
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.doAnswer
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import software.aws.toolkits.core.lambda.LambdaManifestValidator
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
@@ -40,8 +41,8 @@ class RemoteResourceResolverTest {
val resource = resource()
- val firstCall = sut.resolve(resource).toCompletableFuture().get()
- val secondCall = sut.resolve(resource).toCompletableFuture().get()
+ val firstCall = sut.resolve(resource).unwrap()
+ val secondCall = sut.resolve(resource).unwrap()
assertThat(firstCall).isEqualTo(secondCall)
assertThat(firstCall).hasContent("data")
@@ -52,7 +53,7 @@ class RemoteResourceResolverTest {
@Test
fun expiredFileIsDownloadedAgain() {
val urlFetcher = mock {
- on { fetch(eq(PRIMARY_URL), any()) }.doAnswer(writeDataToFile("first")).doAnswer(writeDataToFile("second"))
+ on { fetch(eq(PRIMARY_URL), any()) }.doAnswer(writeDataToFile("data1")).doAnswer(writeDataToFile("data2"))
}
val sut = DefaultRemoteResourceResolver(urlFetcher, tempPath.newFolder().toPath(), immediateExecutor)
@@ -64,10 +65,29 @@ class RemoteResourceResolverTest {
val secondCall = sut.resolve(resource).toCompletableFuture().get()
assertThat(firstCall).isEqualTo(secondCall)
- assertThat(secondCall).hasContent("second")
+
+ assertThat(secondCall).hasContent("data2")
verify(urlFetcher, times(2)).fetch(eq(PRIMARY_URL), any())
}
+ @Test
+ fun downloadedParseFailedSkippedMove() {
+ val urlFetcher = mock {
+ on { fetch(eq(PRIMARY_URL), any()) }.doAnswer(writeDataToFile(FAIL))
+ }
+
+ val cachePath = tempPath.newFolder().toPath()
+ val sut = DefaultRemoteResourceResolver(urlFetcher, cachePath, immediateExecutor)
+
+ val resource = xmlResource()
+
+ val firstCall = sut.resolve(resource).unwrap()
+ val secondCall = sut.resolve(resource).unwrap()
+
+ assertThat(firstCall).isEqualTo(secondCall)
+ assertThat(firstCall.exists()).isFalse()
+ }
+
@Test
fun failureToHitUrlFallsBackToCurrentCopy() {
val urlFetcher = mock {
@@ -114,7 +134,6 @@ class RemoteResourceResolverTest {
@Test
fun canFallbackDownListOfUrls() {
-
val urlFetcher = mock {
on { fetch(eq(PRIMARY_URL), any()) }.thenThrow(RuntimeException("BOOM!"))
on { fetch(eq(SECONDARY_URL), any()) }.doAnswer(writeDataToFile("data"))
@@ -126,25 +145,51 @@ class RemoteResourceResolverTest {
}
private companion object {
+ val LOG = getLogger()
+
fun resource(
name: String = "resource",
urls: List = listOf(PRIMARY_URL),
ttl: Duration? = Duration.ofMillis(1000),
- initialValue: InputStream? = null
+ initialValue: InputStream? = null,
+ ) = object : RemoteResource {
+ override val urls: List = urls
+ override val name: String = name
+ override val ttl: Duration? = ttl
+ override val initialValue = initialValue?.let { { it } }
+ }
+
+ fun xmlResource(
+ name: String = "resource",
+ urls: List = listOf(PRIMARY_URL),
+ ttl: Duration? = Duration.ofMillis(1000),
+ initialValue: InputStream? = null,
+ remoteResolveParser: RemoteResolveParser? = LambdaManifestValidator
) = object : RemoteResource {
override val urls: List = urls
override val name: String = name
override val ttl: Duration? = ttl
override val initialValue = initialValue?.let { { it } }
+ override val remoteResolveParser: RemoteResolveParser = remoteResolveParser as LambdaManifestValidator
}
fun writeDataToFile(data: String): (InvocationOnMock) -> Unit = { invocation ->
- (invocation.arguments[1] as Path).writeText(data)
+ val path = invocation.arguments[1] as Path
+ path.writeText(data)
+ // It's possible for it to be done writing but path.exists to not work yet which
+ // makes the canDownloadAFileOnce test fail (on CodeBuild).
+ while (!path.exists()) {
+ LOG.debug { "writeDataToFile path does not exist yet: $path" }
+ Thread.sleep(10)
+ }
}
val immediateExecutor: (Callable) -> CompletionStage = { CompletableFuture.completedFuture(it.call()) }
const val PRIMARY_URL = "http://example.com"
const val SECONDARY_URL = "http://example2.com"
+ const val FAIL = "" +
+ "data" +
+ "<>"
}
}
diff --git a/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt b/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt
index 3cb636a72c..5556fe2ba5 100644
--- a/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt
+++ b/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt
@@ -7,13 +7,14 @@ import java.util.Random
object RuleUtils {
fun randomName(prefix: String = "a", length: Int = 63): String {
+ val characters = ('0'..'9') + ('A'..'Z') + ('a'..'Z')
val userName = System.getProperty("user.name", "unknown")
- return "${prefix.toLowerCase()}-${userName.toLowerCase()}-${Random().nextInt(10000)}".take(length)
+ return "${prefix.lowercase()}-${userName.lowercase()}-${List(length) { characters.random() }.joinToString("")}".take(length)
}
fun prefixFromCallingClass(): String {
- val callingClass = Thread.currentThread().stackTrace[3].className
- return callingClass.substringAfterLast(".")
+ val callingClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).callerClass
+ return callingClass.simpleName
}
fun randomNumber(min: Int = 0, max: Int = 65535): Int = Random().nextInt(max - min + 1) + min
diff --git a/core/tst/software/aws/toolkits/core/utils/RuleUtilsTest.kt b/core/tst/software/aws/toolkits/core/utils/RuleUtilsTest.kt
new file mode 100644
index 0000000000..c1402150fd
--- /dev/null
+++ b/core/tst/software/aws/toolkits/core/utils/RuleUtilsTest.kt
@@ -0,0 +1,27 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.core.utils
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Before
+import org.junit.Test
+
+class RuleUtilsTest {
+ private lateinit var callingClass: String
+
+ @Before
+ fun setUp() {
+ callingClass = RuleUtils.prefixFromCallingClass()
+ }
+
+ @Test
+ fun `late init before works`() {
+ assertThat(callingClass).isEqualTo("RuleUtilsTest")
+ }
+
+ @Test
+ fun `inline works`() {
+ assertThat(RuleUtils.prefixFromCallingClass()).isEqualTo("RuleUtilsTest")
+ }
+}
diff --git a/core/tst/software/aws/toolkits/core/utils/test/AssertJAsserts.kt b/core/tst/software/aws/toolkits/core/utils/test/AssertJAsserts.kt
index 07aab489ca..106d7720e8 100644
--- a/core/tst/software/aws/toolkits/core/utils/test/AssertJAsserts.kt
+++ b/core/tst/software/aws/toolkits/core/utils/test/AssertJAsserts.kt
@@ -3,8 +3,18 @@
package software.aws.toolkits.core.utils.test
+import org.assertj.core.api.IterableAssert
+import org.assertj.core.api.ListAssert
import org.assertj.core.api.ObjectAssert
@Suppress("UNCHECKED_CAST")
val ObjectAssert.notNull: ObjectAssert
get() = this.isNotNull as ObjectAssert
+
+@Suppress("UNCHECKED_CAST")
+inline fun IterableAssert<*>.hasOnlyElementsOfType(): IterableAssert =
+ hasOnlyElementsOfType(SubType::class.java) as IterableAssert
+
+@Suppress("UNCHECKED_CAST")
+inline fun ListAssert<*>.hasOnlyOneElementOfType(): ObjectAssert =
+ (hasOnlyElementsOfType(SubType::class.java) as ListAssert).singleElement()
diff --git a/core/tst/software/aws/toolkits/core/utils/test/TestUtilsTest.kt b/core/tst/software/aws/toolkits/core/utils/test/TestUtilsTest.kt
index 2e90092f65..83b3a5b5de 100644
--- a/core/tst/software/aws/toolkits/core/utils/test/TestUtilsTest.kt
+++ b/core/tst/software/aws/toolkits/core/utils/test/TestUtilsTest.kt
@@ -3,11 +3,11 @@
package software.aws.toolkits.core.utils.test
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import java.time.Duration
class TestUtilsTest {
diff --git a/designs/credentialManagement/credentialManagement.md b/designs/credentialManagement/credentialManagement.md
index 6cb560e707..c75a21c0c3 100644
--- a/designs/credentialManagement/credentialManagement.md
+++ b/designs/credentialManagement/credentialManagement.md
@@ -17,14 +17,14 @@ by third party plugins.
## Classes and Concepts
![ClassDiagram]
-1. `AwsRegion` - Date class that represents an AWS Region and joins together related data for that region. This data is sourced from the endpoints.json file.
+1. `AwsRegion` - Data class that represents an AWS Region and joins together related data for that region. This data is sourced from the endpoints.json file.
It contains the following data:
1. `ID` - Contains the ID of the region (e.g. `us-west-2`)
1. `Name` - Contains the human readable name for the region (e.g. `US West (Oregon)`)
1. `Partiton ID` - Contains the ID of the top level AWS partition (e.g. `aws`, `aws-cn`)
-1. `CredentialIdentifier` - Represents the globally unique identifier for a possible credential profile in the toolkit. This identifier must be deterministic
-meaning that if two `CredentialIdentifier`s for the same credential source should be equal even across different IDE sessions.
+1. `CredentialIdentifier` - Represents the globally unique identifier for a possible credential profile in the toolkit. This identifier must be deterministic,
+meaning a credential source should have identical `CredentialIdentifier` values when represented by different instances, or when used across different IDE sessions.
This is shown to the user as the **Profile** in the UI.
1. `AwsCredentialsProvider` - SDK interface that resolves AWS Credentials from the provider. For more info, see [AwsCredentialsProvider] in the SDK.
@@ -69,8 +69,9 @@ If the call fails, we consider the credentials to be invalid.
The class [AwsConnectionManager] is the entry point into this system.
-The concept of _Active Connection Settings_ represents the current user selected credentials and region that the toolkit uses to perform actions in the AWS Explorer as well as
-being used as defaults when more than one option is possible.
+The concept of _Active Connection Settings_ represents the user's currently selected credentials and region. They are used by the toolkit:
+* to perform actions in the AWS Explorer
+* as defaults when presenting more than one option in a dialog
Due to the nature of the IntellJ projects (project level) each has their own windows while existing in one JVM (application level). Since we store active
connection settings at the project level, each window can have a different active `CredenitalIdentifier` and/or `AwsRegion` selected.
diff --git a/designs/credentialManagement/images/classDiagram.svg b/designs/credentialManagement/images/classDiagram.svg
index de97bcd5a8..7acc439c49 100644
--- a/designs/credentialManagement/images/classDiagram.svg
+++ b/designs/credentialManagement/images/classDiagram.svg
@@ -1,20 +1,19 @@
-
+-->
\ No newline at end of file
diff --git a/detekt-rules/build.gradle.kts b/detekt-rules/build.gradle.kts
new file mode 100644
index 0000000000..13b426c44b
--- /dev/null
+++ b/detekt-rules/build.gradle.kts
@@ -0,0 +1,26 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+plugins {
+ id("toolkit-kotlin-conventions")
+ id("toolkit-testing")
+}
+
+dependencies {
+ compileOnly(libs.detekt.api)
+
+ testImplementation(libs.detekt.test)
+ testImplementation(libs.junit4)
+ testImplementation(libs.assertj)
+
+ testRuntimeOnly(libs.slf4j.api)
+ testRuntimeOnly(libs.junit5.jupiterVintage)
+}
+
+tasks.test {
+ useJUnitPlatform()
+}
+
+tasks.jar {
+ duplicatesStrategy = DuplicatesStrategy.WARN
+}
diff --git a/detekt-rules/detekt.yml b/detekt-rules/detekt.yml
new file mode 100644
index 0000000000..f05ffeeffc
--- /dev/null
+++ b/detekt-rules/detekt.yml
@@ -0,0 +1,239 @@
+build:
+ excludeCorrectable: false
+
+config:
+ validation: true
+
+processors:
+ active: true
+ exclude:
+ - 'DetektProgressListener'
+
+console-reports:
+ active: true
+ exclude:
+ - 'ProjectStatisticsReport'
+ - 'ComplexityReport'
+ - 'NotificationReport'
+ - 'FileBasedFindingsReport'
+
+output-reports:
+ active: true
+
+comments:
+ active: true
+ AbsentOrWrongFileLicense:
+ active: true
+ licenseTemplateFile: ./license.template
+ licenseTemplateIsRegex: true
+
+complexity:
+ active: false
+
+coroutines:
+ active: true
+ GlobalCoroutineUsage:
+ active: true
+ RedundantSuspendModifier:
+ active: true
+ SleepInsteadOfDelay:
+ active: true
+ SuspendFunWithFlowReturnType:
+ active: true
+
+empty-blocks:
+ EmptyFunctionBlock:
+ active: false
+
+exceptions:
+ # there might actually be some useful rules in here
+ active: false
+
+# unfortunately, does not respect .editorconfig in the project folder
+# this configuration reflects the delta from the default "formatting" (ktlint) rule set
+formatting:
+ active: true
+ android: false
+ autoCorrect: true
+ AnnotationOnSeparateLine:
+ active: false
+ autoCorrect: true
+ AnnotationSpacing:
+ active: false
+ autoCorrect: true
+ ArgumentListWrapping:
+ active: true
+ autoCorrect: true
+ indentSize: 4
+ maxLineLength: 160
+ Indentation:
+ active: true
+ autoCorrect: true
+ indentSize: 4
+ excludes: [ '**/TelemetryDefinitions.kt' ]
+ MaximumLineLength:
+ active: true
+ maxLineLength: 160
+ ignoreBackTickedIdentifier: true
+ NoWildcardImports:
+ # no `packagesToUseImportOnDemandProperty` because we don't want to allow any star imports
+ active: true
+ ParameterListWrapping:
+ active: true
+ indentSize: 4
+ maxLineLength: 160
+ ParameterWrapping:
+ active: true
+ indentSize: 4
+ maxLineLength: 160
+ PropertyWrapping:
+ active: true
+ indentSize: 4
+ maxLineLength: 160
+ SpacingBetweenDeclarationsWithComments:
+ active: true
+ autoCorrect: true
+ excludes: [ '**/icons/**' ]
+ Wrapping:
+ active: true
+ autoCorrect: true
+ indentSize: 4
+ maxLineLength: 160
+
+naming:
+ active: true
+ ClassNaming:
+ active: true
+ classPattern: '[A-Z][a-zA-Z0-9]*'
+ ConstructorParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ privateParameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ EnumNaming:
+ active: true
+ enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
+ FunctionNaming:
+ active: true
+ functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'
+ excludeClassPattern: '$^'
+ ignoreAnnotated: [ 'Composable' ]
+ FunctionParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ # prefer rule provided by ktlint
+ MatchingDeclarationName:
+ active: false
+ MemberNameEqualsClassName:
+ active: false
+ NoNameShadowing:
+ active: true
+ ObjectPropertyNaming:
+ active: true
+ constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+ PackageNaming:
+ active: true
+ packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
+ TopLevelPropertyNaming:
+ active: true
+ constantPattern: '[A-Z][_A-Z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
+ VariableNaming:
+ active: true
+ variablePattern: '[a-z][A-Za-z0-9]*'
+ privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+
+performance:
+ SpreadOperator:
+ active: false
+
+potential-bugs:
+ active: true
+ CastToNullableType:
+ active: false
+ Deprecation:
+ active: false
+ DontDowncastCollectionTypes:
+ active: false
+ EqualsAlwaysReturnsTrueOrFalse:
+ active: true
+ EqualsWithHashCodeExist:
+ active: true
+ ExitOutsideMain:
+ active: true
+ ExplicitGarbageCollectionCall:
+ active: true
+ HasPlatformType:
+ active: false
+ IgnoredReturnValue:
+ active: false
+ ImplicitUnitReturnType:
+ active: false
+ allowExplicitReturnType: true
+ InvalidRange:
+ active: true
+ IteratorHasNextCallsNextMethod:
+ active: true
+ IteratorNotThrowingNoSuchElementException:
+ active: true
+ MapGetWithNotNullAssertionOperator:
+ active: false
+ NullableToStringCall:
+ active: false
+ UnconditionalJumpStatementInLoop:
+ active: false
+ UnnecessaryNotNullOperator:
+ active: true
+ UnnecessarySafeCall:
+ active: true
+ UnreachableCatchBlock:
+ active: true
+ # doesn't seem to work correctly on our codebase
+ # qodana provides coverage for this
+ UnreachableCode:
+ active: false
+ UnsafeCallOnNullableType:
+ active: true
+ UnsafeCast:
+ active: true
+ UnusedUnaryOperator:
+ active: false
+ UselessPostfixExpression:
+ active: false
+ WrongEqualsTypeParameter:
+ active: true
+
+style:
+ ExpressionBodySyntax:
+ active: true
+ includeLineWrapping: true
+ ForbiddenComment:
+ active: false
+ MagicNumber:
+ active: false
+ # prefer rule provided by ktlint
+ MaxLineLength:
+ active: false
+ # prefer rule provided by ktlint
+ ModifierOrder:
+ active: false
+ # prefer rule provided by ktlint
+ NewLineAtEndOfFile:
+ active: false
+ NoTabs:
+ active: true
+ ReturnCount:
+ active: false
+ ThrowsCount:
+ active: false
+ UnnecessaryAbstractClass:
+ active: false
+ UnusedPrivateMember:
+ allowedNames: '(_|ignored|expected|serialVersionUID|createUIComponents)'
+ VarCouldBeVal:
+ active: false
diff --git a/detekt-rules/license.template b/detekt-rules/license.template
new file mode 100644
index 0000000000..5bce42c196
--- /dev/null
+++ b/detekt-rules/license.template
@@ -0,0 +1 @@
+^// Copyright \d{4} Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n?
diff --git a/detekt-rules/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider b/detekt-rules/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider
new file mode 100644
index 0000000000..ecba26700c
--- /dev/null
+++ b/detekt-rules/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider
@@ -0,0 +1 @@
+software.aws.toolkits.gradle.detekt.rules.CustomRuleSetProvider
diff --git a/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/BannedImportsRule.kt b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/BannedImportsRule.kt
new file mode 100644
index 0000000000..d6fe598a7e
--- /dev/null
+++ b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/BannedImportsRule.kt
@@ -0,0 +1,74 @@
+// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import org.jetbrains.kotlin.psi.KtImportList
+
+class BannedImportsRule : Rule() {
+ override val issue = Issue("BannedImports", Severity.Defect, "Imports banned by the project", Debt.FIVE_MINS)
+
+ override fun visitImportList(importList: KtImportList) {
+ super.visitImportList(importList)
+ importList.imports.forEach { element ->
+ val importedFqName = element.importedFqName?.asString()
+ if (importedFqName == "org.assertj.core.api.Assertions") {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Import the assertion you want to use directly instead of importing the top level Assertions"
+ )
+ )
+ }
+
+ if (importedFqName?.startsWith("org.hamcrest") == true) {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Use AssertJ instead of Hamcrest assertions"
+ )
+ )
+ }
+
+ if (importedFqName?.startsWith("kotlin.test.assert") == true &&
+ importedFqName.startsWith("kotlin.test.assertNotNull") == false
+ ) {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Use AssertJ instead of Kotlin test assertions"
+ )
+ )
+ }
+
+ if (importedFqName?.contains("kotlinx.coroutines.Dispatchers") == true) {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Use contexts from contexts.kt instead of Dispatchers"
+ )
+ )
+ }
+
+ if (importedFqName == "com.intellij.ui.layout.panel") {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Use com.intellij.ui.dsl.builder.panel from Kotlin UI DSL Version 2"
+ )
+ )
+ }
+ }
+ }
+}
diff --git a/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/BannedPatternRule.kt b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/BannedPatternRule.kt
new file mode 100644
index 0000000000..a676bb754b
--- /dev/null
+++ b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/BannedPatternRule.kt
@@ -0,0 +1,55 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+@file:Suppress("BannedPattern")
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import org.jetbrains.kotlin.psi.KtFile
+
+class BannedPatternRule(private val patterns: List) : Rule() {
+ override val issue = Issue("BannedPattern", Severity.Defect, "Banned calls", Debt.FIVE_MINS)
+
+ override fun visitKtFile(file: KtFile) {
+ var offset = 0
+ file.text.split("\n").forEachIndexed { _, text ->
+ patterns.forEach { pattern ->
+ val match = pattern.regex.find(text) ?: return@forEach
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(file, offset + match.range.first),
+ message = pattern.message
+ )
+ )
+ }
+ // account for delimiter
+ offset += text.length + 1
+ }
+ }
+
+ companion object {
+ val DEFAULT_PATTERNS = listOf(
+ BannedPattern("Runtime\\.valueOf".toRegex(), "Runtime.valueOf is banned, use Runtime.fromValue instead."),
+ BannedPattern(
+ """com\.intellij\.openapi\.actionSystem\.DataKeys""".toRegex(),
+ "DataKeys is not available in all IDEs, use LangDataKeys instead"
+ ),
+ BannedPattern(
+ """PsiUtil\.getPsiFile""".toRegex(),
+ "PsiUtil (java-api.jar) is not available in all IDEs, use PsiManager.getInstance(project).findFile() instead"
+ ),
+ BannedPattern(
+ """com\.intellij\.psi\.util\.PsiUtil$""".toRegex(),
+ "PsiUtil (java-api.jar) is not available in all IDEs, use PsiUtilCore or PsiManager instead (platform-api.jar)"
+ )
+ )
+ }
+}
+
+data class BannedPattern(val regex: Regex, val message: String)
diff --git a/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/CustomRuleSetProvider.kt b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/CustomRuleSetProvider.kt
new file mode 100644
index 0000000000..2b8683719d
--- /dev/null
+++ b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/CustomRuleSetProvider.kt
@@ -0,0 +1,21 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.RuleSet
+import io.gitlab.arturbosch.detekt.api.RuleSetProvider
+
+class CustomRuleSetProvider : RuleSetProvider {
+ override val ruleSetId: String = "CustomDetektRules"
+ override fun instance(config: Config): RuleSet = RuleSet(
+ ruleSetId,
+ listOf(
+ BannedPatternRule(BannedPatternRule.DEFAULT_PATTERNS),
+ LazyLogRule(),
+ DialogModalityRule(),
+ BannedImportsRule()
+ )
+ )
+}
diff --git a/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/DialogModalityRule.kt b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/DialogModalityRule.kt
new file mode 100644
index 0000000000..8187f52610
--- /dev/null
+++ b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/DialogModalityRule.kt
@@ -0,0 +1,41 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtNameReferenceExpression
+import org.jetbrains.kotlin.psi.psiUtil.containingClass
+import org.jetbrains.kotlin.psi.psiUtil.getSuperNames
+
+class DialogModalityRule : Rule() {
+ override val issue = Issue("RunInEdtWithoutModalityInDialog", Severity.Defect, "Use ModalityState when calling runInEdt in dialogs", Debt.FIVE_MINS)
+
+ override fun visitCallExpression(element: KtCallExpression) {
+ super.visitCallExpression(element)
+ val callee = element.calleeExpression as? KtNameReferenceExpression ?: return
+ if (callee.getReferencedName() != "runInEdt") return
+ val clz = element.containingClass() ?: return
+ if (clz.getSuperNames().none { it in KNOWN_DIALOG_SUPER_TYPES }) return
+
+ if (element.valueArguments.none { it.text == "ModalityState.any()" }) {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Call to runInEdt without ModalityState.any() within Dialog will not run until Dialog exits."
+ )
+ )
+ }
+ }
+
+ companion object {
+ private val KNOWN_DIALOG_SUPER_TYPES = setOf("DialogWrapper")
+ }
+}
diff --git a/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/LazyLogRule.kt b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/LazyLogRule.kt
new file mode 100644
index 0000000000..ec66b51363
--- /dev/null
+++ b/detekt-rules/src/software/aws/toolkits/gradle/detekt/rules/LazyLogRule.kt
@@ -0,0 +1,64 @@
+// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
+import io.gitlab.arturbosch.detekt.rules.fqNameOrNull
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.calls.util.getReceiverExpression
+import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
+
+@RequiresTypeResolution
+class LazyLogRule : Rule() {
+ override val issue = Issue("LazyLog", Severity.Style, "Use lazy logging syntax (e.g. warning {\"abc\"} ) instead of warning(\"abc\")", Debt.FIVE_MINS)
+
+ // UI tests have issues with this TODO see if we want multiple detekt.yml files or disable for certain modules in this rule
+ private val optOut = setOf("software.aws.toolkits.jetbrains.uitests")
+
+ override fun visitCallExpression(element: KtCallExpression) {
+ super.visitCallExpression(element)
+ element.getCallNameExpression()?.let {
+ if (!logMethods.contains(it.text)) {
+ return
+ }
+
+ if (optOut.any { name -> element.containingKtFile.packageFqName.asString().startsWith(name) }) {
+ return
+ }
+
+ if (bindingContext == BindingContext.EMPTY) return
+ val resolvedCall = it.getResolvedCall(bindingContext)
+ val type = resolvedCall?.extensionReceiver?.type?.fqNameOrNull()?.asString()
+ ?: resolvedCall?.dispatchReceiver?.type?.fqNameOrNull()?.asString()
+
+ if (type !in loggers) {
+ return
+ }
+
+ if (element.lambdaArguments.size != 1) {
+ val receiverName = resolvedCall?.getReceiverExpression()?.text ?: type
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(element),
+ message = "Use the lambda version of $receiverName.${it.text} instead"
+ )
+ )
+ }
+ }
+ }
+
+ companion object {
+ private val logMethods = setOf("error", "warn", "info", "debug", "trace")
+ val loggers = setOf("org.slf4j.Logger")
+ }
+}
diff --git a/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/BannedImportsRuleTest.kt b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/BannedImportsRuleTest.kt
new file mode 100644
index 0000000000..5370420f75
--- /dev/null
+++ b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/BannedImportsRuleTest.kt
@@ -0,0 +1,60 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.test.lint
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+
+class BannedImportsRuleTest {
+ private val rule = BannedImportsRule()
+
+ @Test
+ fun `Importing Assert fails`() {
+ assertThat(rule.lint("import org.assertj.core.api.Assertions"))
+ .singleElement()
+ .matches { it.id == "BannedImports" && it.message == "Import the assertion you want to use directly instead of importing the top level Assertions" }
+ }
+
+ @Test
+ fun `Importing Hamcrest fails`() {
+ assertThat(rule.lint("import org.hamcrest.AnyClass"))
+ .singleElement()
+ .matches { it.id == "BannedImports" && it.message == "Use AssertJ instead of Hamcrest assertions" }
+ }
+
+ @Test
+ fun `Importing Kotlin test assert fails`() {
+ assertThat(rule.lint("import kotlin.test.assertTrue"))
+ .singleElement()
+ .matches { it.id == "BannedImports" && it.message == "Use AssertJ instead of Kotlin test assertions" }
+ assertThat(rule.lint("import kotlin.test.assertFalse"))
+ .singleElement()
+ .matches { it.id == "BannedImports" && it.message == "Use AssertJ instead of Kotlin test assertions" }
+ }
+
+ @Test
+ fun `Importing kotlin test notNull succeeds`() {
+ assertThat(rule.lint("import kotlin.test.assertNotNull")).isEmpty()
+ }
+
+ @Test
+ fun `Importing Assert assertThat succeeds`() {
+ assertThat(rule.lint("import org.assertj.core.api.Assertions.assertThat")).isEmpty()
+ }
+
+ @Test
+ fun `Importing Dispatchers fails`() {
+ assertThat(rule.lint("import kotlinx.coroutines.Dispatchers"))
+ .singleElement()
+ .matches { it.id == "BannedImports" && it.message == "Use contexts from contexts.kt instead of Dispatchers" }
+ }
+
+ @Test
+ fun `Importing Dispatchers statically fails`() {
+ assertThat(rule.lint("import kotlinx.coroutines.Dispatchers.IO"))
+ .singleElement()
+ .matches { it.id == "BannedImports" && it.message == "Use contexts from contexts.kt instead of Dispatchers" }
+ }
+}
diff --git a/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/BannedPatternRuleTest.kt b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/BannedPatternRuleTest.kt
new file mode 100644
index 0000000000..8c3b99d537
--- /dev/null
+++ b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/BannedPatternRuleTest.kt
@@ -0,0 +1,80 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+@file:Suppress("BannedPattern")
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.test.lint
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+
+class BannedPatternRuleTest {
+ @Test
+ fun classContainingRegexCreatesError() {
+ val rule = BannedPatternRule(listOf(BannedPattern("""blah\(\)""".toRegex(), "Use of method blah() is banned.")))
+ assertThat(
+ rule.lint(
+ """
+ fun hello() {
+ blah()
+ }
+ """.trimIndent()
+ )
+ )
+ .singleElement()
+ .matches {
+ it.id == "BannedPattern" &&
+ it.message == "Use of method blah() is banned." &&
+ it.location.source.line == 2 &&
+ it.location.source.column == 5
+ }
+ }
+
+ @Test
+ fun forbidPsiUtil() {
+ val rule = BannedPatternRule(BannedPatternRule.DEFAULT_PATTERNS)
+ assertThat(
+ rule.lint(
+ """
+ import com.intellij.psi.util.PsiUtil
+ class DockerfileParser(private val project: Project) {
+ fun parse(virtualFile: VirtualFile): DockerfileDetails? {
+ val psiFile = PsiUtil.getPsiFile(project, virtualFile)
+ }
+ }
+ """.trimIndent()
+ )
+ )
+ .hasSize(2)
+ .anyMatch {
+ it.id == "BannedPattern" &&
+ it.message == "PsiUtil (java-api.jar) is not available in all IDEs, use PsiUtilCore or PsiManager instead (platform-api.jar)" &&
+ it.location.source.line == 1 &&
+ it.location.source.column == 8
+ }
+ .anyMatch {
+ it.id == "BannedPattern" &&
+ it.message == "PsiUtil (java-api.jar) is not available in all IDEs, use PsiManager.getInstance(project).findFile() instead" &&
+ it.location.source.line == 4 &&
+ it.location.source.column == 23
+ }
+ }
+
+ @Test
+ fun allowPsiUtilCore() {
+ val rule = BannedPatternRule(BannedPatternRule.DEFAULT_PATTERNS)
+ assertThat(
+ rule.lint(
+ """
+ import com.intellij.psi.util.PsiUtilCore
+ class DockerfileParser(private val project: Project) {
+ fun parse(virtualFile: VirtualFile): DockerfileDetails? {
+ val psiFile = PsiUtilCore.getPsiFile(project, virtualFile)
+ }
+ }
+ """.trimIndent()
+ )
+ )
+ .hasSize(0)
+ }
+}
diff --git a/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/DialogModalityRuleTest.kt b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/DialogModalityRuleTest.kt
new file mode 100644
index 0000000000..09c3f409d8
--- /dev/null
+++ b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/DialogModalityRuleTest.kt
@@ -0,0 +1,57 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.gitlab.arturbosch.detekt.test.lint
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+
+class DialogModalityRuleTest {
+ private val rule = DialogModalityRule()
+
+ @Test
+ fun runInEdtCallsShouldSpecifyModalityWhenCalledWithinDialog() {
+ val code = """
+ class Blah : DialogWrapper {
+ fun blah() {
+ runInEdt { }
+ }
+ }
+ """
+ assertThat(rule.lint(code)).singleElement()
+ .matches {
+ it.id == "RunInEdtWithoutModalityInDialog" &&
+ it.message == "Call to runInEdt without ModalityState.any() within Dialog will not run until Dialog exits."
+ }
+ }
+
+ @Test
+ fun callsThatSpecifyModalityAnyAreFine() {
+ val code = """
+ class Blah : DialogWrapper {
+ fun blah() {
+ runInEdt(ModalityState.any()) { }
+ }
+ }
+ """.trimIndent()
+ assertThat(rule.lint(code)).isEmpty()
+ }
+
+ @Test
+ fun callsThatSpecifyWrongModalityAreNotFine() {
+ val code = """
+ class Blah : DialogWrapper() {
+ fun blah() {
+ runInEdt(ModalityState.current()) { }
+ }
+ }
+ """
+
+ assertThat(rule.lint(code)).singleElement()
+ .matches {
+ it.id == "RunInEdtWithoutModalityInDialog" &&
+ it.message == "Call to runInEdt without ModalityState.any() within Dialog will not run until Dialog exits."
+ }
+ }
+}
diff --git a/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/LazyLogRuleTest.kt b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/LazyLogRuleTest.kt
new file mode 100644
index 0000000000..a9b0835414
--- /dev/null
+++ b/detekt-rules/tst/software/aws/toolkits/gradle/detekt/rules/LazyLogRuleTest.kt
@@ -0,0 +1,93 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.gradle.detekt.rules
+
+import io.github.detekt.test.utils.createEnvironment
+import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+import java.io.File
+
+class LazyLogRuleTest {
+ private val rule = LazyLogRule()
+ private val environment = createEnvironment(
+ additionalRootPaths = LazyLogRule.loggers.map {
+ File(Class.forName(it).protectionDomain.codeSource.location.path)
+ }
+ ).env
+
+ @Test
+ fun lambdaIsUsedToLog() {
+ assertThat(
+ rule.compileAndLintWithContext(
+ environment,
+ """
+import org.slf4j.LoggerFactory
+
+val LOG = LoggerFactory.getLogger("")
+fun foo() {
+ LOG.debug { "Hi" }
+}
+ """.trimIndent()
+ )
+ ).isEmpty()
+ }
+
+ @Test
+ fun methodCallIsUsedToLog() {
+ assertThat(
+ rule.compileAndLintWithContext(
+ environment,
+ """
+import org.slf4j.LoggerFactory
+
+val LOG = LoggerFactory.getLogger("")
+fun foo() {
+ LOG.debug("Hi")
+}
+ """.trimIndent()
+ )
+ ).singleElement()
+ .matches {
+ it.id == "LazyLog" && it.message == "Use the lambda version of LOG.debug instead"
+ }
+ }
+
+ @Test
+ fun lambdaIsUsedToLogButWithException() {
+ assertThat(
+ rule.compileAndLintWithContext(
+ environment,
+ """
+import org.slf4j.LoggerFactory
+
+val LOG = LoggerFactory.getLogger("")
+fun foo() {
+ val e = RuntimeException()
+ LOG.debug(e) {"Hi" }
+}
+ """.trimIndent()
+ )
+ ).isEmpty()
+ }
+
+ @Test
+ fun methodCallIsUsedToLogInUiTests() {
+ assertThat(
+ rule.compileAndLintWithContext(
+ environment,
+ """
+package software.aws.toolkits.jetbrains.uitests.really.cool.test
+
+import org.slf4j.LoggerFactory
+
+val LOG = LoggerFactory.getLogger("")
+fun foo() {
+ LOG.debug("Hi")
+}
+ """.trimIndent()
+ )
+ ).isEmpty()
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 7017be1642..7e7f0c1522 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,31 +2,15 @@
# SPDX-License-Identifier: Apache-2.0
# Toolkit Version
-toolkitVersion=1.18-SNAPSHOT
+toolkitVersion=2.3-SNAPSHOT
# Publish Settings
publishToken=
publishChannel=
-# Common dependencies
-ideProfileName=2019.3
-kotlinVersion=1.3.70
-awsSdkVersion=2.13.58
-coroutinesVersion=1.3.3
-ideaPluginVersion=0.4.20
-ktlintVersion=0.36.0
-jacksonVersion=2.9.8
-telemetryVersion=0.0.34
-
-assertjVersion=3.15.0
-junitVersion=4.12
-junit5Version=5.6.2
-mockitoKotlinVersion=2.2.0
-mockitoVersion=3.4.0
+ideProfileName=2023.3
remoteRobotPort=8080
-remoteRobotVersion=0.9.35
-uiTestFixturesVersion=1.1.18
# Code style
kotlin.code.style=official
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000000..b9b13c374f
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,111 @@
+[versions]
+apacheCommons = "2.8.0"
+assertJ = "3.20.2" # Upgrading leads to SAM errors: https://youtrack.jetbrains.com/issue/KT-17765
+# match with /settings.gradle.kts
+awsSdk = "2.20.111"
+commonmark = "0.17.1"
+detekt = "1.23.0"
+intellijGradle = "1.13.2"
+intellijRemoteRobot = "0.11.18"
+jackson = "2.15.1"
+jacoco = "0.8.11"
+jgit = "6.5.0.202303070854-r"
+junit4 = "4.13.2"
+junit5 = "5.10.1"
+# https://plugins.jetbrains.com/docs/intellij/kotlin.html#adding-kotlin-support
+# https://kotlinlang.org/docs/releases.html#release-details
+kotlin = "1.9.21"
+# set in /settings.gradle.kts
+kotlinCoroutines = "1.7.3"
+mockito = "4.6.1"
+mockitoKotlin = "4.0.0"
+mockk = "1.13.8"
+node-gradle = "7.0.1"
+telemetryGenerator = "1.0.176"
+testLogger = "3.1.0"
+testRetry = "1.5.2"
+slf4j = "1.7.36"
+sshd = "2.11.0"
+wiremock = "2.35.0"
+zjsonpatch = "0.4.11"
+
+[libraries]
+assertj = { module = "org.assertj:assertj-core", version.ref = "assertJ" }
+aws-apacheClient = { module = "software.amazon.awssdk:apache-client", version.ref = "awsSdk" }
+aws-apprunner = { module = "software.amazon.awssdk:apprunner", version.ref = "awsSdk" }
+aws-bom = { module = "software.amazon.awssdk:bom", version.ref = "awsSdk" }
+aws-cloudcontrol = { module = "software.amazon.awssdk:cloudcontrol", version.ref = "awsSdk" }
+aws-cloudformation = { module = "software.amazon.awssdk:cloudformation", version.ref = "awsSdk" }
+aws-cloudwatchlogs = { module = "software.amazon.awssdk:cloudwatchlogs", version.ref = "awsSdk" }
+aws-codecatalyst = { module = "software.amazon.awssdk:codecatalyst", version.ref = "awsSdk" }
+aws-codeGen = { module = "software.amazon.awssdk:codegen", version.ref = "awsSdk" }
+aws-cognitoidentity = { module = "software.amazon.awssdk:cognitoidentity", version.ref = "awsSdk" }
+aws-crt = { module = "software.amazon.awssdk:aws-crt-client", version.ref = "awsSdk" }
+aws-dynamodb = { module = "software.amazon.awssdk:dynamodb", version.ref = "awsSdk" }
+aws-ec2 = { module = "software.amazon.awssdk:ec2", version.ref = "awsSdk" }
+aws-ecr = { module = "software.amazon.awssdk:ecr", version.ref = "awsSdk" }
+aws-ecs = { module = "software.amazon.awssdk:ecs", version.ref = "awsSdk" }
+aws-iam = { module = "software.amazon.awssdk:iam", version.ref = "awsSdk" }
+aws-jsonProtocol = { module = "software.amazon.awssdk:aws-json-protocol", version.ref = "awsSdk" }
+aws-lambda = { module = "software.amazon.awssdk:lambda", version.ref = "awsSdk" }
+aws-queryProtocol = { module = "software.amazon.awssdk:aws-query-protocol", version.ref = "awsSdk" }
+aws-rds = { module = "software.amazon.awssdk:rds", version.ref = "awsSdk" }
+aws-redshift = { module = "software.amazon.awssdk:redshift", version.ref = "awsSdk" }
+aws-s3 = { module = "software.amazon.awssdk:s3", version.ref = "awsSdk" }
+aws-schemas = { module = "software.amazon.awssdk:schemas", version.ref = "awsSdk" }
+aws-secretsmanager = { module = "software.amazon.awssdk:secretsmanager", version.ref = "awsSdk" }
+aws-services = { module = "software.amazon.awssdk:services", version.ref = "awsSdk" }
+aws-sns = { module = "software.amazon.awssdk:sns", version.ref = "awsSdk" }
+aws-sqs = { module = "software.amazon.awssdk:sqs", version.ref = "awsSdk" }
+aws-sso = { module = "software.amazon.awssdk:sso", version.ref = "awsSdk" }
+aws-ssooidc = { module = "software.amazon.awssdk:ssooidc", version.ref = "awsSdk" }
+aws-sts = { module = "software.amazon.awssdk:sts", version.ref = "awsSdk" }
+commonmark = { module = "org.commonmark:commonmark", version.ref = "commonmark" }
+commons-io = { module = "commons-io:commons-io", version.ref = "apacheCommons" }
+detekt-api = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" }
+detekt-formattingRules = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
+detekt-test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" }
+gradlePlugin-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
+gradlePlugin-intellij = { module = "org.jetbrains.intellij:org.jetbrains.intellij.gradle.plugin", version.ref = "intellijGradle" }
+gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
+gradlePlugin-testLogger = { module = "com.adarshr:gradle-test-logger-plugin", version.ref = "testLogger" }
+gradlePlugin-testRetry = { module = "org.gradle:test-retry-gradle-plugin", version.ref = "testRetry" }
+intellijRemoteFixtures = { module = "com.intellij.remoterobot:remote-fixtures", version.ref = "intellijRemoteRobot" }
+intellijRemoteRobot = { module = "com.intellij.remoterobot:remote-robot", version.ref = "intellijRemoteRobot" }
+jackson-datetime = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" }
+jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
+jackson-xml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-xml", version.ref = "jackson" }
+jackson-yaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jackson" }
+jacoco = { module = "org.jacoco:org.jacoco.core", version.ref = "jacoco" }
+jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version.ref = "jgit" }
+junit4 = { module = "junit:junit", version.ref = "junit4" }
+junit5-bom = { module = "org.junit:junit-bom", version.ref = "junit5" }
+junit5-jupiterApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit5" }
+junit5-jupiterEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" }
+junit5-jupiterVintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" }
+kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinCoroutines" }
+kotlin-coroutinesDebug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-debug", version.ref = "kotlinCoroutines" }
+kotlin-coroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinCoroutines" }
+kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
+kotlin-stdLibJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
+kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
+mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
+mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" }
+mockk = { module = "io.mockk:mockk", version.ref="mockk" }
+telemetryGenerator = { module = "software.aws.toolkits:telemetry-generator", version.ref = "telemetryGenerator" }
+slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
+sshd-core = { module = "org.apache.sshd:sshd-core", version.ref = "sshd" }
+sshd-scp = { module = "org.apache.sshd:sshd-scp", version.ref = "sshd" }
+sshd-sftp = { module = "org.apache.sshd:sshd-sftp", version.ref = "sshd" }
+wiremock = { module = "com.github.tomakehurst:wiremock-jre8", version.ref = "wiremock" }
+zjsonpatch = { module = "com.flipkart.zjsonpatch:zjsonpatch", version.ref = "zjsonpatch" }
+
+[bundles]
+jackson = ["jackson-datetime", "jackson-kotlin", "jackson-yaml", "jackson-xml"]
+kotlin = ["kotlin-stdLibJdk8", "kotlin-reflect"]
+mockito = ["mockito-core", "mockito-kotlin"]
+sshd = ["sshd-core", "sshd-scp", "sshd-sftp"]
+
+[plugins]
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+node-gradle = { id = "com.github.node-gradle.node", version.ref = "node-gradle" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index f3d88b1c2f..ccebba7710 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 186b71557c..bdc9a83b1e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
+networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 2fe81a7d95..79a61d421c 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,78 +17,113 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -105,79 +140,105 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=`expr $i + 1`
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 24467a141f..6689b85bee 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,10 +25,14 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@@ -37,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -51,7 +55,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -61,38 +65,26 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/intellij/build.gradle.kts b/intellij/build.gradle.kts
new file mode 100644
index 0000000000..255b557277
--- /dev/null
+++ b/intellij/build.gradle.kts
@@ -0,0 +1,86 @@
+// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import software.aws.toolkits.gradle.intellij.IdeVersions
+
+plugins {
+ id("org.jetbrains.intellij")
+ id("toolkit-testing") // Needed so the coverage configurations are present
+ id("toolkit-detekt")
+}
+
+val ideProfile = IdeVersions.ideProfile(project)
+
+val toolkitVersion: String by project
+val publishToken: String by project
+val publishChannel: String by project
+
+// please check changelog generation logic if this format is changed
+// also sync with gateway version
+version = "$toolkitVersion-${ideProfile.shortName}"
+
+val resharperDlls = configurations.create("resharperDlls") {
+ isCanBeConsumed = false
+}
+
+val gatewayResources = configurations.create("gatewayResources") {
+ isCanBeConsumed = false
+}
+
+intellij {
+ pluginName.set("aws-toolkit-jetbrains")
+
+ version.set(ideProfile.community.version())
+ localPath.set(ideProfile.community.localPath())
+
+ updateSinceUntilBuild.set(false)
+ instrumentCode.set(false)
+}
+
+tasks.prepareSandbox {
+ from(resharperDlls) {
+ into("aws-toolkit-jetbrains/dotnet")
+ }
+ from(gatewayResources) {
+ into("aws-toolkit-jetbrains/gateway-resources")
+ }
+}
+
+tasks.publishPlugin {
+ token.set(publishToken)
+ channels.set(publishChannel.split(",").map { it.trim() })
+}
+
+tasks.check {
+ dependsOn(tasks.verifyPlugin)
+}
+
+// We have no source in this project, so skip test task
+tasks.test {
+ enabled = false
+}
+
+dependencies {
+ implementation(project(":jetbrains-core", "instrumentedJar"))
+ implementation(project(":jetbrains-ultimate", "instrumentedJar"))
+ project.findProject(":jetbrains-gateway")?.let {
+ // does this need to be the instrumented variant?
+ implementation(it)
+ gatewayResources(project(":jetbrains-gateway", configuration = "gatewayResources"))
+ }
+ project.findProject(":jetbrains-rider")?.let {
+ // does this need to be the instrumented variant?
+ implementation(it)
+ resharperDlls(project(":jetbrains-rider", configuration = "resharperDlls"))
+ }
+}
+
+configurations {
+ // Make sure we exclude stuff we either A) ships with IDE, B) we don't use to cut down on size
+ runtimeClasspath {
+ exclude(group = "org.slf4j")
+ exclude(group = "org.jetbrains.kotlin")
+ exclude(group = "org.jetbrains.kotlinx")
+ exclude(group = "software.amazon.awssdk", module = "netty-nio-client")
+ }
+}
diff --git a/intellijJVersions.gradle b/intellijJVersions.gradle
deleted file mode 100644
index 92e1558420..0000000000
--- a/intellijJVersions.gradle
+++ /dev/null
@@ -1,207 +0,0 @@
-static def ideProfiles() {
- return [
- "2019.3": [
- "sinceVersion": "193",
- "untilVersion": "193.*",
- "products" : [
- "IC": [
- sdkVersion: "IC-2019.3",
- plugins : [
- "org.jetbrains.plugins.terminal",
- "org.jetbrains.plugins.yaml",
- "PythonCore:193.5233.139",
- "java",
- "com.intellij.gradle",
- "org.jetbrains.idea.maven",
- "Docker:193.5233.140"
- ]
- ],
- "IU": [
- sdkVersion: "IU-2019.3",
- plugins : [
- "org.jetbrains.plugins.terminal",
- "Pythonid:193.5233.109",
- "org.jetbrains.plugins.yaml",
- "JavaScript",
- "JavaScriptDebugger",
- ]
- ],
- "RD": [
- sdkVersion : "RD-2019.3.4",
- rdGenVersion: "0.193.146",
- nugetVersion: "2019.3.4",
- plugins : [
- "org.jetbrains.plugins.yaml"
- ]
- ]
- ]
- ],
- "2020.1": [
- "sinceVersion": "201",
- "untilVersion": "201.*",
- "products" : [
- "IC": [
- sdkVersion: "IC-2020.1",
- plugins : [
- "org.jetbrains.plugins.terminal",
- "org.jetbrains.plugins.yaml",
- "PythonCore:201.6668.31",
- "java",
- "com.intellij.gradle",
- "org.jetbrains.idea.maven",
- "Docker:201.6668.30"
- ]
- ],
- "IU": [
- sdkVersion: "IU-2020.1",
- plugins : [
- "org.jetbrains.plugins.terminal",
- "Pythonid:201.6668.31",
- "org.jetbrains.plugins.yaml",
- "JavaScript",
- "JavaScriptDebugger",
- "com.intellij.database",
- ]
- ],
- "RD": [
- sdkVersion : "RD-2020.1.0",
- rdGenVersion: "0.201.69",
- nugetVersion: "2020.1.0",
- plugins : [
- "org.jetbrains.plugins.yaml"
- ]
- ]
- ]
- ],
- "2020.2": [
- "sinceVersion": "202",
- "untilVersion": "202.*",
- "products" : [
- "IC": [
- sdkVersion: "IC-202.6250.13-EAP-SNAPSHOT",
- plugins : [
- "org.jetbrains.plugins.terminal",
- "org.jetbrains.plugins.yaml",
- "PythonCore:202.6250.13",
- "java",
- "com.intellij.gradle",
- "org.jetbrains.idea.maven",
- "Docker:202.6250.6"
- ]
- ],
- "IU": [
- sdkVersion: "IU-202.6250.13-EAP-SNAPSHOT",
- plugins : [
- "org.jetbrains.plugins.terminal",
- "Pythonid:202.6250.13",
- "org.jetbrains.plugins.yaml",
- "JavaScript",
- "JavaScriptDebugger",
- "com.intellij.database",
- ]
- ],
- "RD": [
- sdkVersion : "RD-2020.2-SNAPSHOT",
- rdGenVersion: "0.202.113",
- nugetVersion: "2020.2.0-eap07",
- plugins : [
- "org.jetbrains.plugins.yaml"
- ]
- ]
- ]
- ]
- ]
-}
-
-def idePlugins(String productCode) {
- return ideProduct(productCode).plugins
-}
-
-def ideSdkVersion(String productCode) {
- return ideProduct(productCode).sdkVersion
-}
-
-private def ideProduct(String productCode) {
- def product = ideProfile()["products"][productCode]
- if (product == null) {
- throw new IllegalArgumentException("Unknown IDE product `$productCode` for ${resolveIdeProfileName()}")
- }
- return product
-}
-
-def ideSinceVersion() {
- def guiVersion = ideProfile()["sinceVersion"]
- if (guiVersion == null) {
- throw new IllegalArgumentException("Missing 'sinceVersion' key for ${resolveIdeProfileName()}")
- }
- return guiVersion
-}
-
-def ideUntilVersion() {
- def guiVersion = ideProfile()["untilVersion"]
- if (guiVersion == null) {
- throw new IllegalArgumentException("Missing 'untilVersion' key for ${resolveIdeProfileName()}")
- }
- return guiVersion
-}
-
-// https://www.myget.org/feed/rd-snapshots/package/maven/com.jetbrains.rd/rd-gen
-def rdGenVersion() {
- def rdGen = ideProduct("RD").rdGenVersion
- if (rdGen == null) {
- throw new IllegalArgumentException("Missing 'rdGenVersion' in 'RD' product for ${resolveIdeProfileName()}")
- }
- return rdGen
-}
-
-// https://www.nuget.org/packages/JetBrains.Rider.SDK/
-def riderNugetSdkVersion() {
- def rdGen = ideProduct("RD").nugetVersion
- if (rdGen == null) {
- throw new IllegalArgumentException("Missing 'nugetVersion' in 'RD' product for ${resolveIdeProfileName()}")
- }
- return rdGen
-}
-
-def ideProfile() {
- def profileName = resolveIdeProfileName()
- def profile = ideProfiles()[profileName]
- if (profile == null) {
- throw new IllegalArgumentException("Unknown ideProfile `$profileName`")
- }
-
- return profile
-}
-
-def resolveIdeProfileName() {
- if (System.env.ALTERNATIVE_IDE_PROFILE_NAME) {
- return System.env.ALTERNATIVE_IDE_PROFILE_NAME
- }
-
- return project.ideProfileName
-}
-
-
-static def shortenVersion(String ver) {
- try {
- def result = ver =~ /^\d\d(\d{2})[\\.](\d)/
- if (result) {
- return result.group(1) + result.group(2)
- }
- } catch (Exception ignored) {
- }
- return ver
-}
-
-ext {
- ideProfiles = this.&ideProfiles
- idePlugins = this.&idePlugins
- ideSdkVersion = this.&ideSdkVersion
- ideSinceVersion = this.&ideSinceVersion
- ideUntilVersion = this.&ideUntilVersion
- ideProfile = this.&ideProfile
- rdGenVersion = this.&rdGenVersion
- riderNugetSdkVersion = this.&riderNugetSdkVersion
- resolveIdeProfileName = this.&resolveIdeProfileName
- shortenVersion = this.&shortenVersion
-}
diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts
index aa9998b3a9..5b063f9271 100644
--- a/jetbrains-core/build.gradle.kts
+++ b/jetbrains-core/build.gradle.kts
@@ -1,72 +1,48 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-import groovy.lang.Closure
-import org.gradle.jvm.tasks.Jar
-import org.jetbrains.intellij.IntelliJPluginExtension
-import org.jetbrains.intellij.tasks.PatchPluginXmlTask
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
+import software.aws.toolkits.gradle.buildMetadata
+import software.aws.toolkits.gradle.changelog.tasks.GeneratePluginChangeLog
+import software.aws.toolkits.gradle.intellij.IdeFlavor
+import software.aws.toolkits.gradle.intellij.IdeVersions
+import software.aws.toolkits.gradle.isCi
import software.aws.toolkits.telemetry.generator.gradle.GenerateTelemetry
-import toolkits.gradle.changelog.tasks.GeneratePluginChangeLog
-// Cannot be removed or else it will fail to compile
-import org.jetbrains.intellij.IntelliJPlugin
+
+val toolkitVersion: String by project
+val ideProfile = IdeVersions.ideProfile(project)
plugins {
- id("org.jetbrains.intellij")
+ id("toolkit-kotlin-conventions")
+ id("toolkit-testing")
+ id("toolkit-integration-testing")
+ id("toolkit-intellij-subplugin")
}
-apply(from = "../intellijJVersions.gradle")
buildscript {
- val telemetryVersion: String by project
- repositories {
- mavenCentral()
- maven { setUrl("https://jitpack.io") }
- }
dependencies {
- classpath("software.aws.toolkits:telemetry-generator:$telemetryVersion")
+ classpath(libs.telemetryGenerator)
}
}
-val telemetryVersion: String by project
-val awsSdkVersion: String by project
-val coroutinesVersion: String by project
-
-val ideSdkVersion: Closure by ext
-val idePlugins: Closure> by ext
-val ideSinceVersion: Closure by ext
-val ideUntilVersion: Closure by ext
-
-val compileKotlin: KotlinCompile by tasks
-val patchPluginXml: PatchPluginXmlTask by tasks
-
-intellij {
- val rootIntelliJTask = rootProject.intellij
- version = ideSdkVersion("IC")
- setPlugins(*(idePlugins("IC").toArray()))
- pluginName = rootIntelliJTask.pluginName
- updateSinceUntilBuild = rootIntelliJTask.updateSinceUntilBuild
- downloadSources = rootIntelliJTask.downloadSources
+intellijToolkit {
+ ideFlavor.set(IdeFlavor.IC)
}
-patchPluginXml.setSinceBuild(ideSinceVersion())
-patchPluginXml.setUntilBuild(ideUntilVersion())
-
-configurations {
- testArtifacts
+sourceSets {
+ main {
+ java.srcDir("${project.buildDir}/generated-src")
+ }
}
val generateTelemetry = tasks.register("generateTelemetry") {
- inputFiles = listOf()
+ inputFiles = listOf(file("${project.projectDir}/resources/telemetryOverride.json"))
outputDirectory = file("${project.buildDir}/generated-src")
}
-compileKotlin.dependsOn(generateTelemetry)
-sourceSets {
- main.get().java.srcDir("${project.buildDir}/generated-src")
-}
-
-tasks.test {
- systemProperty("log.dir", "${project.intellij.sandboxDirectory}-test/logs")
+tasks.compileKotlin {
+ dependsOn(generateTelemetry)
}
val changelog = tasks.register("pluginChangeLog") {
@@ -76,32 +52,109 @@ val changelog = tasks.register("pluginChangeLog") {
tasks.jar {
dependsOn(changelog)
- archiveBaseName.set("aws-intellij-toolkit-core")
- from(changelog.get().changeLogFile) {
+ from(changelog) {
into("META-INF")
}
}
+val gatewayPluginXml = tasks.create("patchPluginXmlForGateway") {
+ pluginXmlFiles.set(tasks.patchPluginXml.map { it.pluginXmlFiles }.get())
+ destinationDir.set(project.buildDir.resolve("patchedPluginXmlFilesGW"))
+
+ val buildSuffix = if (!project.isCi()) "+${buildMetadata()}" else ""
+ version.set("GW-$toolkitVersion-${ideProfile.shortName}$buildSuffix")
+}
+
+val gatewayArtifacts by configurations.creating {
+ isCanBeConsumed = true
+ isCanBeResolved = false
+ // share same dependencies as default configuration
+ extendsFrom(configurations["implementation"], configurations["runtimeOnly"])
+}
+
+val gatewayJar = tasks.create("gatewayJar") {
+ dependsOn(tasks.instrumentedJar)
+
+ archiveBaseName.set("aws-toolkit-jetbrains-IC-GW")
+ from(tasks.instrumentedJar.get().outputs.files.map { zipTree(it) }) {
+ exclude("**/plugin.xml")
+ exclude("**/plugin-intellij.xml")
+ exclude("**/inactive")
+ }
+
+ from(gatewayPluginXml) {
+ into("META-INF")
+ }
+
+ val pluginGateway = sourceSets.main.get().resources.first { it.name == "plugin-gateway.xml" }
+ from(pluginGateway) {
+ into("META-INF")
+ }
+}
+
+artifacts {
+ add("gatewayArtifacts", gatewayJar)
+}
+
+tasks.prepareSandbox {
+ // you probably do not want to modify this.
+ // this affects the IDE sandbox / build for `:jetbrains-core`, but will not propogate to the build generated by `:intellij`
+ // (which is what is ultimately published to the marketplace)
+ // without additional effort
+}
+
+tasks.testJar {
+ // classpath.index is a duplicated
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
+}
+
+tasks.processTestResources {
+ // TODO how can we remove this. Fails due to:
+ // "customerUploadedEventSchemaMultipleTypes.json.txt is a duplicate but no duplicate handling strategy has been set"
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
+}
+
dependencies {
api(project(":core"))
- api("software.amazon.awssdk:s3:$awsSdkVersion")
- api("software.amazon.awssdk:lambda:$awsSdkVersion")
- api("software.amazon.awssdk:iam:$awsSdkVersion")
- api("software.amazon.awssdk:ecs:$awsSdkVersion")
- api("software.amazon.awssdk:cloudformation:$awsSdkVersion")
- api("software.amazon.awssdk:schemas:$awsSdkVersion")
- api("software.amazon.awssdk:cloudwatchlogs:$awsSdkVersion")
- api("software.amazon.awssdk:apache-client:$awsSdkVersion")
- api("software.amazon.awssdk:resourcegroupstaggingapi:$awsSdkVersion")
- api("software.amazon.awssdk:rds:$awsSdkVersion")
- api("software.amazon.awssdk:redshift:$awsSdkVersion")
- api("software.amazon.awssdk:secretsmanager:$awsSdkVersion")
+ api(libs.aws.apacheClient)
+ api(libs.aws.apprunner)
+ api(libs.aws.cloudcontrol)
+ api(libs.aws.cloudformation)
+ api(libs.aws.cloudwatchlogs)
+ api(libs.aws.codecatalyst)
+ api(libs.aws.dynamodb)
+ api(libs.aws.ec2)
+ api(libs.aws.ecr)
+ api(libs.aws.ecs)
+ api(libs.aws.iam)
+ api(libs.aws.lambda)
+ api(libs.aws.rds)
+ api(libs.aws.redshift)
+ api(libs.aws.s3)
+ api(libs.aws.schemas)
+ api(libs.aws.secretsmanager)
+ api(libs.aws.sns)
+ api(libs.aws.sqs)
+ api(libs.aws.services)
+
+ implementation(project(":mynah-ui"))
+ implementation(libs.aws.crt)
+ implementation(libs.bundles.jackson)
+ implementation(libs.zjsonpatch)
+ implementation(libs.commonmark)
testImplementation(project(path = ":core", configuration = "testArtifacts"))
- testImplementation("com.github.tomakehurst:wiremock-jre8:2.26.0")
- testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")
- testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-debug:$coroutinesVersion")
+ testImplementation(libs.mockk)
+ testImplementation(libs.kotlin.coroutinesTest)
+ testImplementation(libs.kotlin.coroutinesDebug)
+ testImplementation(libs.wiremock)
+}
+
+// fix implicit dependency on generated source
+tasks.withType {
+ dependsOn(generateTelemetry)
+}
- integrationTestImplementation("org.eclipse.jetty:jetty-servlet:9.4.15.v20190215")
- integrationTestImplementation("org.eclipse.jetty:jetty-proxy:9.4.15.v20190215")
+tasks.withType {
+ dependsOn(generateTelemetry)
}
diff --git a/jetbrains-core/detekt-baseline-integrationTest.xml b/jetbrains-core/detekt-baseline-integrationTest.xml
new file mode 100644
index 0000000000..6476cce61e
--- /dev/null
+++ b/jetbrains-core/detekt-baseline-integrationTest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+