Skip to content

Commit 9b68da3

Browse files
committed
Improve build info.
1 parent 13b27b6 commit 9b68da3

File tree

6 files changed

+168
-104
lines changed

6 files changed

+168
-104
lines changed

pkg/plugins/builder/builder.go

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9+
"regexp"
910
"runtime"
11+
"strings"
1012
"text/template"
1113

1214
"github.com/launchrctl/launchr"
@@ -23,19 +25,47 @@ type Builder struct {
2325
env *buildEnvironment
2426
}
2527

28+
// ReplacePluginInfo has mod replace information.
29+
type ReplacePluginInfo struct {
30+
Path string
31+
Version string
32+
}
33+
2634
// UsePluginInfo stores plugin info.
2735
type UsePluginInfo struct {
28-
Package string
36+
Path string
2937
Version string
38+
Replace ReplacePluginInfo
39+
}
40+
41+
// UsePluginInfoFromString constructs mod plugin info.
42+
func UsePluginInfoFromString(s string) UsePluginInfo {
43+
pv := strings.SplitN(s, "@", 2)
44+
if len(pv) == 1 {
45+
pv = append(pv, "")
46+
}
47+
return UsePluginInfo{pv[0], pv[1], ReplacePluginInfo{}}
48+
}
49+
50+
// GetVersion returns package version.
51+
func (p UsePluginInfo) GetVersion() string {
52+
if p.Replace.Path != "" {
53+
return p.Replace.Version
54+
}
55+
return p.Version
3056
}
3157

3258
func (p UsePluginInfo) String() string {
33-
return fmt.Sprintf("%s %s", p.Package, p.Version)
59+
v := fmt.Sprintf("%s %s", p.Path, p.Version)
60+
if p.Replace.Path != "" {
61+
v = fmt.Sprintf("%s => %s %s", v, p.Replace.Path, p.Replace.Version)
62+
}
63+
return v
3464
}
3565

