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 <will@tailscale.com>
pull/9017/head
Will Norris 9 months ago committed by Will Norris
parent 09e5e68297
commit 3722b05465

@ -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.StringVar(&buildArgs.manifest, "manifest", "", "manifest file to write")
fs.BoolVar(&buildArgs.verbose, "verbose", false, "verbose logging") 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.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 return fs
})(), })(),
LongHelp: strings.TrimSpace(` LongHelp: strings.TrimSpace(`
@ -100,6 +101,7 @@ var buildArgs struct {
manifest string manifest string
verbose bool verbose bool
tgzSigningKey string tgzSigningKey string
webClientRoot string
} }
func runBuild(ctx context.Context, filters []string, targets []dist.Target) error { 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() defer b.Close()
b.Verbose = buildArgs.verbose b.Verbose = buildArgs.verbose
b.WebClientSource = buildArgs.webClientRoot
out, err := b.Build(tgts) out, err := b.Build(tgts)
if err != nil { if err != nil {

@ -38,11 +38,16 @@ type Build struct {
// Verbose is whether to print all command output, rather than just failed // Verbose is whether to print all command output, rather than just failed
// commands. // commands.
Verbose bool 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 is a temporary directory that gets deleted when the Builder is closed.
Tmp string Tmp string
// Go is the path to the Go binary to use for building. // Go is the path to the Go binary to use for building.
Go string 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 is the version info of the build.
Version mkversion.VersionInfo Version mkversion.VersionInfo
// Time is the timestamp of the build. // Time is the timestamp of the build.
@ -79,15 +84,20 @@ func NewBuild(repo, out string) (*Build, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("finding module root: %w", err) return nil, fmt.Errorf("finding module root: %w", err)
} }
goTool, err := findGo(repo) goTool, err := findTool(repo, "go")
if err != nil { if err != nil {
return nil, fmt.Errorf("finding go binary: %w", err) 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{ b := &Build{
Repo: repo, Repo: repo,
Tmp: tmp, Tmp: tmp,
Out: out, Out: out,
Go: goTool, Go: goTool,
Yarn: yarnTool,
Version: mkversion.Info(), Version: mkversion.Info(),
Time: time.Now().UTC(), Time: time.Now().UTC(),
extra: map[any]any{}, extra: map[any]any{},
@ -180,6 +190,27 @@ func (b *Build) TmpDir() string {
return ret 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 // 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 // binary. Builds are cached by path and env, so each build only happens once
// per process execution. // per process execution.
@ -303,16 +334,19 @@ func findModRoot(path string) (string, error) {
} }
} }
func findGo(path string) (string, error) { // findTool returns the path to the specified named tool.
toolGo := filepath.Join(path, "tool/go") // It first looks in the "tool" directory in the provided path,
if _, err := os.Stat(toolGo); err == nil { // then in the $PATH environment variable.
return toolGo, nil 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 { if err != nil {
return "", err return "", err
} }
return toolGo, nil return tool, nil
} }
// FilterTargets returns the subset of targets that match any of the filters. // FilterTargets returns the subset of targets that match any of the filters.

@ -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) { func (m *synologyBuilds) buildInnerPackage(b *dist.Build, dsmVersion int, goenv map[string]string) (*innerPkg, error) {
key := []any{dsmVersion, goenv} key := []any{dsmVersion, goenv}
return m.innerPkgs.Do(key, func() (*innerPkg, error) { 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) ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", goenv)
if err != nil { if err != nil {
return nil, err return nil, err

@ -53,6 +53,9 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) {
} else { } else {
filename = fmt.Sprintf("tailscale_%s_%s_%s.tgz", b.Version.Short, t.os(), t.arch()) 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) ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", t.goEnv)
if err != nil { if err != nil {
return nil, err 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") 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) ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", t.goEnv)
if err != nil { if err != nil {
return nil, err 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") 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) ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", t.goEnv)
if err != nil { if err != nil {
return nil, err return nil, err

@ -21,7 +21,7 @@ fi
cachedir="$HOME/.cache/tailscale-yarn" cachedir="$HOME/.cache/tailscale-yarn"
tarball="${cachedir}.tar.gz" tarball="${cachedir}.tar.gz"
read -r want_rev < "$(dirname "$0")/yarn.rev" read -r want_rev < "./tool/yarn.rev"
got_rev="" got_rev=""
if [[ -x "${cachedir}/bin/yarn" ]]; then if [[ -x "${cachedir}/bin/yarn" ]]; then

Loading…
Cancel
Save