|
|
@@ -0,0 +1,95 @@
|
|
|
+// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
|
+
|
|
|
+// The build-webclient tool generates the static resources needed for the
|
|
|
+// web client (code at client/web).
|
|
|
+//
|
|
|
+// # Running
|
|
|
+//
|
|
|
+// Meant to be invoked from the tailscale/web-client-prebuilt repo when
|
|
|
+// updating the production built web client assets. To run it manually,
|
|
|
+// you can use `./tool/go run ./misc/build-webclient`
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "flag"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "os/exec"
|
|
|
+ "path/filepath"
|
|
|
+
|
|
|
+ "tailscale.com/util/precompress"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ outDir = flag.String("outDir", "build/", "path to output directory")
|
|
|
+)
|
|
|
+
|
|
|
+func main() {
|
|
|
+ flag.Parse()
|
|
|
+
|
|
|
+ // The toolDir flag is relative to the current working directory,
|
|
|
+ // so we need to resolve it to an absolute path.
|
|
|
+ toolDir, err := filepath.Abs("./tool")
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Cannot resolve tool-dir: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := build(toolDir, "client/web"); err != nil {
|
|
|
+ log.Fatalf("%v", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func build(toolDir, appDir string) error {
|
|
|
+ if err := os.Chdir(appDir); err != nil {
|
|
|
+ return fmt.Errorf("Cannot change cwd: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := yarn(toolDir); err != nil {
|
|
|
+ return fmt.Errorf("install failed: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := yarn(toolDir, "lint"); err != nil {
|
|
|
+ return fmt.Errorf("lint failed: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := yarn(toolDir, "build", "--outDir="+*outDir, "--emptyOutDir"); err != nil {
|
|
|
+ return fmt.Errorf("build failed: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var compressedFiles []string
|
|
|
+ if err := precompress.PrecompressDir(*outDir, precompress.Options{
|
|
|
+ ProgressFn: func(path string) {
|
|
|
+ log.Printf("Pre-compressing %v\n", path)
|
|
|
+ compressedFiles = append(compressedFiles, path)
|
|
|
+ },
|
|
|
+ }); err != nil {
|
|
|
+ return fmt.Errorf("Cannot precompress: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Cleanup pre-compressed files.
|
|
|
+ for _, f := range compressedFiles {
|
|
|
+ if err := os.Remove(f); err != nil {
|
|
|
+ log.Printf("Failed to cleanup %q: %v", f, err)
|
|
|
+ }
|
|
|
+ // Removing intermediate ".br" version, we use ".gz" asset.
|
|
|
+ if err := os.Remove(f + ".br"); err != nil {
|
|
|
+ log.Printf("Failed to cleanup %q: %v", f+".gz", err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func yarn(toolDir string, args ...string) error {
|
|
|
+ args = append([]string{"--silent", "--non-interactive"}, args...)
|
|
|
+ return run(filepath.Join(toolDir, "yarn"), args...)
|
|
|
+}
|
|
|
+
|
|
|
+func run(name string, args ...string) error {
|
|
|
+ cmd := exec.Command(name, args...)
|
|
|
+ cmd.Stdout = os.Stdout
|
|
|
+ cmd.Stderr = os.Stderr
|
|
|
+ return cmd.Run()
|
|
|
+}
|