Skip to content

Commit 3baf984

Browse files
committed
Added hardcoded weaver module version.
> Module Version We want a `weaver version` command that prints out the weaver module version the `weaver` binary was built with, or failing that, the commit at which the binary was built. Unfortunately, both of these things are hard. There is currently no nice way to automatically get the version of the main module in a go program [1]. There is a way to get the git commit using `debug.ReadBuildInfo()` [2], but when `go install`ing a binary, the version control information is stripped. Browsing existing open source projects, it seems the standard practice is to hard code the module version in the code. This PR does that and updates the `weaver version` command to use it: ``` $ weaver version weaver v0.17.0 linux/amd64 ``` > Other Versions The weaver repo has two other versioned APIs: the deployer API version and the codegen version. Currently, the deployer API version is the latest module version where the deployer API changed (and the same for the codegen version). We discussed offline the idea of replacing the three versions (module, deployer API, codegen) with just the module version. Then, we could write additional code to check version compatibility. Is codegen v0.17.3 incompatible with v0.12.0, for example? When trying to implement this, however, I ran into some problems. For example, let's say a deployer is at version v0.10.0 and tries to deploy an app at version v0.12.0. Is deployer API version v0.12.0 compatible with version v0.10.0? Well, the deployer was written before v0.12.0 was even created, so it doesn't have a good way to know. The codegen version is also tricky because it relies on some compiler tricks to prevent an app from compiling if it has code generated with a stale version of `weaver generate`. I'm not sure how to implement these tricks without hardcoding a codegen version. Because of these challenges, I decided to stick with our current approach to versioning, for now at least. To clean things up a bit though, I moved all versioning related code to `runtime/version.go`. I also moved some code to the `bin` package because it felt more appropriate there. [1]: golang/go#29228 [2]: https://pkg.go.dev/runtime/debug#ReadBuildInfo
1 parent f8bf9ff commit 3baf984

File tree

17 files changed

+241
-415
lines changed

17 files changed

+241
-415
lines changed

cmd/weaver/main.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"os/exec"
2626
"strings"
2727

