Repository: aws-cloudformation/rain (go)

Issue:
fmt adds newlines to multiline comments
I believe YAML technically does not have "multiline" comments, but below behavior strikes me as unintended.

```
---
Conditions:
  NeverValid: !Equals [True, False]

# Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
# tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
# quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
# consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
# cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
# proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Resources:
  NullResource:
    Type: 'Custom::NullResource'
    Condition: NeverValid
```

```
% diff -y null_template.yaml <(rain fmt null_template.yaml)
---                                                           <
Conditions:                                                     Conditions:
  NeverValid: !Equals [True, False]                               NeverValid: !Equals [True, False]

# Lorem ipsum dolor sit amet, consectetur adipiscing elit, se   # Lorem ipsum dolor sit amet, consectetur adipiscing elit, se
                                                              >
# tempor incididunt ut labore et dolore magna aliqua. Ut enim   # tempor incididunt ut labore et dolore magna aliqua. Ut enim
                                                              >
# quis nostrud exercitation ullamco laboris nisi ut aliquip e   # quis nostrud exercitation ullamco laboris nisi ut aliquip e
                                                              >
# consequat. Duis aute irure dolor in reprehenderit in volupt   # consequat. Duis aute irure dolor in reprehenderit in volupt
                                                              >
# cillum dolore eu fugiat nulla pariatur. Excepteur sint occa   # cillum dolore eu fugiat nulla pariatur. Excepteur sint occa
                                                              >
# proident, sunt in culpa qui officia deserunt mollit anim id   # proident, sunt in culpa qui officia deserunt mollit anim id
                                                              >
Resources:                                                      Resources:
  NullResource:                                                   NullResource:
    Type: 'Custom::NullResource'                              |     Type: Custom::NullResource
    Condition: NeverValid                                           Condition: NeverValid
```

Pull request description:
Bugfixes for 1.3 release
Fixes #46 and #57

Patch:
diff --git a/.gitignore b/.gitignore
index 19bb77f0..1c41d1c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ go.work
 main
 rain
 local/
+.idea
diff --git a/cft/format/format.go b/cft/format/format.go
index ec4404bb..1e35eb72 100644
--- a/cft/format/format.go
+++ b/cft/format/format.go
@@ -45,12 +45,33 @@ func String(t cft.Template, opt Options) string {
 
 	lastIndent := 0
 	indent := 0
+	lastPartWasComment := false
+	lastLineWasEmpty := false
 
 	for _, part := range parts {
-		indent = len(part) - len(strings.TrimLeft(part, " "))
 
-		// Add spaces between 1st and 2nd level properties
+		trimmedPart := strings.TrimLeft(part, " ")
+		indent = len(part) - len(trimmedPart)
+
+		isComment := false
+		if len(part) > 0 && strings.HasPrefix(trimmedPart, "#") {
+			isComment = true
+		}
+
+		isEmpty := len(part) == 0 // This should never be true
+
+		// Add spaces between 1st and 2nd level properties, except for comments
 		if indent <= lastIndent && (indent == 0 || indent == 2) {
+			if !lastPartWasComment {
+				// If the last line was a comment, don't newline here,
+				// since we want the comment to stick to the thing it was above
+				result.WriteString("\n")
+				lastLineWasEmpty = true
+			}
+		}
+
+		// Put a space above first/only comment lines
+		if !lastPartWasComment && isComment && !lastLineWasEmpty {
 			result.WriteString("\n")
 		}
 
@@ -58,6 +79,8 @@ func String(t cft.Template, opt Options) string {
 		result.WriteString("\n")
 
 		lastIndent = indent
+		lastPartWasComment = isComment
+		lastLineWasEmpty = isEmpty
 	}
 
 	out := strings.TrimSpace(result.String())
diff --git a/internal/aws/aws.go b/internal/aws/aws.go
index d28f85f3..a7115a75 100644
--- a/internal/aws/aws.go
+++ b/internal/aws/aws.go
@@ -87,6 +87,10 @@ func loadConfig(ctx context.Context, sessionName string) *aws.Config {
 		panic(errors.New("could not establish AWS credentials; please run 'aws configure' or choose a profile"))
 	}
 
+	if cfg.Region == "" {
+		panic(errors.New("a region was not specified. You can run 'aws configure' or choose a profile with a region"))
+	}
+
 	return &cfg
 }
 
diff --git a/internal/cmd/fmt/fmt.go b/internal/cmd/fmt/fmt.go
index dfc9516a..d1c42a8e 100644
--- a/internal/cmd/fmt/fmt.go
+++ b/internal/cmd/fmt/fmt.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"github.com/aws-cloudformation/rain/cft/format"
+	"github.com/aws-cloudformation/rain/internal/config"
 	"github.com/aws-cloudformation/rain/internal/console"
 	"github.com/aws-cloudformation/rain/internal/ui"
 
@@ -150,4 +151,5 @@ func init() {
 	Cmd.Flags().BoolVarP(&verifyFlag, "verify", "v", false, "Check if the input is already correctly formatted and exit.\nThe exit status will be 0 if so and 1 if not.")
 	Cmd.Flags().BoolVarP(&writeFlag, "write", "w", false, "Write the output back to the file rather than to stdout.")
 	Cmd.Flags().BoolVarP(&unsortedFlag, "unsorted", "u", false, "Do not sort the template's properties.")
+	Cmd.Flags().BoolVar(&config.Debug, "debug", false, "Output debugging information")
 }


Test patch:
diff --git a/cft/comments_test.go b/cft/comments_test.go
index 5c82b82a..909024a5 100644
--- a/cft/comments_test.go
+++ b/cft/comments_test.go
@@ -34,7 +34,6 @@ waldo: {}
 	}
 
 	expected := `# Comment about the doc
-
 foo: bar # Comment about foo:bar
 
 baz: # Comment about baz
@@ -42,6 +41,7 @@ baz: # Comment about baz
 
   xyzzy: # Comment about xyzzy
     - corge # Comment about corge
+
     # Comment about grault
     - grault: garply # Comment about grault:garply
 
diff --git a/cft/format/format_test.go b/cft/format/format_test.go
index fd27d701..212b43cf 100644
--- a/cft/format/format_test.go
+++ b/cft/format/format_test.go
@@ -18,6 +18,8 @@ Outputs:
         - Bucket2
         - Arn # Arn comment
 
+# Multiline comment
+# starting at indent 0
 Resources:
   Bucket2:
     Properties:
@@ -36,6 +38,8 @@ const expectedYaml = `Parameters:
   Name:
     Type: String
 
