1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package build
import (
"bytes"
"fmt"
"log"
"os/exec"
"text/template"
)
// ContractBuilder handles building smart contracts using just commands
type ContractBuilder struct {
// Base directory where the build commands should be executed
baseDir string
// Template for the build command
cmdTemplate *template.Template
// Dry run mode
dryRun bool
builtContracts map[string]interface{}
}
const (
contractsCmdTemplateStr = "just _contracts-build {{.BundlePath}}"
)
var defaultContractTemplate *template.Template
func init() {
defaultContractTemplate = template.Must(template.New("contract_build_cmd").Parse(contractsCmdTemplateStr))
}
type ContractBuilderOptions func(*ContractBuilder)
func WithContractBaseDir(baseDir string) ContractBuilderOptions {
return func(b *ContractBuilder) {
b.baseDir = baseDir
}
}
func WithContractTemplate(cmdTemplate *template.Template) ContractBuilderOptions {
return func(b *ContractBuilder) {
b.cmdTemplate = cmdTemplate
}
}
func WithContractDryRun(dryRun bool) ContractBuilderOptions {
return func(b *ContractBuilder) {
b.dryRun = dryRun
}
}
// NewContractBuilder creates a new ContractBuilder instance
func NewContractBuilder(opts ...ContractBuilderOptions) *ContractBuilder {
b := &ContractBuilder{
baseDir: ".",
cmdTemplate: defaultContractTemplate,
dryRun: false,
builtContracts: make(map[string]interface{}),
}
for _, opt := range opts {
opt(b)
}
return b
}
// templateData holds the data for the command template
type contractTemplateData struct {
BundlePath string
}
// Build executes the contract build command
func (b *ContractBuilder) Build(_layer string, bundlePath string) error {
// since we ignore layer for now, we can skip the build if the file already
// exists: it'll be the same file!
if _, ok := b.builtContracts[bundlePath]; ok {
return nil
}
log.Printf("Building contracts bundle: %s", bundlePath)
// Prepare template data
data := contractTemplateData{
BundlePath: bundlePath,
}
// Execute template to get command string
var cmdBuf bytes.Buffer
if err := b.cmdTemplate.Execute(&cmdBuf, data); err != nil {
return fmt.Errorf("failed to execute command template: %w", err)
}
// Create command
cmd := exec.Command("sh", "-c", cmdBuf.String())
cmd.Dir = b.baseDir
if !b.dryRun {
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("contract build command failed: %w\nOutput: %s", err, string(output))
}
}
b.builtContracts[bundlePath] = struct{}{}
return nil
}