28+
itool "github.com/ServiceWeaver/weaver/internal/tool"
2829
"github.com/ServiceWeaver/weaver/internal/tool/callgraph"
2930
"github.com/ServiceWeaver/weaver/internal/tool/generate"
3031
"github.com/ServiceWeaver/weaver/internal/tool/multi"
@@ -84,11 +85,12 @@ func main() {
8485
return
8586

8687
case "version":
87-
cmd := tool.VersionCmd("weaver")
88+
cmd := itool.VersionCmd("weaver")
8889
if err := cmd.Fn(context.Background(), flag.Args()[1:]); err != nil {
8990
fmt.Fprintln(os.Stderr, err)
9091
os.Exit(1)
9192
}
93+
return
9294

9395
case "callgraph":
9496
const usage = `Generate component callgraphs.

godeps.txt

+16-13
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ github.com/ServiceWeaver/weaver/cmd/weaver
5959
errors
6060
flag
6161
fmt
62+
github.com/ServiceWeaver/weaver/internal/tool
6263
github.com/ServiceWeaver/weaver/internal/tool/callgraph
6364
github.com/ServiceWeaver/weaver/internal/tool/generate
6465
github.com/ServiceWeaver/weaver/internal/tool/multi
@@ -517,6 +518,13 @@ github.com/ServiceWeaver/weaver/internal/status
517518
syscall
518519
text/template
519520
time
521+
github.com/ServiceWeaver/weaver/internal/tool
522+
context
523+
flag
524+
fmt
525+
github.com/ServiceWeaver/weaver/runtime/tool
526+
github.com/ServiceWeaver/weaver/runtime/version
527+
runtime
520528
github.com/ServiceWeaver/weaver/internal/tool/callgraph
521529
fmt
522530
github.com/ServiceWeaver/weaver/runtime/bin
@@ -550,6 +558,7 @@ github.com/ServiceWeaver/weaver/internal/tool/generate
550558
github.com/ServiceWeaver/weaver/internal/files
551559
github.com/ServiceWeaver/weaver/runtime/codegen
552560
github.com/ServiceWeaver/weaver/runtime/colors
561+
github.com/ServiceWeaver/weaver/runtime/version
553562
go/ast
554563
go/format
555564
go/parser
@@ -579,6 +588,7 @@ github.com/ServiceWeaver/weaver/internal/tool/multi
579588
github.com/ServiceWeaver/weaver/internal/proxy
580589
github.com/ServiceWeaver/weaver/internal/routing
581590
github.com/ServiceWeaver/weaver/internal/status
591+
github.com/ServiceWeaver/weaver/internal/tool
582592
github.com/ServiceWeaver/weaver/internal/tool/certs
583593
github.com/ServiceWeaver/weaver/internal/tool/config
584594
github.com/ServiceWeaver/weaver/runtime
@@ -619,6 +629,7 @@ github.com/ServiceWeaver/weaver/internal/tool/single
619629
fmt
620630
github.com/ServiceWeaver/weaver/internal/must
621631
github.com/ServiceWeaver/weaver/internal/status
632+
github.com/ServiceWeaver/weaver/internal/tool
622633
github.com/ServiceWeaver/weaver/internal/tool/config
623634
github.com/ServiceWeaver/weaver/runtime
624635
github.com/ServiceWeaver/weaver/runtime/codegen
@@ -640,6 +651,7 @@ github.com/ServiceWeaver/weaver/internal/tool/ssh
640651
flag
641652
fmt
642653
github.com/ServiceWeaver/weaver/internal/status
654+
github.com/ServiceWeaver/weaver/internal/tool
643655
github.com/ServiceWeaver/weaver/internal/tool/config
644656
github.com/ServiceWeaver/weaver/internal/tool/ssh/impl
645657
github.com/ServiceWeaver/weaver/runtime
@@ -733,7 +745,10 @@ github.com/ServiceWeaver/weaver/runtime/bin
733745
debug/pe
734746
fmt
735747
github.com/ServiceWeaver/weaver/runtime/codegen
748+
github.com/ServiceWeaver/weaver/runtime/version
736749
os
750+
regexp
751+
strconv
737752
github.com/ServiceWeaver/weaver/runtime/bin/testprogram
738753
context
739754
github.com/ServiceWeaver/weaver
@@ -752,6 +767,7 @@ github.com/ServiceWeaver/weaver/runtime/codegen
752767
github.com/ServiceWeaver/weaver/metrics
753768
github.com/ServiceWeaver/weaver/runtime
754769
github.com/ServiceWeaver/weaver/runtime/protos
770+
github.com/ServiceWeaver/weaver/runtime/version
755771
go.opentelemetry.io/otel/trace
756772
google.golang.org/protobuf/proto
757773
math
@@ -899,30 +915,17 @@ github.com/ServiceWeaver/weaver/runtime/tool
899915
errors
900916
flag
901917
fmt
902-
github.com/ServiceWeaver/weaver/runtime/codegen
903918
github.com/ServiceWeaver/weaver/runtime/colors
904919
github.com/ServiceWeaver/weaver/runtime/logging
905-
github.com/ServiceWeaver/weaver/runtime/version
906920
io
907921
os
908922
os/exec
909-
runtime
910-
runtime/debug
911923
sort
912924
strings
913925
text/template
914926
time
915927
github.com/ServiceWeaver/weaver/runtime/version
916928
fmt
917-
github.com/ServiceWeaver/weaver/runtime/bin
918-
regexp
919-
strconv
920-
github.com/ServiceWeaver/weaver/runtime/version/testprogram
921-
context
922-
github.com/ServiceWeaver/weaver
923-
github.com/ServiceWeaver/weaver/runtime/codegen
924-
go.opentelemetry.io/otel/trace
925-
reflect
926929
github.com/ServiceWeaver/weaver/weavertest
927930
context
928931
errors

internal/envelope/conn/envelope_conn.go

+5-10
Original file line numberDiff line numberDiff line change
@@ -381,18 +381,13 @@ func verifyWeaveletInfo(wlet *protos.WeaveletInfo) error {
381381
// checkVersion checks that the deployer API version the deployer was built
382382
// with is compatible with the deployer API version the app was built with,
383383
// erroring out if they are not compatible.
384-
func checkVersion(appVersion *protos.SemVer) error {
385-
if appVersion == nil {
384+
func checkVersion(v *protos.SemVer) error {
385+
if v == nil {
386386
return fmt.Errorf("version mismatch: nil app version")
387387
}
388-
if appVersion.Major != version.Major ||
389-
appVersion.Minor != version.Minor ||
390-
appVersion.Patch != version.Patch {
391-
return fmt.Errorf(
392-
"version mismatch: deployer version %d.%d.%d is incompatible with app version %d.%d.%d.",
393-
version.Major, version.Minor, version.Patch,
394-
appVersion.Major, appVersion.Minor, appVersion.Patch,
395-
)
388+
got := version.SemVer{Major: int(v.Major), Minor: int(v.Minor), Patch: int(v.Patch)}
389+
if got != version.DeployerVersion {
390+
return fmt.Errorf("version mismatch: deployer's deployer API version %s is incompatible with app' deployer API version %s.", version.DeployerVersion, got)
396391
}
397392
return nil
398393
}

internal/envelope/conn/weavelet_conn.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ func NewWeaveletConn(r io.ReadCloser, w io.WriteCloser, h WeaveletHandler) (*Wea
102102
DialAddr: dialAddr,
103103
Pid: int64(os.Getpid()),
104104
Version: &protos.SemVer{
105-
Major: version.Major,
106-
Minor: version.Minor,
107-
Patch: version.Patch,
105+
Major: version.DeployerMajor,
106+
Minor: version.DeployerMinor,
107+
Patch: 0,
108108
},
109109
}
110110
if err := wc.conn.send(&protos.WeaveletMsg{WeaveletInfo: wc.winfo}); err != nil {

internal/tool/generate/generator.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/ServiceWeaver/weaver/internal/files"
3838
"github.com/ServiceWeaver/weaver/runtime/codegen"
3939
"github.com/ServiceWeaver/weaver/runtime/colors"
40+
"github.com/ServiceWeaver/weaver/runtime/version"
4041
"golang.org/x/exp/maps"
4142
"golang.org/x/tools/go/packages"
4243
"golang.org/x/tools/go/types/typeutil"
@@ -928,8 +929,8 @@ func (g *generator) generateVersionCheck(p printFn) {
928929
// var _ codegen.LatestVersion = codegen.Version[[0][1]struct{}]("You used ...")
929930
p(`var _ %s = %s[[%d][%d]struct{}](%q)`,
930931
g.codegen().qualify("LatestVersion"), g.codegen().qualify("Version"),
931-
codegen.Major, codegen.Minor,
932-
fmt.Sprintf(`You used 'weaver generate' codegen version %d.%d.0, but you built your code with an incompatible weaver module version. Try upgrading 'weaver generate' and re-running it.`, codegen.Major, codegen.Minor),
932+
version.CodegenMajor, version.CodegenMinor,
933+
fmt.Sprintf(`You used 'weaver generate' codegen version %d.%d.0, but you built your code with an incompatible weaver module version. Try upgrading 'weaver generate' and re-running it.`, version.CodegenMajor, version.CodegenMinor),
933934
)
934935
}
935936

internal/tool/multi/multi.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/ServiceWeaver/weaver/internal/must"
2323
"github.com/ServiceWeaver/weaver/internal/status"
24+
itool "github.com/ServiceWeaver/weaver/internal/tool"
2425
"github.com/ServiceWeaver/weaver/runtime"
2526
"github.com/ServiceWeaver/weaver/runtime/logging"
2627
"github.com/ServiceWeaver/weaver/runtime/tool"
@@ -66,6 +67,6 @@ var (
6667
"metrics": status.MetricsCommand("weaver multi", defaultRegistry),
6768
"profile": status.ProfileCommand("weaver multi", defaultRegistry),
6869
"purge": tool.PurgeCmd(purgeSpec),
69-
"version": tool.VersionCmd("weaver multi"),
70+
"version": itool.VersionCmd("weaver multi"),
7071
}
7172
)

internal/tool/single/single.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/ServiceWeaver/weaver/internal/must"
2323
"github.com/ServiceWeaver/weaver/internal/status"
24+
itool "github.com/ServiceWeaver/weaver/internal/tool"
2425
"github.com/ServiceWeaver/weaver/runtime"
2526
"github.com/ServiceWeaver/weaver/runtime/tool"
2627
)
@@ -55,7 +56,7 @@ var (
5556
"metrics": status.MetricsCommand("weaver single", defaultRegistry),
5657
"profile": status.ProfileCommand("weaver single", defaultRegistry),
5758
"purge": tool.PurgeCmd(purgeSpec),
58-
"version": tool.VersionCmd("weaver single"),
59+
"version": itool.VersionCmd("weaver single"),
5960
}
6061
)
6162

internal/tool/ssh/ssh.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package ssh
1616

1717
import (
1818
"github.com/ServiceWeaver/weaver/internal/status"
19+
itool "github.com/ServiceWeaver/weaver/internal/tool"
1920
"github.com/ServiceWeaver/weaver/runtime/tool"
2021
)
2122

@@ -24,7 +25,7 @@ var (
2425
"deploy": &deployCmd,
2526
"logs": tool.LogsCmd(&logsSpec),
2627
"dashboard": status.DashboardCommand(dashboardSpec),
27-
"version": tool.VersionCmd("weaver ssh"),
28+
"version": itool.VersionCmd("weaver ssh"),
2829

2930
// Hidden commands.
3031
"babysitter": &babysitterCmd,

internal/tool/version.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package tool
16+
17+
import (
18+
"context"
19+
"flag"
20+
"fmt"
21+
"runtime"
22+
23+
"github.com/ServiceWeaver/weaver/runtime/tool"
24+
"github.com/ServiceWeaver/weaver/runtime/version"
25+
)
26+
27+
// VersionCmd returns a command to show a deployer's version.
28+
func VersionCmd(toolname string) *tool.Command {
29+
return &tool.Command{
30+
Name: "version",
31+
Flags: flag.NewFlagSet("version", flag.ContinueOnError),
32+
Description: fmt.Sprintf("Show %q version", toolname),
33+
Help: fmt.Sprintf("Usage:\n %s version", toolname),
34+
Fn: func(context.Context, []string) error {
35+
fmt.Printf("%s %s %s/%s\n", toolname, version.ModuleVersion, runtime.GOOS, runtime.GOARCH)
36+
return nil
37+
},
38+
}
39+
}

runtime/bin/bin.go

+58-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
// Package bin contains code to extract data from a Service Weaver binary.
1516
package bin
1617

1718
import (
@@ -21,12 +22,30 @@ import (
2122
"debug/pe"
2223
"fmt"
2324
"os"
25+
"regexp"
26+
"strconv"
2427

2528
"github.com/ServiceWeaver/weaver/runtime/codegen"
29+
"github.com/ServiceWeaver/weaver/runtime/version"
2630
)
2731

28-
// ROData returns the read-only data section of the provided binary.
29-
func ROData(file string) ([]byte, error) {
32+
// deployerVersion exists to embed the deployer API version into a Service
33+
// Weaver binary. We split declaring and assigning version to prevent the
34+
// compiler from erasing it.
35+
//
36+
//nolint:unused
37+
var deployerVersion string
38+
39+
func init() {
40+
// NOTE that deployerVersion must be assigned a string constant that
41+
// reflects the values of version.DeployerMajor and version.DeployerMinor.
42+
// If the string is not a constant---if we try to use fmt.Sprintf, for
43+
// example---it will not be embedded in a Service Weaver binary.
44+
deployerVersion = "⟦wEaVeRdEpLoYeRvErSiOn:v0.14.0⟧"
45+
}
46+
47+
// rodata returns the read-only data section of the provided binary.
48+
func rodata(file string) ([]byte, error) {
3049
f, err := os.Open(file)
3150
if err != nil {
3251
return nil, err
@@ -76,7 +95,7 @@ func ROData(file string) ([]byte, error) {
7695
//
7796
// github.com/ServiceWeaver/weaver/Main
7897
func ReadComponentGraph(file string) ([][2]string, error) {
79-
data, err := ROData(file)
98+
data, err := rodata(file)
8099
if err != nil {
81100
return nil, err
82101
}
@@ -86,9 +105,44 @@ func ReadComponentGraph(file string) ([][2]string, error) {
86105
// ReadListeners reads the sets of listeners associated with each component
87106
// in the specified binary.
88107
func ReadListeners(file string) ([]codegen.ComponentListeners, error) {
89-
data, err := ROData(file)
108+
data, err := rodata(file)
90109
if err != nil {
91110
return nil, err
92111
}
93112
return codegen.ExtractListeners(data), nil
94113
}
114+
115+
// ReadDeployerVersion reads the deployer API version from the specified binary.
116+
func ReadDeployerVersion(filename string) (version.SemVer, error) {
117+
data, err := rodata(filename)
118+
if err != nil {
119+
return version.SemVer{}, err
120+
}
121+
return extractDeployerVersion(data)
122+
}
123+
124+
// extractDeployerVersion returns the deployer API version embedded in data.
125+
func extractDeployerVersion(data []byte) (version.SemVer, error) {
126+
re := regexp.MustCompile(`⟦wEaVeRdEpLoYeRvErSiOn:v([0-9]*?)\.([0-9]*?)\.([0-9]*?)⟧`)
127+
m := re.FindSubmatch(data)
128+
if m == nil {
129+
return version.SemVer{}, fmt.Errorf("embedded deployer API version not found")
130+
}
131+
major, minor, patch := string(m[1]), string(m[2]), string(m[3])
132+
133+
v := version.SemVer{}
134+
var err error
135+
v.Major, err = strconv.Atoi(major)
136+
if err != nil {
137+
return version.SemVer{}, fmt.Errorf("invalid embedded deployer API major %q: %w", major, err)
138+
}
139+
v.Minor, err = strconv.Atoi(minor)
140+
if err != nil {
141+
return version.SemVer{}, fmt.Errorf("invalid embedded deployer API minor %q: %w", minor, err)
142+
}
143+
v.Patch, err = strconv.Atoi(patch)
144+
if err != nil {
145+
return version.SemVer{}, fmt.Errorf("invalid embedded deployer API patch %q: %w", patch, err)
146+
}
147+
return v, nil
148+
}

0 commit comments

Comments
 (0)