generated from TBD54566975/tbd-project-template
/
cmd_init.go
145 lines (125 loc) 路 4.37 KB
/
cmd_init.go
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package main
import (
"archive/zip"
"bufio"
"context"
"fmt"
"html/template"
"os"
"path"
"path/filepath"
"strings"
"github.com/TBD54566975/scaffolder"
"github.com/TBD54566975/ftl/backend/schema"
"github.com/TBD54566975/ftl/backend/schema/strcase"
"github.com/TBD54566975/ftl/buildengine"
goruntime "github.com/TBD54566975/ftl/go-runtime"
"github.com/TBD54566975/ftl/internal"
"github.com/TBD54566975/ftl/internal/exec"
"github.com/TBD54566975/ftl/internal/log"
kotlinruntime "github.com/TBD54566975/ftl/kotlin-runtime"
)
type initCmd struct {
Hermit bool `help:"Include Hermit language-specific toolchain binaries in the module." negatable:""`
Go initGoCmd `cmd:"" help:"Initialize a new FTL Go module."`
Kotlin initKotlinCmd `cmd:"" help:"Initialize a new FTL Kotlin module."`
}
type initGoCmd struct {
Replace map[string]string `short:"r" help:"Replace a module import path with a local path in the initialised FTL module." placeholder:"OLD=NEW,..." env:"FTL_INIT_GO_REPLACE"`
Dir string `arg:"" help:"Directory to initialize the module in."`
Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
}
func (i initGoCmd) Run(ctx context.Context, parent *initCmd) error {
if i.Name == "" {
i.Name = filepath.Base(i.Dir)
}
if !schema.ValidateName(i.Name) {
return fmt.Errorf("module name %q is invalid", i.Name)
}
logger := log.FromContext(ctx)
logger.Debugf("Initializing FTL Go module %s in %s", i.Name, i.Dir)
if err := scaffold(parent.Hermit, goruntime.Files(), i.Dir, i, scaffolder.Exclude("^go.mod$")); err != nil {
return err
}
if err := updateGitIgnore(i.Dir); err != nil {
return err
}
logger.Debugf("Running go mod tidy")
return exec.Command(ctx, log.Debug, filepath.Join(i.Dir, i.Name), "go", "mod", "tidy").RunBuffered(ctx)
}
type initKotlinCmd struct {
GroupID string `short:"g" help:"Base Maven group ID (defaults to \"ftl\")." default:"ftl"`
ArtifactID string `short:"a" help:"Base Maven artifact ID (defaults to \"ftl\")." default:"ftl"`
Dir string `arg:"" help:"Directory to initialize the module in."`
Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
}
func (i initKotlinCmd) Run(ctx context.Context, parent *initCmd) error {
if i.Name == "" {
i.Name = filepath.Base(i.Dir)
}
if !schema.ValidateName(i.Name) {
return fmt.Errorf("module name %q is invalid", i.Name)
}
moduleDir := filepath.Join(i.Dir, i.Name)
if _, err := os.Stat(moduleDir); err == nil {
return fmt.Errorf("module directory %s already exists", filepath.Join(i.Dir, i.Name))
}
if err := scaffold(parent.Hermit, kotlinruntime.Files(), i.Dir, i); err != nil {
return err
}
return buildengine.SetPOMProperties(ctx, moduleDir)
}
func unzipToTmpDir(reader *zip.Reader) (string, error) {
tmpDir, err := os.MkdirTemp("", "ftl-init-*")
if err != nil {
return "", err
}
err = internal.UnzipDir(reader, tmpDir)
if err != nil {
return "", err
}
return tmpDir, nil
}
func scaffold(hermit bool, source *zip.Reader, destination string, ctx any, options ...scaffolder.Option) error {
opts := []scaffolder.Option{scaffolder.Functions(scaffoldFuncs), scaffolder.Exclude("^go.mod$")}
if !hermit {
opts = append(opts, scaffolder.Exclude("^bin"))
}
opts = append(opts, options...)
if err := internal.ScaffoldZip(source, destination, ctx, opts...); err != nil {
return fmt.Errorf("failed to scaffold: %w", err)
}
return nil
}
var scaffoldFuncs = template.FuncMap{
"snake": strcase.ToLowerSnake,
"screamingSnake": strcase.ToUpperSnake,
"camel": strcase.ToUpperCamel,
"lowerCamel": strcase.ToLowerCamel,
"kebab": strcase.ToLowerKebab,
"screamingKebab": strcase.ToUpperKebab,
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"typename": schema.TypeName,
}
func updateGitIgnore(dir string) error {
gitRoot := internal.GitRoot(dir)
f, err := os.OpenFile(path.Join(gitRoot, ".gitignore"), os.O_RDWR|os.O_CREATE, 0644) //nolint:gosec
if err != nil {
return err
}
defer f.Close() //nolint:gosec
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if strings.TrimSpace(scanner.Text()) == "**/_ftl" {
return nil
}
}
if scanner.Err() != nil {
return scanner.Err()
}
// append if not already present
_, err = f.WriteString("**/_ftl\n")
return err
}