From 3722b0546518f0373e92a6da841b0091e28e3a29 Mon Sep 17 00:00:00 2001 From: Will Norris Date: Mon, 21 Aug 2023 16:37:54 -0700 Subject: [PATCH] release/dist: run yarn build before building CLI This builds the assets for the new web client as part of our release process. The path to the web client source is specified by the -web-client-root flag. This allows corp builds to first vendor the tailscale.com module, and then build the web client assets in the vendor directory. The default value for the -web-client-root flag is empty, so no assets are built by default. This is an update of the previously reverted 0fb95ec Updates tailscale/corp#13775 Signed-off-by: Will Norris --- release/dist/cli/cli.go | 3 +++ release/dist/dist.go | 48 ++++++++++++++++++++++++++++++----- release/dist/synology/pkgs.go | 3 +++ release/dist/unixpkgs/pkgs.go | 9 +++++++ tool/yarn | 2 +- 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/release/dist/cli/cli.go b/release/dist/cli/cli.go index 0067b54a0..8445613e1 100644 --- a/release/dist/cli/cli.go +++ b/release/dist/cli/cli.go @@ -70,6 +70,7 @@ func CLI(getTargets func(unixpkgs.Signers) ([]dist.Target, error)) *ffcli.Comman fs.StringVar(&buildArgs.manifest, "manifest", "", "manifest file to write") fs.BoolVar(&buildArgs.verbose, "verbose", false, "verbose logging") fs.StringVar(&buildArgs.tgzSigningKey, "tgz-signing-key", "", "path to private signing key for release tarballs") + fs.StringVar(&buildArgs.webClientRoot, "web-client-root", "", "path to root of web client source to build") return fs })(), LongHelp: strings.TrimSpace(` @@ -100,6 +101,7 @@ var buildArgs struct { manifest string verbose bool tgzSigningKey string + webClientRoot string } func runBuild(ctx context.Context, filters []string, targets []dist.Target) error { @@ -122,6 +124,7 @@ func runBuild(ctx context.Context, filters []string, targets []dist.Target) erro } defer b.Close() b.Verbose = buildArgs.verbose + b.WebClientSource = buildArgs.webClientRoot out, err := b.Build(tgts) if err != nil { diff --git a/release/dist/dist.go b/release/dist/dist.go index 8d9edd8e2..41ffe05f7 100644 --- a/release/dist/dist.go +++ b/release/dist/dist.go @@ -38,11 +38,16 @@ type Build struct { // Verbose is whether to print all command output, rather than just failed // commands. Verbose bool + // WebClientSource is a path to the source for the web client. + // If non-empty, web client assets will be built. + WebClientSource string // Tmp is a temporary directory that gets deleted when the Builder is closed. 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 +84,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 +190,27 @@ func (b *Build) TmpDir() string { return ret } +// BuildWebClientAssets builds the JS and CSS assets used by the web client. +// If b.WebClientSource is non-empty, assets are built in a "build" sub-directory of that path. +// Otherwise, no assets are built. +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 { + if b.WebClientSource == "" { + return nil + } + dir := b.WebClientSource + 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 +334,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