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.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 {

@ -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.

@ -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

@ -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

@ -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

Loading…
Cancel
Save