Skip to content

Commit

Permalink
feat: add support for loading multiple files on the same directory
Browse files Browse the repository at this point in the history
  • Loading branch information
srevinsaju committed Sep 3, 2023
1 parent 09dd8fe commit 6fb765e
Show file tree
Hide file tree
Showing 41 changed files with 220 additions and 43 deletions.
2 changes: 1 addition & 1 deletion docs/src/configuration/macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ For example,
files = {
"togomak.hcl" = <<-EOT
togomak {
version = 1
version = 2
}
stage "hello" {
script = "echo hello world"
Expand Down
10 changes: 5 additions & 5 deletions docs/src/configuration/stage.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ of arguments, or a macro.
### Stage with Script
```hcl
~togomak {
~ version = 1
~ version = 2
~}
~
stage "script" {
Expand All @@ -21,7 +21,7 @@ stage "script" {
### Stage with Command and Arguments
```hcl
~togomak {
~ version = 1
~ version = 2
~}
~
stage "command" {
Expand All @@ -32,7 +32,7 @@ stage "command" {
### Stage with Macro
```hcl
~togomak {
~ version = 1
~ version = 2
~}
~
macro "echo" {
Expand All @@ -51,7 +51,7 @@ stage "macro" {
### Stage with Dependencies
```hcl
~togomak {
~ version = 1
~ version = 2
~}
~
stage "build" {
Expand All @@ -67,7 +67,7 @@ stage "install" {
### Stage with Retry
```hcl
~togomak {
~ version = 1
~ version = 2
~}
~
stage "build" {
Expand Down
2 changes: 1 addition & 1 deletion docs/src/configuration/togomak.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ block for the file to be recognized as a valid
togomak {
# ...
version = 1
version = 2
}
```
The above block, with `version` parameter
Expand Down
2 changes: 1 addition & 1 deletion examples/conditions/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

data "env" "home" {
Expand Down
2 changes: 1 addition & 1 deletion examples/docker/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

stage "example" {
Expand Down
2 changes: 1 addition & 1 deletion examples/env/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

data "env" "HOME" {
Expand Down
2 changes: 1 addition & 1 deletion examples/files/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}


Expand Down
2 changes: 1 addition & 1 deletion examples/git/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

data "git" "repo" {
Expand Down
2 changes: 1 addition & 1 deletion examples/git_tags/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

data "git" "repo" {
Expand Down
2 changes: 1 addition & 1 deletion examples/locals/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

locals {
Expand Down
2 changes: 1 addition & 1 deletion examples/macros/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

macro "explode" {
Expand Down
4 changes: 4 additions & 0 deletions examples/multiple-files/stage1.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
stage "example" {
name = "example"
script = "echo hello world"
}
3 changes: 3 additions & 0 deletions examples/multiple-files/stage2.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
stage "example_2" {
script = "echo bye world"
}
4 changes: 4 additions & 0 deletions examples/multiple-files/togomak.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
togomak {
version = 2
}

2 changes: 1 addition & 1 deletion examples/output/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

stage "agent" {
Expand Down
2 changes: 1 addition & 1 deletion examples/prompt/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

data "env" "quit_if_not_shinji" {
Expand Down
2 changes: 1 addition & 1 deletion examples/remote-stages/togomak.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
togomak {
version = 1
version = 2
}

data "git" "eva01_source" {
Expand Down
2 changes: 1 addition & 1 deletion pkg/ci/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package ci
const BuilderBlock = "togomak"

type Builder struct {
Version string `hcl:"version" json:"version"`
Version int `hcl:"version" json:"version"`
}
5 changes: 5 additions & 0 deletions pkg/ci/data_prop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ci

func (d *Data) Override() bool {
return true

Check warning on line 4 in pkg/ci/data_prop.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/data_prop.go#L3-L4

Added lines #L3 - L4 were not covered by tests
}
5 changes: 5 additions & 0 deletions pkg/ci/locals_prop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ci

func (l *Local) Override() bool {
return false

Check warning on line 4 in pkg/ci/locals_prop.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/locals_prop.go#L3-L4

Added lines #L3 - L4 were not covered by tests
}
5 changes: 5 additions & 0 deletions pkg/ci/macro_prop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ci

func (m *Macro) Override() bool {
return true

Check warning on line 4 in pkg/ci/macro_prop.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/macro_prop.go#L3-L4

Added lines #L3 - L4 were not covered by tests
}
10 changes: 10 additions & 0 deletions pkg/ci/prop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ci

type Overrideable interface {
Override() bool
}

type Distinct interface {
Overrideable
Describable
}
9 changes: 9 additions & 0 deletions pkg/ci/stage_prop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ci

func (s Stage) Override() bool {
return false

Check warning on line 4 in pkg/ci/stage_prop.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/stage_prop.go#L3-L4

Added lines #L3 - L4 were not covered by tests
}

func (s Stages) Override() bool {
return false

Check warning on line 8 in pkg/ci/stage_prop.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/stage_prop.go#L7-L8

Added lines #L7 - L8 were not covered by tests
}
18 changes: 18 additions & 0 deletions pkg/ci/stage_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ type StageContainer struct {

type Stages []Stage

// IsDistinct checks if the stages in s and ss are distinct
// TODO: check if this is a good way to do this
func (s Stages) CheckIfDistinct(ss Stages) hcl.Diagnostics {
var diags hcl.Diagnostics
for _, stage := range s {
for _, stage2 := range ss {
if stage.Id == stage2.Id {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate stage",
Detail: "Stage with id " + stage.Id + " is defined more than once",
})
}

Check warning on line 48 in pkg/ci/stage_schema.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/stage_schema.go#L38-L48

Added lines #L38 - L48 were not covered by tests
}
}
return diags

Check warning on line 51 in pkg/ci/stage_schema.go

View check run for this annotation

Codecov / codecov/patch

pkg/ci/stage_schema.go#L51

Added line #L51 was not covered by tests
}

type StageEnvironment struct {
Name string `hcl:"name" json:"name"`
Value hcl.Expression `hcl:"value" json:"value"`
Expand Down
23 changes: 16 additions & 7 deletions pkg/orchestra/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/srevinsaju/togomak/v1/pkg/pipeline"
"os"
"path/filepath"
)

func Format(cfg Config, check bool, recursive bool) error {
Expand All @@ -32,15 +33,23 @@ func Format(cfg Config, check bool, recursive bool) error {
t.Logger.Fatalf("Error while globbing for **/*.hcl: %s", err)
}
} else {
fn := pipeline.ConfigFilePath(ctx)
data, err := os.ReadFile(fn)
fDir := pipeline.ConfigFileDir(ctx)
fNames, err := os.ReadDir(fDir)

Check warning on line 37 in pkg/orchestra/format.go

View check run for this annotation

Codecov / codecov/patch

pkg/orchestra/format.go#L36-L37

Added lines #L36 - L37 were not covered by tests
if err != nil {
return err
panic(err)

Check warning on line 39 in pkg/orchestra/format.go

View check run for this annotation

Codecov / codecov/patch

pkg/orchestra/format.go#L39

Added line #L39 was not covered by tests
}
outSrc := hclwrite.Format(data)
if !bytes.Equal(outSrc, data) {
t.Logger.Tracef("%s needs formatting", fn)
toFormat = append(toFormat, fn)

for _, f := range fNames {
fn := filepath.Join(fDir, f.Name())
data, err := os.ReadFile(fn)
if err != nil {
return err
}
outSrc := hclwrite.Format(data)
if !bytes.Equal(outSrc, data) {
t.Logger.Tracef("%s needs formatting", fn)
toFormat = append(toFormat, fn)
}

Check warning on line 52 in pkg/orchestra/format.go

View check run for this annotation

Codecov / codecov/patch

pkg/orchestra/format.go#L42-L52

Added lines #L42 - L52 were not covered by tests
}
}
for _, fn := range toFormat {
Expand Down
106 changes: 103 additions & 3 deletions pkg/pipeline/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package pipeline

import (
"context"
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/srevinsaju/togomak/v1/pkg/c"
"github.com/srevinsaju/togomak/v1/pkg/ci"
"github.com/srevinsaju/togomak/v1/pkg/meta"
"github.com/srevinsaju/togomak/v1/pkg/ui"
"os"
"path/filepath"
"strings"
)

// configFilePath returns the path to the configuration file. If the path is not absolute, it is assumed to be
// relative to the working directory
// DEPRECATED: use configFileDir instead
func ConfigFilePath(ctx context.Context) string {
filePath := ctx.Value(c.TogomakContextPipelineFilePath).(string)
if filePath == "" {
Expand All @@ -24,16 +31,109 @@ func ConfigFilePath(ctx context.Context) string {
return filePath
}

func Read(ctx context.Context, parser *hclparse.Parser) (*ci.Pipeline, hcl.Diagnostics) {
filePath := ConfigFilePath(ctx)
func ConfigFileDir(ctx context.Context) string {
return filepath.Dir(ConfigFilePath(ctx))
}

f, diags := parser.ParseHCLFile(filePath)
// Read reads togomak.hcl from the configuration file directory. A configuration file directory is the one that
// contains togomak.hcl, it searches recursively outwards.
// DEPRECATED: use ReadDir instead
func Read(ctx context.Context, parser *hclparse.Parser) (*ci.Pipeline, hcl.Diagnostics) {
ciFile := ConfigFilePath(ctx)

f, diags := parser.ParseHCLFile(ciFile)
if diags.HasErrors() {
return nil, diags
}

pipeline := &ci.Pipeline{}
diags = gohcl.DecodeBody(f.Body, nil, pipeline)

if pipeline.Builder.Version != 1 {
return ReadDir(ctx, parser)
} else if pipeline.Builder.Version == 1 {
ui.DeprecationWarning(fmt.Sprintf("%s configuration version 1 is deprecated, and support for the same will be removed in a later version. ", meta.AppName))
}
return pipeline, diags
}

// ReadDir parses an entire directory of *.hcl files and merges them together. This is useful when you want to
// split your pipeline into multiple files, without having to import them individually
func ReadDir(ctx context.Context, parser *hclparse.Parser) (*ci.Pipeline, hcl.Diagnostics) {
var diags hcl.Diagnostics
dir := ConfigFileDir(ctx)
togomakFiles, err := os.ReadDir(dir)
if err != nil {
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "directory not found",
Detail: err.Error(),
})
}

Check warning on line 72 in pkg/pipeline/parse.go

View check run for this annotation

Codecov / codecov/patch

pkg/pipeline/parse.go#L67-L72

Added lines #L67 - L72 were not covered by tests
var pipes []*pipelineMeta
for _, file := range togomakFiles {
if file.IsDir() {
continue
}
if !strings.HasSuffix(file.Name(), ".hcl") {
continue
}
f, d := parser.ParseHCLFile(filepath.Join(dir, file.Name()))
diags = diags.Extend(d)

p := &ci.Pipeline{}

d = gohcl.DecodeBody(f.Body, nil, p)
diags = diags.Extend(d)
pipes = append(pipes, &pipelineMeta{
pipe: p,
f: f,
filename: file.Name(),
})

}
return createRawPipeline(pipes...)

}

// pipelineMeta is a helper struct to create a pipeline from multiple pipelines
// this additionally includes the file pointer f, and the filename
type pipelineMeta struct {
pipe *ci.Pipeline
f *hcl.File
filename string
}

// createRawPipeline creates a pipeline from multiple pipelines. This is useful when you want to merge multiple
// pipelines together, without having to import them individually
func createRawPipeline(pipelines ...*pipelineMeta) (*ci.Pipeline, hcl.Diagnostics) {
pipe := &ci.Pipeline{}

var diags hcl.Diagnostics

var versionDefinedFromFilename string
for _, p := range pipelines {
if pipe.Builder.Version == 0 && p.pipe.Builder.Version != 0 {
pipe.Builder.Version = p.pipe.Builder.Version
versionDefinedFromFilename = p.filename
}
if p.pipe.Builder.Version != pipe.Builder.Version && p.pipe.Builder.Version != 0 {
// when overriding and using multiple pipelines, the version of the togomak pipeline schema is
// required to be the same
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "version mismatch",
Detail: fmt.Sprintf("version mismatch between pipelines: %d (%s) and %d (%s)", p.pipe.Builder.Version, p.filename, pipe.Builder.Version, versionDefinedFromFilename),
})
}

Check warning on line 128 in pkg/pipeline/parse.go

View check run for this annotation

Codecov / codecov/patch

pkg/pipeline/parse.go#L121-L128

Added lines #L121 - L128 were not covered by tests

// TODO: create an error if there are duplicate resource definition
pipe.Stages = append(pipe.Stages, p.pipe.Stages...)
pipe.Data = append(pipe.Data, p.pipe.Data...)
pipe.DataProviders = append(pipe.DataProviders, p.pipe.DataProviders...)
pipe.Macros = append(pipe.Macros, p.pipe.Macros...)
pipe.Locals = append(pipe.Locals, p.pipe.Locals...)
pipe.Local = append(pipe.Local, p.pipe.Local...)
}
return pipe, diags
}
Loading

0 comments on commit 6fb765e

Please sign in to comment.