diff --git a/release/dist/dist.go b/release/dist/dist.go index 8d9edd8e2..a83db8ce5 100644 --- a/release/dist/dist.go +++ b/release/dist/dist.go @@ -43,6 +43,8 @@ type Build struct { Tmp string // Go is the path to the Go binary to use for building. Go string + // Yarn is the path to the yarn binary to use for building the web client assets. + Yarn string // Version is the version info of the build. Version mkversion.VersionInfo // Time is the timestamp of the build. @@ -79,15 +81,20 @@ func NewBuild(repo, out string) (*Build, error) { if err != nil { return nil, fmt.Errorf("finding module root: %w", err) } - goTool, err := findGo(repo) + goTool, err := findTool(repo, "go") if err != nil { return nil, fmt.Errorf("finding go binary: %w", err) } + yarnTool, err := findTool(repo, "yarn") + if err != nil { + return nil, fmt.Errorf("finding yarn binary: %w", err) + } b := &Build{ Repo: repo, Tmp: tmp, Out: out, Go: goTool, + Yarn: yarnTool, Version: mkversion.Info(), Time: time.Now().UTC(), extra: map[any]any{}, @@ -180,6 +187,24 @@ func (b *Build) TmpDir() string { return ret } +// BuildWebClientAssets builds the JS and CSS assets used by the web client. +// Assets are built in the client/web/build directory and embedded in +// the cmd/tailscale binary. +func (b *Build) BuildWebClientAssets() error { + // Nothing in the web client assets is platform-specific, + // so we only need to build it once. + return b.Once("build-web-client-assets", func() error { + dir := filepath.Join(b.Repo, "client", "web") + if err := b.Command(dir, b.Yarn, "install").Run(); err != nil { + return err + } + if err := b.Command(dir, b.Yarn, "build").Run(); err != nil { + return err + } + return nil + }) +} + // BuildGoBinary builds the Go binary at path and returns the path to the // binary. Builds are cached by path and env, so each build only happens once // per process execution. @@ -303,16 +328,19 @@ func findModRoot(path string) (string, error) { } } -func findGo(path string) (string, error) { - toolGo := filepath.Join(path, "tool/go") - if _, err := os.Stat(toolGo); err == nil { - return toolGo, nil +// findTool returns the path to the specified named tool. +// It first looks in the "tool" directory in the provided path, +// then in the $PATH environment variable. +func findTool(path, name string) (string, error) { + tool := filepath.Join(path, "tool", name) + if _, err := os.Stat(tool); err == nil { + return tool, nil } - toolGo, err := exec.LookPath("go") + tool, err := exec.LookPath(name) if err != nil { return "", err } - return toolGo, nil + return tool, nil } // FilterTargets returns the subset of targets that match any of the filters. diff --git a/release/dist/synology/pkgs.go b/release/dist/synology/pkgs.go index 00660dbba..67deab4d1 100644 --- a/release/dist/synology/pkgs.go +++ b/release/dist/synology/pkgs.go @@ -144,6 +144,9 @@ func getSynologyBuilds(b *dist.Build) *synologyBuilds { func (m *synologyBuilds) buildInnerPackage(b *dist.Build, dsmVersion int, goenv map[string]string) (*innerPkg, error) { key := []any{dsmVersion, goenv} return m.innerPkgs.Do(key, func() (*innerPkg, error) { + if err := b.BuildWebClientAssets(); err != nil { + return nil, err + } ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", goenv) if err != nil { return nil, err diff --git a/release/dist/unixpkgs/pkgs.go b/release/dist/unixpkgs/pkgs.go index 0b45f5c11..9047cd096 100644 --- a/release/dist/unixpkgs/pkgs.go +++ b/release/dist/unixpkgs/pkgs.go @@ -53,6 +53,9 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) { } else { filename = fmt.Sprintf("tailscale_%s_%s_%s.tgz", b.Version.Short, t.os(), t.arch()) } + if err := b.BuildWebClientAssets(); err != nil { + return nil, err + } ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", t.goEnv) if err != nil { return nil, err @@ -193,6 +196,9 @@ func (t *debTarget) Build(b *dist.Build) ([]string, error) { return nil, errors.New("deb only supported on linux") } + if err := b.BuildWebClientAssets(); err != nil { + return nil, err + } ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", t.goEnv) if err != nil { return nil, err @@ -305,6 +311,9 @@ func (t *rpmTarget) Build(b *dist.Build) ([]string, error) { return nil, errors.New("rpm only supported on linux") } + if err := b.BuildWebClientAssets(); err != nil { + return nil, err + } ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", t.goEnv) if err != nil { return nil, err diff --git a/tool/yarn b/tool/yarn index 48c61c564..6357beda6 100755 --- a/tool/yarn +++ b/tool/yarn @@ -21,7 +21,7 @@ fi cachedir="$HOME/.cache/tailscale-yarn" tarball="${cachedir}.tar.gz" - read -r want_rev < "$(dirname "$0")/yarn.rev" + read -r want_rev < "./tool/yarn.rev" got_rev="" if [[ -x "${cachedir}/bin/yarn" ]]; then