3666
// GoGetString provides a package path for a go get.
3767
func (p UsePluginInfo) GoGetString() string {
38-
dep := p.Package
68+
dep := p.Path
3969
if p.Version != "" {
4070
dep += "@" + p.Version
4171
}
@@ -53,6 +83,16 @@ type BuildOptions struct {
5383
Debug bool
5484
}
5585

86+
var validPkgNameRegex = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
87+
88+
// Validate verifies build options.
89+
func (opts *BuildOptions) Validate() error {
90+
if !validPkgNameRegex.MatchString(opts.PkgName) {
91+
return fmt.Errorf(`invalid application name "%s"`, opts.PkgName)
92+
}
93+
return nil
94+
}
95+
5696
type genGoFile struct {
5797
TmplName string
5898
Vars interface{}
@@ -111,12 +151,11 @@ func (b *Builder) Build(ctx context.Context) error {
111151
}
112152

113153
// Generate app version info.
114-
buildVer := b.getBuildVersion(b.LaunchrVersion)
115-
for _, p := range b.Plugins {
116-
if p.Version == "" {
117-
p.Version = b.env.GetPkgVersion(p.Package)
118-
}
154+
b.CorePkg = b.env.GetPkgVersion(b.CorePkg.Path)
155+
for i, p := range b.Plugins {
156+
b.Plugins[i] = b.env.GetPkgVersion(p.Path)
119157
}
158+
buildVer := b.getBuildVersion(b.LaunchrVersion)
120159

121160
// Generate project files.
122161
mainVars := buildVars{
@@ -175,16 +214,21 @@ func (b *Builder) goBuild(ctx context.Context) error {
175214
if err != nil {
176215
return err
177216
}
178-
args := []string{
179-
"build",
180-
"-o",
181-
out,
182-
}
217+
// Set build result file.
218+
args := []string{"build", "-o", out}
219+
// Collect ldflags
220+
ldflags := make([]string, 0, 4)
221+
// Set application name.
222+
ldflags = append(ldflags, "-X", b.CorePkg.Path+".Name="+b.PkgName)
223+
// Include or trim debug information.
183224
if b.Debug {
184225
args = append(args, "-gcflags", "all=-N -l")
185226
} else {
186-
args = append(args, "-ldflags", "-w -s", "-trimpath")
227+
ldflags = append(ldflags, "-s", "-w")
228+
args = append(args, "-trimpath")
187229
}
230+
args = append(args, "-ldflags", strings.Join(ldflags, " "))
231+
// Run build.
188232
cmd := b.env.NewCommand(ctx, b.env.Go(), args...)
189233
err = b.env.RunCmd(ctx, cmd)
190234
if err != nil {
@@ -197,10 +241,10 @@ func (b *Builder) goBuild(ctx context.Context) error {
197241
func (b *Builder) getBuildVersion(version *launchr.AppVersion) *launchr.AppVersion {
198242
bv := *version
199243
bv.Name = b.PkgName
200-
201-
// Get version from the fetched go.mod module
202-
bv.Version = b.env.GetPkgVersion(b.CorePkg.Package)
203-
244+
bv.Version = b.CorePkg.GetVersion()
245+
if bv.Version == "" {
246+
bv.Version = "dev"
247+
}
204248
bv.OS = os.Getenv("GOOS")
205249
bv.Arch = os.Getenv("GOARCH")
206250
if bv.OS == "" {

pkg/plugins/builder/environment.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,27 @@ func (env *buildEnvironment) CreateModFile(ctx context.Context, opts *BuildOptio
8888
}
8989
}
9090

91-
err = env.execGoGet(ctx, opts.CorePkg.GoGetString())
92-
if err != nil {
93-
return err
91+
// Download core.
92+
var coreRepl bool
93+
for repl := range opts.ModReplace {
94+
if strings.HasPrefix(opts.CorePkg.Path, repl) {
95+
coreRepl = true
96+
break
97+
}
98+
}
99+
if !coreRepl {
100+
err = env.execGoGet(ctx, opts.CorePkg.GoGetString())
101+
if err != nil {
102+
return err
103+
}
94104
}
95105

96106
// Download plugins.
97107
nextPlugin:
98108
for _, p := range opts.Plugins {
99109
// Do not get plugins of module subpath.
100110
for repl := range opts.ModReplace {
101-
if strings.HasPrefix(p.Package, repl) {
111+
if strings.HasPrefix(p.Path, repl) {
102112
continue nextPlugin
103113
}
104114
}
@@ -220,7 +230,7 @@ func (env *buildEnvironment) parseGoMod() error {
220230
return nil
221231
}
222232

223-
func (env *buildEnvironment) GetPkgVersion(pkg string) string {
233+
func (env *buildEnvironment) GetPkgVersion(pkg string) UsePluginInfo {
224234
var vrep *modfile.Replace
225235
for _, rep := range env.gomod.Replace {
226236
if strings.HasPrefix(pkg, rep.Old.Path) {
@@ -236,9 +246,19 @@ func (env *buildEnvironment) GetPkgVersion(pkg string) string {
236246
break
237247
}
238248
}
239-
v := vreq.Mod.Version
240-
if vrep != nil && vrep.New.Version != "" {
241-
v = vrep.New.Version
249+
var v UsePluginInfo
250+
if vreq == nil {
251+
v = UsePluginInfo{Path: pkg}
252+
} else {
253+
v = UsePluginInfo{
254+
Path: vreq.Mod.Path,
255+
Version: vreq.Mod.Version,
256+
}
257+
}
258+
259+
if vrep != nil {
260+
v.Replace.Path = vrep.New.Path
261+
v.Replace.Version = vrep.New.Version
242262
}
243263
return v
244264
}

pkg/plugins/builder/plugin.go

Lines changed: 70 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -35,78 +35,82 @@ func (p *Plugin) InitApp(*launchr.App) error {
3535
return nil
3636
}
3737

38+
type builderInput struct {
39+
name string
40+
out string
41+
timeout string
42+
plugins []string
43+
replace []string
44+
debug bool
45+
}
46+
3847
// CobraAddCommands implements launchr.CobraPlugin interface to provide build functionality.
3948
func (p *Plugin) CobraAddCommands(rootCmd *cobra.Command) error {
4049
// Flag options.
41-
var (
42-
name string
43-
out string
44-
timeoutStr string
45-
plugins []string
46-
replace []string
47-
debug bool
48-
)
50+
flags := builderInput{}
4951

5052
var buildCmd = &cobra.Command{
5153
Use: "build",
5254
RunE: func(cmd *cobra.Command, args []string) error {
5355
// Don't show usage help on a runtime error.
5456
cmd.SilenceUsage = true
55-
56-
// Set build timeout.
57-
ctx := cmd.Context()
58-
timeout, err := time.ParseDuration(timeoutStr)
59-
if err != nil {
60-
return err
61-
}
62-
if timeout != 0 {
63-
// Execute build.
64-
var cancel context.CancelFunc
65-
ctx, cancel = context.WithTimeout(ctx, timeout)
66-
defer cancel()
67-
}
68-
69-
// Parse dependencies definition.
70-
allplugs, err := parsePlugins(plugins)
71-
if err != nil {
72-
return err
73-
}
74-
allrepl, err := parseReplace(replace)
75-
if err != nil {
76-
return err
77-
}
78-
79-
// Set output path if it's not defined.
80-
if len(out) == 0 && len(name) > 0 {
81-
out = "./" + name
82-
}
83-
84-
opts := &BuildOptions{
85-
LaunchrVersion: launchr.GetVersion(),
86-
CorePkg: UsePluginInfo{Package: launchrPkg},
87-
PkgName: name,
88-
ModReplace: allrepl,
89-
Plugins: allplugs,
90-
BuildOutput: out,
91-
Debug: debug,
92-
}
93-
94-
return Execute(ctx, opts)
57+
return Execute(cmd.Context(), &flags)
9558
},
9659
}
9760
// Command flags.
98-
buildCmd.Flags().StringVarP(&name, "name", "n", "launchr", `Result application name`)
99-
buildCmd.Flags().StringVarP(&out, "output", "o", "", `Build output file, by default application name is used`)
100-
buildCmd.Flags().StringVarP(&timeoutStr, "timeout", "t", "120s", `Build timeout duration, example: 0, 100ms, 1h23m`)
101-
buildCmd.Flags().StringSliceVarP(&plugins, "plugin", "p", nil, `Include PLUGIN into the build with an optional version`)
102-
buildCmd.Flags().StringSliceVarP(&replace, "replace", "r", nil, `Replace go dependency, see "go mod edit -replace"`)
103-
buildCmd.Flags().BoolVarP(&debug, "debug", "d", false, `Include debug flags into the build to support go debugging with "delve". If not specified, debugging info is trimmed`)
61+
buildCmd.Flags().StringVarP(&flags.name, "name", "n", "launchr", `Result application name`)
62+
buildCmd.Flags().StringVarP(&flags.out, "output", "o", "", `Build output file, by default application name is used`)
63+
buildCmd.Flags().StringVarP(&flags.timeout, "timeout", "t", "120s", `Build timeout duration, example: 0, 100ms, 1h23m`)
64+
buildCmd.Flags().StringSliceVarP(&flags.plugins, "plugin", "p", nil, `Include PLUGIN into the build with an optional version`)
65+
buildCmd.Flags().StringSliceVarP(&flags.replace, "replace", "r", nil, `Replace go dependency, see "go mod edit -replace"`)
66+
buildCmd.Flags().BoolVarP(&flags.debug, "debug", "d", false, `Include debug flags into the build to support go debugging with "delve". If not specified, debugging info is trimmed`)
10467
rootCmd.AddCommand(buildCmd)
10568
return nil
10669
}
10770

10871
// Execute runs launchr and executes build of launchr.
109-
func Execute(ctx context.Context, opts *BuildOptions) error {
72+
func Execute(ctx context.Context, flags *builderInput) error {
73+
// Set build timeout.
74+
timeout, err := time.ParseDuration(flags.timeout)
75+
if err != nil {
76+
return err
77+
}
78+
if timeout != 0 {
79+
// Execute build.
80+
var cancel context.CancelFunc
81+
ctx, cancel = context.WithTimeout(ctx, timeout)
82+
defer cancel()
83+
}
84+
85+
// Parse dependencies definition.
86+
plugins, err := parsePlugins(flags.plugins)
87+
if err != nil {
88+
return err
89+
}
90+
replace, err := parseReplace(flags.replace)
91+
if err != nil {
92+
return err
93+
}
94+
95+
// Set output path if it's not defined.
96+
if len(flags.out) == 0 && len(flags.name) > 0 {
97+
flags.out = "./" + flags.name
98+
}
99+
100+
opts := &BuildOptions{
101+
LaunchrVersion: launchr.GetVersion(),
102+
CorePkg: UsePluginInfo{Path: launchrPkg},
103+
PkgName: flags.name,
104+
ModReplace: replace,
105+
Plugins: plugins,
106+
BuildOutput: flags.out,
107+
Debug: flags.debug,
108+
}
109+
110+
if err = opts.Validate(); err != nil {
111+
return err
112+
}
113+
110114
builder, err := NewBuilder(opts)
111115
if err != nil {
112116
return err
@@ -116,34 +120,28 @@ func Execute(ctx context.Context, opts *BuildOptions) error {
116120
}
117121

118122
func parsePlugins(plugins []string) ([]UsePluginInfo, error) {
119-
// Collect unique plugins. Include default launchr plugins.
120-
defaultPlugins := launchrPkg + "/pkg/plugins"
121-
setplugs := map[string]UsePluginInfo{
122-
defaultPlugins: {defaultPlugins, ""},
123-
}
123+
// Collect unique plugins.
124+
unique := map[string]UsePluginInfo{}
124125
for _, pdef := range plugins {
125-
pv := strings.SplitN(pdef, "@", 2)
126-
if len(pv) == 1 {
127-
pv = append(pv, "")
128-
}
129-
setplugs[pv[0]] = UsePluginInfo{pv[0], pv[1]}
126+
pi := UsePluginInfoFromString(pdef)
127+
unique[pi.Path] = pi
130128
}
131-
allplugs := make([]UsePluginInfo, 0, len(setplugs))
132-
for _, p := range setplugs {
133-
allplugs = append(allplugs, p)
129+
res := make([]UsePluginInfo, 0, len(unique))
130+
for _, p := range unique {
131+
res = append(res, p)
134132
}
135-
return allplugs, nil
133+
return res, nil
136134
}
137135

138136
func parseReplace(replace []string) (map[string]string, error) {
139137
// Replace module dependencies, e.g. with local paths for development or different version.
140-
allrepl := map[string]string{}
138+
repl := map[string]string{}
141139
for _, rdef := range replace {
142140
oldnew := strings.SplitN(rdef, "=", 2)
143141
if len(oldnew) == 1 {
144142
return nil, fmt.Errorf("incorrect replace definition: %s", rdef)
145143
}
146-
allrepl[oldnew[0]] = oldnew[1]
144+
repl[oldnew[0]] = oldnew[1]
147145
}
148-
return allrepl, nil
146+
return repl, nil
149147
}

pkg/plugins/builder/tmpl/gen.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ package main
55
import (
66
"os"
77

8-
"{{.CorePkg.Package}}"
8+
"{{.CorePkg.Path}}"
9+
_ "{{.CorePkg.Path}}/pkg/plugins"
910
{{range .Plugins}}
10-
_ "{{.Package}}"
11+
_ "{{.Path}}"
1112
{{- end}}
1213
)
1314

0 commit comments

Comments
 (0)