Skip to content

Commit 51bb741

Browse files
committedJun 28, 2023
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 3d6954f commit 51bb741

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
@@ -589,6 +598,7 @@ github.com/ServiceWeaver/weaver/internal/tool/multi
589598
github.com/ServiceWeaver/weaver/internal/proxy
590599
github.com/ServiceWeaver/weaver/internal/routing
591600
github.com/ServiceWeaver/weaver/internal/status
601+
github.com/ServiceWeaver/weaver/internal/tool
592602
github.com/ServiceWeaver/weaver/internal/tool/certs
593603
github.com/ServiceWeaver/weaver/internal/tool/config
594604
github.com/ServiceWeaver/weaver/runtime
@@ -629,6 +639,7 @@ github.com/ServiceWeaver/weaver/internal/tool/single
629639
fmt
630640
github.com/ServiceWeaver/weaver/internal/must
631641
github.com/ServiceWeaver/weaver/internal/status
642+
github.com/ServiceWeaver/weaver/internal/tool
632643
github.com/ServiceWeaver/weaver/internal/tool/config
633644
github.com/ServiceWeaver/weaver/runtime
634645
github.com/ServiceWeaver/weaver/runtime/codegen
@@ -650,6 +661,7 @@ github.com/ServiceWeaver/weaver/internal/tool/ssh
650661
flag
651662
fmt
652663
github.com/ServiceWeaver/weaver/internal/status
664+
github.com/ServiceWeaver/weaver/internal/tool
653665
github.com/ServiceWeaver/weaver/internal/tool/config
654666
github.com/ServiceWeaver/weaver/internal/tool/ssh/impl
655667
github.com/ServiceWeaver/weaver/runtime
@@ -743,7 +755,10 @@ github.com/ServiceWeaver/weaver/runtime/bin
743755
debug/pe
744756
fmt
745757
github.com/ServiceWeaver/weaver/runtime/codegen
758+
github.com/ServiceWeaver/weaver/runtime/version
746759
os
760+
regexp
761+
strconv
747762
github.com/ServiceWeaver/weaver/runtime/bin/testprogram
748763
context
749764
github.com/ServiceWeaver/weaver
@@ -762,6 +777,7 @@ github.com/ServiceWeaver/weaver/runtime/codegen
762777
github.com/ServiceWeaver/weaver/metrics
763778
github.com/ServiceWeaver/weaver/runtime
764779
github.com/ServiceWeaver/weaver/runtime/protos
780+
github.com/ServiceWeaver/weaver/runtime/version
765781
go.opentelemetry.io/otel/trace
766782
google.golang.org/protobuf/proto
767783
math
@@ -909,30 +925,17 @@ github.com/ServiceWeaver/weaver/runtime/tool
909925
errors
910926
flag
911927
fmt
912-
github.com/ServiceWeaver/weaver/runtime/codegen
913928
github.com/ServiceWeaver/weaver/runtime/colors
914929
github.com/ServiceWeaver/weaver/runtime/logging
915-
github.com/ServiceWeaver/weaver/runtime/version
916930
io
917931
os
918932
os/exec
919-
runtime
920-
runtime/debug
921933
sort
922934
strings
923935
text/template
924936
time
925937
github.com/ServiceWeaver/weaver/runtime/version
926938
fmt
927-
github.com/ServiceWeaver/weaver/runtime/bin
928-
regexp
929-
strconv
930-
github.com/ServiceWeaver/weaver/runtime/version/testprogram
931-
context
932-
github.com/ServiceWeaver/weaver
933-
github.com/ServiceWeaver/weaver/runtime/codegen
934-
go.opentelemetry.io/otel/trace
935-
reflect
936939
github.com/ServiceWeaver/weaver/weavertest
937940
context
938941
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+
}

‎runtime/bin/bin_test.go

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

15-
package bin_test
15+
package bin
1616