+# Multiline comment
+# starting at indent 0
 Resources:
   Bucket2:
     Type: AWS::S3::Bucket
@@ -62,6 +66,8 @@ const expectedYamlUnsorted = `Outputs:
   Bucket2: # Bucket comment
     Value: !GetAtt Bucket2.Arn # GetAtt comment Arn comment
 
+# Multiline comment
+# starting at indent 0
 Resources:
   Bucket2:
     Properties:
diff --git a/test/templates/comments.yaml b/test/templates/comments.yaml
new file mode 100644
index 00000000..513e5dbf
--- /dev/null
+++ b/test/templates/comments.yaml
@@ -0,0 +1,29 @@
+# Comment on the first line
+Parameters:
+  MyParam:
+    Type: String
+
+# Multi-line comment between sections before Resources
+# Starting at indent 0
+
+Resources:
+
+  # Comment by itself before MyBucket
+
+  MyBucket:
+    Type: AWS::S3::Bucket  # Comment at the end of line
+
+# Multi-line comment between resources 
+# Starting at indent 0
+# Between MyBucket and MyOtherBucket
+    
+  # Comment at the beginning of MyOtherBucket
+  MyOtherBucket:
+    # Comment in the middle of a resource
+    Type: AWS::S3::Bucket
+    # Multi line
+    # comment
+    Properties:
+      BucketName: asdf
+
+# Comment at the end
diff --git a/test/templates/format-test.yaml b/test/templates/format-test.yaml
new file mode 100644
index 00000000..092f844b
--- /dev/null
+++ b/test/templates/format-test.yaml
@@ -0,0 +1,24 @@
+Outputs:
+  Bucket1:
+    Value: !GetAtt Bucket1.Arn # Short GetAtt
+  Bucket2: # Bucket comment
+    Value:
+      Fn::GetAtt: # GetAtt comment
+        - Bucket2
+        - Arn # Arn comment
+
+# Multiline comment
+# starting at indent 0
+Resources:
+  Bucket2:
+    Properties:
+      BucketName: !Ref Name # Ref: comment
+    Type: "AWS::S3::Bucket"
+  Bucket1:
+    Type: AWS::S3::Bucket
+    Properties:
+      BucketName: !Sub ${Bucket2}-newer
+Parameters:
+  Name:
+    Type: String
+
diff --git a/test/templates/null.yaml b/test/templates/null.yaml
new file mode 100644
index 00000000..ad4f8a98
--- /dev/null
+++ b/test/templates/null.yaml
@@ -0,0 +1,14 @@
+Conditions:
+  NeverValid: !Equals [True, False]
+
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+# tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+# quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+# consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+# cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+# proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+Resources:
+  NullResource:
+    Type: 'Custom::NullResource'
+    Condition: NeverValid
+