Skip to content

Commit cdd2f5d

Browse files
committed
Use urfave/cli package; add verbose flag; print metrics; refactor driver
1 parent 7ebd2c8 commit cdd2f5d

File tree

13 files changed

+315
-165
lines changed

13 files changed

+315
-165
lines changed

backend/llm/openai/gpt4/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,5 +158,5 @@ func logMessages(llmMessages []*LlmMessage) {
158158
str += llmMessage.Content + "\n\n"
159159
}
160160
}
161-
logger.Log(str)
161+
logger.Debug(str)
162162
}

backend/target/terraform/validate_code.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func parseTerraformValidateOutput(
194194
return tfErrors[i].Range.Start.Line < tfErrors[j].Range.Start.Line
195195
})
196196

197-
logger.Log("tfErrors: " + fmt.Sprint(tfErrors))
197+
logger.Debug("tfErrors: " + fmt.Sprint(tfErrors))
198198

199199
j := 0
200200
for _, tfError := range tfErrors {

cli/go.mod

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ module salami-cli
22

33
go 1.21.2
44

5+
require github.com/urfave/cli/v2 v2.25.7
6+
57
require (
6-
github.com/fatih/color v1.15.0
7-
github.com/mattn/go-colorable v0.1.13 // indirect
8-
github.com/mattn/go-isatty v0.0.17 // indirect
9-
golang.org/x/sys v0.6.0 // indirect
8+
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
9+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
10+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
1011
)

cli/go.sum

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
2-
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
3-
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
4-
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
5-
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
6-
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
7-
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
8-
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9-
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
10-
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1+
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
2+
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
3+
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
4+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
5+
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
6+
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
7+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
8+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=

cli/main.go

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,82 @@
11
package main
22

33
import (
4-
"flag"
54
"fmt"
5+
"log"
6+
"os"
67
"salami/common/constants"
78
"salami/common/driver"
9+
"strings"
810

9-
"github.com/fatih/color"
11+
"github.com/urfave/cli/v2"
1012
)
1113

12-
const GENERAL_COMMAND = "salami"
13-
14-
var command_list = map[string]map[string]string{
15-
"version": {
16-
"cmd": "version",
17-
"description": "Print the version of the installed Salami CLI",
18-
},
19-
"compile": {
20-
"cmd": "compile",
21-
"description": "Run the compilation end-to-end",
22-
},
14+
type SalamiMultiError struct {
15+
errors []error
2316
}
2417

25-
func showCommands() {
26-
fmt.Println("Usage: \n \t ", GENERAL_COMMAND, "<command>\n ")
27-
fmt.Println("The commands are:")
28-
for id, command_ := range command_list {
29-
fmt.Println(color.HiBlueString(id), ":", command_["description"])
18+
func (m *SalamiMultiError) Error() string {
19+
msgs := []string{}
20+
for _, err := range m.errors {
21+
msgs = append(msgs, err.Error())
3022
}
31-
fmt.Println()
23+
return strings.Join(msgs, ", ")
3224
}
3325

34-
func runSystem() {
35-
errors := driver.Run()
36-
37-
for _, err := range errors {
38-
color.Red(err.Error())
39-
}
26+
func (m *SalamiMultiError) Errors() []error {
27+
return m.errors
4028
}
4129

4230
func main() {
43-
flag.Parse()
44-
command := flag.Arg(0)
31+
app := &cli.App{
32+
Name: "salami",
33+
HelpName: "Salami",
34+
Version: constants.SalamiVersion,
35+
Usage: "a declarative DSL for cloud infrastructure based on natural language descriptions",
36+
UsageText: "salami [global options] [command] [command options]",
37+
HideVersion: true,
38+
Suggest: true,
39+
Flags: []cli.Flag{
40+
&cli.BoolFlag{
41+
Name: "verbose",
42+
Aliases: []string{"v"},
43+
Usage: "Enable verbose mode",
44+
},
45+
},
46+
Commands: []*cli.Command{
47+
{
48+
Name: "compile",
49+
Usage: "Runs the compilation end-to-end",
50+
UsageText: "salami [global options] compile [command options]",
51+
Action: func(cCtx *cli.Context) error {
52+
verbose := cCtx.Bool("verbose")
53+
errors := driver.Run(verbose)
54+
55+
if len(errors) == 1 {
56+
return errors[0]
57+
} else if len(errors) > 1 {
58+
return &SalamiMultiError{errors: errors}
59+
}
4560

46-
if command == "" {
47-
color.Green("====================================")
48-
color.Green("======= Welcome to Salami CLI ======")
49-
color.Green("====================================\n ")
50-
fmt.Println("Salami is a declarative domain-specific language for cloud infrastructure based on natural language descriptions. " +
51-
"Salami compiler uses GPT4 to convert the natural language to Terraform code. You can think of Salami as writing documentation " +
52-
"for each cloud resource object, and letting the compiler take care of converting that to IaC (Infrastructure as Code).",
53-
)
54-
showCommands()
55-
return
61+
return nil
62+
},
63+
},
64+
{
65+
Name: "version",
66+
Usage: "Prints the version",
67+
Action: func(cCtx *cli.Context) error {
68+
fmt.Println("Salami version " + constants.SalamiVersion)
69+
return nil
70+
},
71+
},
72+
},
73+
Authors: []*cli.Author{{
74+
Name: "Petr Gazarov",
75+
76+
}},
5677
}
5778

58-
switch cmd := command; cmd {
59-
case command_list["version"]["cmd"]:
60-
fmt.Println("Salami version " + constants.SalamiVersion)
61-
case command_list["compile"]["cmd"]:
62-
runSystem()
63-
case "help":
64-
showCommands()
65-
default:
66-
msg := "Invalid command passed. Type '" + GENERAL_COMMAND + " help'"
67-
color.Red(msg)
79+
if err := app.Run(os.Args); err != nil {
80+
log.Fatal(err)
6881
}
6982
}
Lines changed: 40 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,21 @@
11
package driver
22

33
import (
4-
"path/filepath"
5-
"sort"
6-
74
"salami/backend/llm"
85
"salami/backend/target"
96
"salami/backend/target_file_manager"
107
"salami/common/change_set"
118
"salami/common/config"
12-
"salami/common/constants"
139
"salami/common/lock_file_manager"
10+
"salami/common/metrics"
1411
"salami/common/symbol_table"
15-
commonTypes "salami/common/types"
16-
"salami/common/utils/file_utils"
17-
"salami/frontend/semantic_analyzer"
12+
"salami/common/types"
13+
"sort"
1814
)
1915

20-
func Run() []error {
21-
if errors := runValidations(); len(errors) > 0 {
22-
return errors
23-
}
24-
25-
symbolTable, errors := runFrontend()
26-
if len(errors) > 0 {
27-
return errors
28-
}
29-
30-
newTargetFileMetas, newObjects, errors := runBackend(symbolTable)
31-
if len(errors) > 0 {
32-
return errors
33-
}
34-
35-
if err := lock_file_manager.UpdateLockFile(newTargetFileMetas, newObjects); err != nil {
36-
return []error{err}
37-
}
38-
39-
return nil
40-
}
41-
42-
func runFrontend() (*symbol_table.SymbolTable, []error) {
43-
sourceFilePaths, err := getSourceFilePaths()
44-
if err != nil {
45-
return nil, []error{err}
46-
}
47-
48-
allResources, allVariables, errors := parseFiles(sourceFilePaths, config.GetSourceDir())
49-
if len(errors) > 0 {
50-
return nil, errors
51-
}
52-
53-
symbolTable, err := symbol_table.NewSymbolTable(allResources, allVariables)
54-
if err != nil {
55-
return nil, []error{err}
56-
}
57-
58-
semanticAnalyzer := semantic_analyzer.NewSemanticAnalyzer(symbolTable)
59-
if err = semanticAnalyzer.Analyze(); err != nil {
60-
return nil, []error{err}
61-
}
62-
63-
return symbolTable, nil
64-
}
65-
6616
func runBackend(
6717
symbolTable *symbol_table.SymbolTable,
68-
) ([]commonTypes.TargetFileMeta, []*commonTypes.Object, []error) {
18+
) ([]types.TargetFileMeta, []*types.Object, []error) {
6919
newObjects, newTargetFiles, errors := generateCode(symbolTable)
7020
if len(errors) > 0 {
7121
return nil, nil, errors
@@ -83,12 +33,14 @@ func runBackend(
8333
return nil, nil, errors
8434
}
8535

36+
metrics.SetMetric(metrics.SourceFilesProcessed, len(newTargetFiles))
37+
8638
return newTargetFileMetas, newObjects, nil
8739
}
8840

8941
func generateCode(
9042
symbolTable *symbol_table.SymbolTable,
91-
) ([]*commonTypes.Object, []*commonTypes.TargetFile, []error) {
43+
) ([]*types.Object, []*types.TargetFile, []error) {
9244
previousObjects := lock_file_manager.GetObjects()
9345
changeSet := change_set.NewChangeSet(previousObjects, symbolTable)
9446
changeSetRepository := change_set.NewChangeSetRepository(changeSet)
@@ -106,33 +58,35 @@ func generateCode(
10658
}
10759
targetFiles := target.GetFilesFromObjects(newObjects)
10860

61+
updateMetricsFromChangeSet(changeSet)
62+
10963
return newObjects, targetFiles, nil
11064
}
11165

112-
func getSourceFilePaths() ([]string, error) {
113-
sourceFilePaths, err := file_utils.GetFilePaths(config.GetSourceDir(), func(path string) bool {
114-
return filepath.Ext(path) == constants.SalamiFileExtension
115-
})
116-
if err != nil {
117-
return nil, err
66+
func getFilePathsToRemove(
67+
oldTargetFileMetas []types.TargetFileMeta,
68+
newTargetFileMetas []types.TargetFileMeta,
69+
) []string {
70+
newMetaMap := make(map[string]bool)
71+
for _, meta := range newTargetFileMetas {
72+
newMetaMap[meta.FilePath] = true
11873
}
11974

120-
relativeSourceFilePaths, err := file_utils.GetRelativeFilePaths(
121-
config.GetSourceDir(),
122-
sourceFilePaths,
123-
)
124-
if err != nil {
125-
return nil, err
75+
oldFilePaths := make([]string, 0)
76+
for _, meta := range oldTargetFileMetas {
77+
if _, exists := newMetaMap[meta.FilePath]; !exists {
78+
oldFilePaths = append(oldFilePaths, meta.FilePath)
79+
}
12680
}
12781

128-
return relativeSourceFilePaths, nil
82+
return oldFilePaths
12983
}
13084

13185
func computeNewObjects(
132-
previousObjects []*commonTypes.Object,
86+
previousObjects []*types.Object,
13387
changeSetRepository *change_set.ChangeSetRepository,
134-
) []*commonTypes.Object {
135-
objects := make([]*commonTypes.Object, 0)
88+
) []*types.Object {
89+
objects := make([]*types.Object, 0)
13690
for _, object := range previousObjects {
13791
if changeSetRepository.WasObjectChanged(object) {
13892
objects = append(objects, changeSetRepository.GetChangedObject(object))
@@ -158,21 +112,22 @@ func computeNewObjects(
158112
return objects
159113
}
160114

161-
func getFilePathsToRemove(
162-
oldTargetFileMetas []commonTypes.TargetFileMeta,
163-
newTargetFileMetas []commonTypes.TargetFileMeta,
164-
) []string {
165-
newMetaMap := make(map[string]bool)
166-
for _, meta := range newTargetFileMetas {
167-
newMetaMap[meta.FilePath] = true
168-
}
169-
170-
oldFilePaths := make([]string, 0)
171-
for _, meta := range oldTargetFileMetas {
172-
if _, exists := newMetaMap[meta.FilePath]; !exists {
173-
oldFilePaths = append(oldFilePaths, meta.FilePath)
115+
func updateMetricsFromChangeSet(changeSet *types.ChangeSet) {
116+
objectsAdded := 0
117+
objectsRemoved := 0
118+
objectsChanged := 0
119+
120+
for _, diff := range changeSet.Diffs {
121+
if diff.IsAdd() {
122+
objectsAdded++
123+
} else if diff.IsRemove() {
124+
objectsRemoved++
125+
} else if diff.IsUpdate() || diff.IsMove() {
126+
objectsChanged++
174127
}
175128
}
176129

177-
return oldFilePaths
130+
metrics.SetMetric(metrics.ObjectsAdded, objectsAdded)
131+
metrics.SetMetric(metrics.ObjectsRemoved, objectsRemoved)
132+
metrics.SetMetric(metrics.ObjectsChanged, objectsChanged)
178133
}

0 commit comments

Comments
 (0)