1717
import (
1818
"fmt"
@@ -21,18 +21,13 @@ import (
2121
"path/filepath"
2222
"testing"
2323

24-
"github.com/ServiceWeaver/weaver/runtime/bin"
2524
"github.com/ServiceWeaver/weaver/runtime/codegen"
25+
"github.com/ServiceWeaver/weaver/runtime/version"
2626
"github.com/google/go-cmp/cmp"
2727
)
2828

2929
func TestReadComponentGraph(t *testing.T) {
30-
type testCase struct {
31-
os string
32-
arch string
33-
}
34-
35-
for _, test := range []testCase{
30+
for _, test := range []struct{ os, arch string }{
3631
{"linux", "amd64"},
3732
{"windows", "amd64"},
3833
{"darwin", "arm64"},
@@ -43,13 +38,12 @@ func TestReadComponentGraph(t *testing.T) {
4338
binary := filepath.Join(d, "bin")
4439
cmd := exec.Command("go", "build", "-o", binary, "./testprogram")
4540
cmd.Env = append(os.Environ(), "GOOS="+test.os, "GOARCH="+test.arch)
46-
err := cmd.Run()
47-
if err != nil {
41+
if err := cmd.Run(); err != nil {
4842
t.Fatal(err)
4943
}
5044

5145
// Read edges.
52-
edges, err := bin.ReadComponentGraph(binary)
46+
edges, err := ReadComponentGraph(binary)
5347
if err != nil {
5448
t.Fatal(err)
5549
}
@@ -87,12 +81,7 @@ func TestReadComponentGraph(t *testing.T) {
8781
}
8882

8983
func TestReadListeners(t *testing.T) {
90-
type testCase struct {
91-
os string
92-
arch string
93-
}
94-
95-
for _, test := range []testCase{
84+
for _, test := range []struct{ os, arch string }{
9685
{"linux", "amd64"},
9786
{"windows", "amd64"},
9887
{"darwin", "arm64"},
@@ -109,7 +98,7 @@ func TestReadListeners(t *testing.T) {
10998
}
11099

111100
// Read listeners.
112-
actual, err := bin.ReadListeners(binary)
101+
actual, err := ReadListeners(binary)
113102
if err != nil {
114103
t.Fatal(err)
115104
}
@@ -131,3 +120,55 @@ func TestReadListeners(t *testing.T) {
131120
})
132121
}
133122
}
123+
124+
func TestExtractDeployerVersion(t *testing.T) {
125+
for _, want := range []version.SemVer{
126+
{Major: 0, Minor: 0, Patch: 0},
127+
{Major: 10, Minor: 10, Patch: 10},
128+
{Major: 123, Minor: 4567, Patch: 891011},
129+
} {
130+
t.Run(want.String(), func(t *testing.T) {
131+
// Embed the version string inside a big array of bytes.
132+
var bytes [10000]byte
133+
embedded := fmt.Sprintf("⟦wEaVeRdEpLoYeRvErSiOn:%s⟧", want)
134+
copy(bytes[1234:], []byte(embedded))
135+
136+
// Extract the version string.
137+
got, err := extractDeployerVersion(bytes[:])
138+
if err != nil {
139+
t.Fatal(err)
140+
}
141+
if got != want {
142+
t.Fatalf("bad deployer API version: got %s, want %s", got, want)
143+
}
144+
})
145+
}
146+
}
147+
148+
func TestReadVersion(t *testing.T) {
149+
for _, test := range []struct{ os, arch string }{
150+
{"linux", "amd64"},
151+
{"windows", "amd64"},
152+
{"darwin", "arm64"},
153+
} {
154+
t.Run(fmt.Sprintf("%s/%s", test.os, test.arch), func(t *testing.T) {
155+
// Build the binary for os/arch.
156+
d := t.TempDir()
157+
binary := filepath.Join(d, "bin")
158+
cmd := exec.Command("go", "build", "-o", binary, "./testprogram")
159+
cmd.Env = append(os.Environ(), "GOOS="+test.os, "GOARCH="+test.arch)
160+
if err := cmd.Run(); err != nil {
161+
t.Fatal(err)
162+
}
163+
164+
// Read version.
165+
got, err := ReadDeployerVersion(binary)
166+
if err != nil {
167+
t.Fatal(err)
168+
}
169+
if got != version.DeployerVersion {
170+
t.Fatalf("bad dipe version: got %s, want %s", got, version.DeployerVersion)
171+
}
172+
})
173+
}
174+
}

‎runtime/codegen/version.go

+2-44
Original file line numberDiff line numberDiff line change
@@ -14,49 +14,7 @@
1414

1515
package codegen
1616

17-
const (
18-
// The version of the codegen API in semantic version format (Major.Minor).
19-
//
20-
// We need to make sure that the code generated by 'weaver generate' is in
21-
// sync with the weaver module version compiled into a developer's binary.
22-
// For example, 'weaver generate' may generate code using a function in the
23-
// codegen package that doesn't yet exist in the version of the codegen
24-
// package linked into the developer's binary.
25-
//
26-
// We also need to make sure that all generated weaver_gen.go files are
27-
// compatible. For example, if we change the RPC wire protocol, we have to
28-
// ensure that every weaver_gen.go file is re-generated to use the new wire
29-
// protocol.
30-
//
31-
// To address these issues, we introduce a codegen version. Every time we
32-
// make a change to how code is generated---e.g., we change our wire
33-
// protocol, we change which codegen functions get called---we increment
34-
// the codegen version.
35-
//
36-
// In the future, we have to be careful to make backwards compatible
37-
// changes and ensure different versions work with each other. In the short
38-
// term, we make the restriction that all weaver_gen.go files use the same
39-
// codegen API version linked into the developer's binary.
40-
//
41-
// We could assign the codegen API versions v1, v2, v3, and so on.
42-
// However, this makes it hard to understand the relationship between the
43-
// codegen API version and the version of the Service Weaver module. (What
44-
// version of Service Weaver do I need to install to get version 7 of the
45-
// codegen API?)
46-
//
47-
// Instead, we use Service Weaver module versions as codegen API versions.
48-
// For example, if we change the codegen API in v0.12.0 of Service Weaver,
49-
// then we update the codegen API version to v0.12.0. If we don't change
50-
// the codegen API in v0.13.0 of Service Weaver, then we leave the codegen
51-
// API at v0.12.0.
52-
//
53-
// TODO(mwhittaker): Write a doc explaining this version as well as the
54-
// module version and deployer API version.
55-
Major = 0
56-
Minor = 17
57-
// NOTE: Patch is omitted because all API changes should bump the major or
58-
// minor version. Patch is assumed to be 0.
59-
)
17+
import "github.com/ServiceWeaver/weaver/runtime/version"
6018

6119
// The following types are used to check, at compile time, that every
6220
// weaver_gen.go file uses the codegen API version that is linked into the
@@ -77,4 +35,4 @@ const (
7735
// (this is "..." above). Again note that we ignore the patch number.
7836

7937
type Version[_ any] string
80-
type LatestVersion = Version[[Major][Minor]struct{}]
38+
type LatestVersion = Version[[version.CodegenMajor][version.CodegenMinor]struct{}]

‎runtime/tool/version.go

-59
This file was deleted.

‎runtime/version/testprogram/main.go

-32
This file was deleted.

‎runtime/version/testprogram/weaver_gen.go

-72
This file was deleted.

‎runtime/version/version.go

+47-65
Original file line numberDiff line numberDiff line change
@@ -12,90 +12,72 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
// Package version contains code related to deployer API versioning.
15+
// Package version contains the version of the weaver module and its
16+
// constituent APIs (e.g., the pipe API, the codegen API).
1617
package version
1718

1819
import (
1920
"fmt"
20-
"regexp"
21-
"strconv"
22-
23-
"github.com/ServiceWeaver/weaver/runtime/bin"
2421
)
2522

23+
// TODO(mwhittaker): Write a doc explaining versioning in detail. Include
24+
// Srdjan's comments in PR #219.
25+
2626
const (
27-
// The version of the deployer API---aka pipe API---in semantic version
28-
// format (Major.Minor.Patch).
27+
// The weaver module semantic version [1].
28+
//
29+
// [1]: https://go.dev/doc/modules/version-numbers
30+
ModuleMajor = 0
31+
ModuleMinor = 17
32+
ModulePatch = 0
33+
34+
// Note that there is currently no way to programmatically get the version
35+
// of the current module [1], so we have to manually update the module
36+
// version here whenever we release a new version.
2937
//
30-
// Every time we make a change to deployer API, we assign it a new version.
31-
// When an envelope spawns a weavelet, the weavelet reports this version to
32-
// the envelope. The envelope then errors out if it is not compatible with
33-
// the reported version.
38+
// [1]: https://github.com/golang/go/issues/29228
39+
40+
// The version of the deployer API.
3441
//
35-
// We could assign the deployer API versions v1, v2, v3, and so on.
36-
// However, this makes it hard to understand the relationship between the
37-
// deployer API version and the version of the Service Weaver module. (What
38-
// version of Service Weaver do I need to install to get version 7 of the
39-
// pipe?)
42+
// Every time we make a change to the deployer API, we assign it a new
43+
// version. We could assign the deployer API versions v1, v2, v3, and so
44+
// on. However, this makes it hard to understand the relationship between
45+
// the deployer API version and the version of the Service Weaver module.
4046
//
4147
// Instead, we use Service Weaver module versions as deployer API versions.
4248
// For example, if we change the deployer API in v0.12.0 of Service Weaver,
4349
// then we update the deployer API version to v0.12.0. If we don't change
4450
// the deployer API in v0.13.0 of Service Weaver, then we leave the
4551
// deployer API at v0.12.0.
46-
//
47-
// TODO(mwhittaker): Write a doc explaining versioning in detail. Include
48-
// Srdjan's comments in PR #219.
49-
Major = 0
50-
Minor = 14
51-
Patch = 0
52+
DeployerMajor = 0
53+
DeployerMinor = 14
54+
55+
// The version of the codegen API. As with the deployer API, we assign a
56+
// new version every time we change how code is generated, and we use
57+
// weaver module versions.
58+
CodegenMajor = 0
59+
CodegenMinor = 17
5260
)
5361

54-
// version exists to embed the deployer API version into a Service Weaver
55-
// binary. We split declaring and assigning version to prevent the compiler
56-
// from erasing it.
57-
//
58-
// NOTE that version should be initialized with a hardcoded string that should
59-
// reflect the values of Major, Minor and Patch.
60-
//
61-
//nolint:unused
62-
var version string
62+
var (
63+
// The weaver module version.
64+
ModuleVersion = SemVer{ModuleMajor, ModuleMinor, ModulePatch}
6365

64-
func init() {
65-
// Make sure that the hardcoded string reflects the values of Major, Minor and
66-
// Patch.
67-
version = "⟦wEaVeRvErSiOn:0.14.0⟧"
68-
}
66+
// The deployer API version.
67+
DeployerVersion = SemVer{DeployerMajor, DeployerMinor, 0}
68+
69+
// The codegen API version.
70+
CodegenVersion = SemVer{CodegenMajor, CodegenMinor, 0}
71+
)
6972

70-
// ReadVersion reads version (major, minor, patch) from the specified binary.
71-
func ReadVersion(filename string) (int, int, int, error) {
72-
data, err := bin.ROData(filename)
73-
if err != nil {
74-
return 0, 0, 0, err
75-
}
76-
return extractVersion(data)
73+
// SemVer is a semantic version. See https://go.dev/doc/modules/version-numbers
74+
// for details.
75+
type SemVer struct {
76+
Major int
77+
Minor int
78+
Patch int
7779
}
7880

79-
// extractVersion returns the version (major, minor, patch) corresponding to
80-
// MakeVersionString() embedded in data.
81-
func extractVersion(data []byte) (int, int, int, error) {
82-
re := regexp.MustCompile(`⟦wEaVeRvErSiOn:([0-9]*?)\.([0-9]*?)\.([0-9]*?)⟧`)
83-
m := re.FindSubmatch(data)
84-
if m == nil {
85-
return 0, 0, 0, fmt.Errorf("version not found")
86-
}
87-
major, minor, patch := string(m[1]), string(m[2]), string(m[3])
88-
ma, err := strconv.Atoi(major)
89-
if err != nil {
90-
return 0, 0, 0, fmt.Errorf("invalid major %q: %w", major, err)
91-
}
92-
mi, err := strconv.Atoi(minor)
93-
if err != nil {
94-
return 0, 0, 0, fmt.Errorf("invalid minor %q: %w", minor, err)
95-
}
96-
pa, err := strconv.Atoi(patch)
97-
if err != nil {
98-
return 0, 0, 0, fmt.Errorf("invalid patch %q: %w", patch, err)
99-
}
100-
return ma, mi, pa, nil
81+
func (s SemVer) String() string {
82+
return fmt.Sprintf("v%d.%d.%d", s.Major, s.Minor, s.Patch)
10183
}

‎runtime/version/version_test.go

-89
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.