Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support reStructuredText #60

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions cli/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ func (w jsonBlockWriter) Close() error {
func findBlocksInFile(fs afero.Fs, log *logrus.Logger, filename string, verbose, zeroTerminated, jsonOutput, fmtverbs bool, stdin io.Reader, stdout, stderr io.Writer) error {
var blockWriter blocks.BlockWriter

// nolint: gocritic
//nolint: gocritic
if zeroTerminated {
blockWriter = zeroTerminatedBlockWriter{
writer: stdout,
Expand All @@ -400,7 +400,7 @@ func findBlocksInFile(fs afero.Fs, log *logrus.Logger, filename string, verbose,
ReadOnly: true,
LineRead: blocks.ReaderIgnore,
BlockWriter: blockWriter,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, preserveIndent bool) error {
if fmtverbs {
b = verbs.Escape(b)
}
Expand Down Expand Up @@ -433,7 +433,7 @@ func diffFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, verbos
Log: log,
ReadOnly: true,
LineRead: blocks.ReaderPassthrough,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, preserveIndent bool) error {
var fb string
var err error
if fmtverbs {
Expand All @@ -445,6 +445,10 @@ func diffFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, verbos
return err
}

if preserveIndent {
fb = indentToOriginalLevel(fb, b)
}

if fb == b {
return nil
}
Expand All @@ -460,7 +464,7 @@ func diffFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, verbos
for scanner.Scan() {
l := scanner.Text()

// nolint: gocritic
//nolint: gocritic
if strings.HasPrefix(l, "+") {
fmt.Fprint(outW, c.Sprintf("<green>%s</>\n", l))
} else if strings.HasPrefix(l, "-") {
Expand Down Expand Up @@ -500,7 +504,7 @@ func formatFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, fixF
br := blocks.Reader{
Log: log,
LineRead: blocks.ReaderPassthrough,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, preserveIndent bool) error {
var fb string
var err error
if fmtverbs {
Expand All @@ -512,6 +516,10 @@ func formatFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, fixF
return err
}

if preserveIndent {
fb = indentToOriginalLevel(fb, b)
}

hasChange := fb != b

if br.CurrentNodeCursor != nil {
Expand Down Expand Up @@ -574,7 +582,7 @@ func upgrade012File(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs,
br := blocks.Reader{
Log: log,
LineRead: blocks.ReaderPassthrough,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, preserveIndent bool) error {
var fb string
var err error
if fmtverbs {
Expand All @@ -586,6 +594,10 @@ func upgrade012File(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs,
return err
}

if preserveIndent {
fb = indentToOriginalLevel(fb, b)
}

hasChange := fb != b

if br.CurrentNodeCursor != nil {
Expand Down
26 changes: 26 additions & 0 deletions cli/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cli

import (
"strings"
"unicode"
)

func indentToOriginalLevel(formatted string, original string) string {
prefix := ""
for _, r := range original {
if unicode.IsSpace(r) {
if r == '\n' {
prefix = ""
continue
}
prefix += string(r)
} else {
break
}
}
res := strings.ReplaceAll(formatted, "\n", "\n"+prefix)
res = strings.TrimRight(res, prefix)
res = strings.TrimLeft(res, prefix)

return prefix + res
}
38 changes: 15 additions & 23 deletions lib/blocks/blockreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go/token"
"io"
"io/ioutil"
"path/filepath"
"regexp"
"strconv"
"strings"
Expand All @@ -21,7 +22,7 @@ import (

var lineWithLeadingSpacesMatcher = regexp.MustCompile("^[[:space:]]*(.*\n)$")

type blockReadFunc func(*Reader, int, string) error
type blockReadFunc func(*Reader, int, string, bool) error

type BlockWriter interface {
Write(index, startLine, endLine int, text string)
Expand Down Expand Up @@ -71,23 +72,6 @@ func ReaderIgnore(br *Reader, number int, line string) error {
return nil
}

func IsStartLine(line string) bool {
// nolint:gocritic
if strings.HasPrefix(line, "```hcl") { // documentation
return true
} else if strings.HasPrefix(line, "```terraform") { // documentation
return true
} else if strings.HasPrefix(line, "```tf") { // documentation
return true
}

return false
}

func IsFinishLine(line string) bool {
return strings.HasPrefix(line, "```") // documentation
}

type blockVisitor struct {
br *Reader
fset *token.FileSet
Expand Down Expand Up @@ -116,7 +100,7 @@ func (bv blockVisitor) Visit(cursor *astutil.Cursor) bool {
// This is to deal with some outputs using just LineCount and some using LineCount-BlockCurrentLine
bv.br.BlockCurrentLine = bv.fset.Position(node.End()).Line - bv.fset.Position(node.Pos()).Line

err := bv.f(bv.br, 0, value)
err := bv.f(bv.br, 0, value, false)
if err != nil {
bv.br.ErrorBlocks++
bv.br.Log.Errorf("block %d @ %s:%d failed to process with: %v", bv.br.BlockCount, bv.br.FileName, bv.fset.Position(node.Pos()).Line, err)
Expand Down Expand Up @@ -256,6 +240,14 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
}
}

var format textFormat
switch filepath.Ext(filename) {
case ".rst":
format = restructuredTextFormat{}
default:
format = markdownTextFormat{}
}

br.LineCount = 0
br.BlockCount = 0
s := bufio.NewScanner(br.Reader)
Expand All @@ -268,7 +260,7 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
return fmt.Errorf("NB LineRead failed @ %s:%d for %s: %w", br.FileName, br.LineCount, l, err)
}

if IsStartLine(l) {
if format.isStartingLine(l) {
block := ""
br.BlockCurrentLine = 0
br.BlockCount++
Expand All @@ -279,7 +271,7 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
l2 := s.Text() + "\n"

// make sure we don't run into another block
if IsStartLine(l2) {
if format.isStartingLine(l2) {
// the end of current block must be malformed, so lets pass it through and log an error
br.Log.Errorf("block %d @ %s:%d failed to find end of block", br.BlockCount, br.FileName, br.LineCount-br.BlockCurrentLine)
if err := ReaderPassthrough(br, br.LineCount, block); err != nil { // is this ok or should we loop with LineRead?
Expand All @@ -296,15 +288,15 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
continue
}

if IsFinishLine(l2) {
if format.isFinishLine(l2) {
if br.FixFinishLines {
l2 = lineWithLeadingSpacesMatcher.ReplaceAllString(l2, `$1`)
}

br.LinesBlock += br.BlockCurrentLine

// todo configure this behaviour with switch's
if err := br.BlockRead(br, br.LineCount, block); err != nil {
if err := br.BlockRead(br, br.LineCount, block, format.preserveIndentation()); err != nil {
// for now ignore block errors and output unformatted
br.ErrorBlocks++
br.Log.Errorf("block %d @ %s:%d failed to process with: %v", br.BlockCount, br.FileName, br.LineCount-br.BlockCurrentLine, err)
Expand Down
112 changes: 111 additions & 1 deletion lib/blocks/blockreader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,116 @@ func TestBlockDetection(t *testing.T) {
resource "aws_s3_bucket" "leading-space-and-line" {
bucket = "tf-test-bucket-leading-space-and-line"
}
`,
},
},
},
{
sourcefile: "testdata/test3.rst",
expectedBlocks: []block{
{
text: ` resource "aws_s3_bucket" "terraform" {
bucket = "tf-test-bucket-terraform"
}

`,
},
{
text: ` resource "azurerm_resource_group" "example" {
name = "testaccbatch"
location = "West Europe"
}

resource "azurerm_storage_account" "example" {
name = "testaccsa"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_batch_account" "example" {
name = "testaccbatch"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
pool_allocation_mode = "BatchService"
storage_account_id = azurerm_storage_account.example.id

tags = {
env = "test"
}
}

resource "azurerm_batch_certificate" "example" {
resource_group_name = azurerm_resource_group.example.name
account_name = azurerm_batch_account.example.name
certificate = filebase64("certificate.cer")
format = "Cer"
thumbprint = "312d31a79fa0cef49c00f769afc2b73e9f4edf34"
thumbprint_algorithm = "SHA1"
}

resource "azurerm_batch_pool" "example" {
name = "testaccpool"
resource_group_name = azurerm_resource_group.example.name
account_name = azurerm_batch_account.example.name
display_name = "Test Acc Pool Auto"
vm_size = "Standard_A1"
node_agent_sku_id = "batch.node.ubuntu 20.04"

auto_scale {
evaluation_interval = "PT15M"

formula = <<EOF
startingNumberOfVMs = 1;
maxNumberofVMs = 25;
pendingTaskSamplePercent = $PendingTasks.GetSamplePercent(180 * TimeInterval_Second);
pendingTaskSamples = pendingTaskSamplePercent < 70 ? startingNumberOfVMs : avg($PendingTasks.GetSample(180 * TimeInterval_Second));
$TargetDedicatedNodes=min(maxNumberofVMs, pendingTaskSamples);
EOF

}

storage_image_reference {
publisher = "microsoft-azure-batch"
offer = "ubuntu-server-container"
sku = "20-04-lts"
version = "latest"
}

container_configuration {
type = "DockerCompatible"
container_registries {
registry_server = "docker.io"
user_name = "login"
password = "apassword"
}
}

start_task {
command_line = "echo 'Hello World from $env'"
task_retry_maximum = 1
wait_for_success = true

common_environment_properties = {
env = "TEST"
}

user_identity {
auto_user {
elevation_level = "NonAdmin"
scope = "Task"
}
}
}

certificate {
id = azurerm_batch_certificate.example.id
store_location = "CurrentUser"
visibility = ["StartTask"]
}
}

`,
},
},
Expand All @@ -138,7 +248,7 @@ func TestBlockDetection(t *testing.T) {
Log: log,
ReadOnly: true,
LineRead: ReaderIgnore,
BlockRead: func(br *Reader, i int, b string) error {
BlockRead: func(br *Reader, i int, b string, preserveIndent bool) error {
actualBlocks = append(actualBlocks, block{
leadingPadding: br.CurrentNodeLeadingPadding,
text: b,
Expand Down
Loading