Building and Distribution

The development setup described in Getting Started is a great way to get going with Vugu. But when you're ready to move your application to a staging or production environment (or you need to do development work on the Go server part of your web application), additional steps are in order.

Adding a Server

The server you created on the Getting Started page is only a single file (devserver.go), it does not compile as part of your application (due to the // +build ignore at the top). The main reason for this is that it makes things simpler when you are just hacking away on your user interface. If you enter bad code into a .vugu file (which ends up in a .go file and breaks your build), go run devserver.go will still run.

To make a proper server suitable for staging or production, or to start adding more server-side functionality, you'll want to create a server.go file, and place separate web server code in here. It is suggested you leave devserver.go where it is and you can use it if/when needed, it won't conflict with server.go.

Note that server.go looks really similar to devserver.go in the beginning. But as you add server-side functionality other things to server.go, the need for it becomes more apparent. We also have some additional command line flags that are useful for an application running on a server.

This example includes a -dev flag to enable automatically performing Vugu code generation and rebuilding your wasm (same as devserver.go), which you can turn on and off. Running without -dev, this can also serve as a viable production server. (Obviously you need to be aware of your specific production environment requirements, but this will get you pointed in the right direction.)

Command line flags for the HTTP listener and the directory to look for files in are also included. server.go:

// +build !wasm

package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/vugu/vugu/simplehttp"
)

func main() {
	dev := flag.Bool("dev", false, "Enable development features")
	dir := flag.String("dir", ".", "Project directory")
	httpl := flag.String("http", "127.0.0.1:8877", "Listen for HTTP on this host:port")
	flag.Parse()
	wd, _ := filepath.Abs(*dir)
	os.Chdir(wd)
	log.Printf("Starting HTTP Server at %q", *httpl)
	h := simplehttp.New(wd, *dev)
	log.Fatal(http.ListenAndServe(*httpl, h))
}

To run this server, you can either build the binary and run it directly, or go run . -dev also works.

Making dist.go

Rather than introducing third party build tools, the suggested approach to distributing your application is to create a small file called dist.go (ignored by the rest of your application) that you run when you want to build your distribution. The distutil package has some convient functions that make this less tedious than it might seem. This approach also has the advantage that it works equally well on Windows, Linux and Mac, and without having to install anything. The Go module system takes care of that for you.

Various steps are possible and there is not a one-size-fits-all dist.go. That said, here's one that will get you started:

  • It creates a "dist" folder, copies the static files from your project into it
  • It finds and copies wasm_exec.js
  • It makes sure vugugen is installed
  • It runs go generate
  • It builds your main.wasm file
  • And if desired you can make it write out an index.html or build your server executable
// +build ignore

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/vugu/vugu/distutil"
)

func main() {

	clean := flag.Bool("clean", false, "Remove dist dir before starting")
	dist := flag.String("dist", "dist", "Directory to put distribution files in")
	flag.Parse()

	start := time.Now()

	if *clean {
		os.RemoveAll(*dist)
	}

	os.MkdirAll(*dist, 0755) // create dist dir if not there

	// copy static files
	distutil.MustCopyDirFiltered(".", *dist, nil)

	// find and copy wasm_exec.js
	distutil.MustCopyFile(distutil.MustWasmExecJsPath(), filepath.Join(*dist, "wasm_exec.js"))

	// check for vugugen and go get if not there
	if _, err := exec.LookPath("vugugen"); err != nil {
		fmt.Print(distutil.MustExec("go", "get", "github.com/vugu/vugu/cmd/vugugen"))
	}

	// run go generate
	fmt.Print(distutil.MustExec("go", "generate", "."))

	// run go build for wasm binary
	fmt.Print(distutil.MustEnvExec([]string{"GOOS=js", "GOARCH=wasm"}, "go", "build", "-o", filepath.Join(*dist, "main.wasm"), "."))

	// STATIC INDEX FILE:
	// if you are hosting with a static file server or CDN, you can write out the default index.html from simplehttp
	// req, _ := http.NewRequest("GET", "/index.html", nil)
	// outf, err := os.OpenFile(filepath.Join(*dist, "index.html"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	// distutil.Must(err)
	// defer outf.Close()
	// template.Must(template.New("_page_").Parse(simplehttp.DefaultPageTemplateSource)).Execute(outf, map[string]interface{}{"Request": req})

	// BUILD GO SERVER:
	// or if you are deploying a Go server (yay!) you can build that binary here
	// fmt.Print(distutil.MustExec("go", "build", "-o", filepath.Join(*dist, "server"), "."))

	log.Printf("dist.go complete in %v", time.Since(start))
}

By dropping the above is dist.go and tuning it, you can run go run dist.go and get a dist folder with the distribution of your project, ready for deployment. And you can of course easily call this from another build/distribution system, such as a Dockerfile.