Example #1
0
File: generate.go Project: kego/ke
// Generate generates the source code for type structs, and writes the
// generated.go to the filesystem.
func Generate(ctx context.Context, env *envctx.Env) error {

	wgctx.Add(ctx, "Generate")
	defer wgctx.Done(ctx, "Generate")

	cmd := cmdctx.FromContext(ctx)

	cmd.Printf("Generating types for %s... ", env.Path)

	outputDir := env.Dir
	filename := "generated.go"
	source, err := generate.Structs(ctx, env)
	if err != nil {
		return kerr.Wrap("XFNESBLBTQ", err)
	}

	// We only backup in the system structs and types files because they are
	// the only generated files we ever need to roll back
	backup := env.Path == "kego.io/system"

	if err = save(outputDir, source, filename, backup); err != nil {
		return kerr.Wrap("UONJTTSTWW", err)
	} else {
		cmd.Println("OK.")
	}

	return nil
}
Example #2
0
File: server.go Project: kego/ke
func serve(ctx context.Context) error {

	wgctx.Add(ctx, "serve")
	defer wgctx.Done(ctx, "serve")

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	// Starting with port zero chooses a random open port
	listner, err := net.Listen("tcp", fmt.Sprintf(":%d", cmd.Port))
	if err != nil {
		return kerr.Wrap("QGLXHWPWQW", err)
	}
	defer listner.Close()

	// Here we get the address we're serving on
	address, ok := listner.Addr().(*net.TCPAddr)
	if !ok {
		return kerr.New("CBLPYVGGUR", "Can't find address (l.Addr() is not *net.TCPAddr)")
	}

	url := fmt.Sprintf("http://localhost:%d/%s/", address.Port, env.Path)

	cmd.Printf("Server now running on %s\n", url)

	// We open the default browser and navigate to the address we're serving
	// from. This will error if the system doesn't have a browser, so we can
	// ignore the error.
	browser.OpenURL(url)

	withCancel(ctx, func() {
		err = http.Serve(listner, nil)
	})
	if err != nil {
		return kerr.Wrap("TUCBTWMRNN", err)
	}

	if cmd.Debug {
		return kerr.New("ATUTBOICGJ", "Connection closed")
	}
	return nil

}
Example #3
0
File: run.go Project: kego/ke
// buildValidateCommand creates a temporary folder in the package, in which the go source for the
// local command is generated. This command is then compiled.
func buildValidateCommand(ctx context.Context) error {

	wgctx.Add(ctx, "buildValidateCommand")
	defer wgctx.Done(ctx, "buildValidateCommand")

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	validateCommandPath := filepath.Join(env.Dir, validateCommand)

	source, err := generate.ValidateCommand(ctx)
	if err != nil {
		return kerr.Wrap("SPRFABSRWK", err)
	}

	outputDir, err := ioutil.TempDir(env.Dir, "temporary")
	if err != nil {
		return kerr.Wrap("HWOPVXYMCT", err)
	}
	defer os.RemoveAll(outputDir)
	outputName := "generated_cmd.go"
	outputPath := filepath.Join(outputDir, outputName)

	if err = save(outputDir, source, outputName, false); err != nil {
		return kerr.Wrap("FRLCYFOWCJ", err)
	}

	cmd.Print("Building validate command... ")

	combined, stdout, stderr := logger.Logger(cmd.Log)
	exe := exec.Command("go", "build", "-o", validateCommandPath, outputPath)
	exe.Stdout = stdout
	exe.Stderr = stderr

	if err := exe.Run(); err != nil {
		return kerr.Wrap("OEPAEEYKIS", err)
	}
	cmd.Println("OK.")
	cmd.Print(combined.String())

	return nil
}
Example #4
0
File: server.go Project: kego/ke
func script(ctx context.Context, w http.ResponseWriter, req *http.Request, sourceMap bool) error {

	wgctx.Add(ctx, "script")
	defer wgctx.Done(ctx, "script")

	path := ""
	if strings.HasSuffix(req.URL.Path, ".map") {
		path = req.URL.Path[1 : len(req.URL.Path)-14]
	} else {
		path = req.URL.Path[1 : len(req.URL.Path)-10]
	}

	env, err := parser.ScanForEnv(ctx, path)

	// This is the client code for the editor which we will compile to
	// Javascript using GopherJs below.
	source, err := generate.Editor(ctx, env)
	if err != nil {
		return kerr.Wrap("UWPDBQXURR", err)
	}

	var out []byte
	withCancel(ctx, func() {
		out, err = Compile(ctx, source, sourceMap)
	})
	if err != nil {
		return kerr.Wrap("LSUXHJMSSX", err)
	}

	withCancel(ctx, func() {
		err = writeWithTimeout(w, out)
	})
	if err != nil {
		return kerr.Wrap("GAOWWBAIAL", err)
	}

	return nil
}
Example #5
0
File: server.go Project: kego/ke
func Start(ctx context.Context, cancel context.CancelFunc) error {

	wgctx.Add(ctx, "Start")
	defer wgctx.Done(ctx, "Start")

	auth := auther.New()

	cmd := cmdctx.FromContext(ctx)

	fail := make(chan error)

	cmd.Println("Starting editor server... ")

	// This contains the source map that will be persisted between requests
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		if strings.HasSuffix(req.URL.Path, "/favicon.ico") {
			w.WriteHeader(404)
			return
		}
		if strings.HasSuffix(req.URL.Path, "/script.js") {
			if err := script(ctx, w, req, false); err != nil {
				fail <- kerr.Wrap("XPVTVKDWHJ", err)
				return
			}
			return
		}
		if strings.HasSuffix(req.URL.Path, "/script.js.map") {
			if err := script(ctx, w, req, true); err != nil {
				fail <- kerr.Wrap("JAIBRHULSI", err)
				return
			}
			return
		}
		if err := root(ctx, w, req, auth); err != nil {
			fail <- kerr.Wrap("QOMJGNOCQF", err)
			return
		}
	})

	if err := rpc.Register(&Server{ctx: ctx, auth: auth}); err != nil {
		return kerr.Wrap("RESBVVGRMH", err)
	}

	http.Handle("/_rpc", websocket.Handler(func(ws *websocket.Conn) {
		ws.PayloadType = websocket.BinaryFrame
		rpc.ServeConn(ws)
	}))

	go func() {
		if err := serve(ctx); err != nil {
			fail <- err
		}
	}()

	done := ctx.Done()
	for {
		select {
		case <-done:
			fmt.Println("Exiting editor server (interupted)... ")
			return nil
		case err, open := <-fail:
			if !open {
				// Channel has been closed, so app should gracefully exit.
				cancel()
				cmd.Println("Exiting editor server (finished)... ")
			} else {
				// Error received, so app should display error.
				// return kerr.New("WKHPTVJBIL", err, "Fail channel receive")
				fmt.Println(err)
			}
			if !cmd.Debug {
				return nil
			}
		}
	}
}
Example #6
0
File: server.go Project: kego/ke
func root(ctx context.Context, w http.ResponseWriter, req *http.Request, auth auther.Auther) error {

	wgctx.Add(ctx, "root")
	defer wgctx.Done(ctx, "root")

	if b, err := static.Asset(req.URL.Path[1:]); err == nil {
		if strings.HasSuffix(req.URL.Path, ".css") {
			w.Header().Set("Content-Type", "text/css")
		}
		if err := writeWithTimeout(w, b); err != nil {
			return kerr.Wrap("PVPCXBIUJT", err)
		}
		return nil
	}

	path := req.URL.Path[1:]
	if strings.HasSuffix(path, "/") {
		path = path[0 : len(path)-1]
	}

	// use a new context with a blank sysctx for the duration of this function
	// to prevent caching
	ctx = sysctx.NewContext(ctx)

	scache := sysctx.FromContext(ctx)

	env, err := parser.ScanForEnv(ctx, path)
	if err != nil {
		if _, ok := kerr.Source(err).(gopackages.NotFoundError); ok {
			w.WriteHeader(404)
			return nil
		}
		return kerr.Wrap("ALINBMKDRP", err)
	}

	pcache, err := parser.Parse(ctx, path)
	if err != nil {
		return kerr.Wrap("HIHWJRPUKE", err)
	}

	if err := process.GenerateAll(ctx, env.Path, map[string]bool{}); err != nil {
		return kerr.Wrap("LVGHABDYNQ", err)
	}

	data := map[string]string{}
	for _, name := range pcache.Globals.Keys() {
		if g, ok := pcache.Globals.Get(name); ok {
			data[name] = g.File
		}
	}

	pkgBytes := pcache.PackageBytes
	pkgFilename := pcache.PackageFilename
	if pkgBytes == nil {
		b, err := system.Marshal(ctx, system.EmptyPackage())
		if err != nil {
			return kerr.Wrap("OUBOTYGPKU", err)
		}
		pkgBytes = b
		pkgFilename = "package.ke.json"
	}

	imports := map[string]shared.ImportInfo{}

	var scan func(string) error
	scan = func(path string) error {
		if _, ok := imports[path]; ok {
			return nil
		}
		syspi, ok := scache.Get(path)
		if !ok {
			return kerr.New("VIGKIUPNCF", "%s not found in sys ctx", path)
		}
		info := shared.ImportInfo{
			Path:    path,
			Aliases: syspi.Aliases,
			Types:   map[string]shared.TypeInfo{},
		}
		for _, name := range syspi.Files.Keys() {
			if b, ok := syspi.Files.Get(name); ok {
				info.Types[name] = shared.TypeInfo{
					File:  b.File,
					Bytes: b.Bytes,
				}
			}
		}
		imports[path] = info

		for _, child := range syspi.Aliases {
			if err := scan(child); err != nil {
				return kerr.Wrap("NCULMUUUOT", err)
			}
		}
		return nil
	}
	// First we always import system
	if err := scan("kego.io/system"); err != nil {
		return kerr.Wrap("KRXSLOJKWV", err)
	}
	if err := scan(env.Path); err != nil {
		return kerr.Wrap("EELKQDCJGN", err)
	}

	info := shared.Info{
		Path:            env.Path,
		Aliases:         env.Aliases,
		Data:            data,
		Package:         pkgBytes,
		PackageFilename: pkgFilename,
		Imports:         imports,
		Hash:            auth.Sign([]byte(env.Path)),
	}
	buf := bytes.NewBuffer([]byte{})
	err = gob.NewEncoder(buf).Encode(info)
	if err != nil {
		return kerr.Wrap("OHBYTULHUQ", err)
	}
	base64EncodedString := base64.StdEncoding.EncodeToString(buf.Bytes())
	attrib := base64EncodedString

	source := []byte(`
		<html>
			<head>
				<meta charset="utf-8">
				<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
				<link rel="stylesheet" href="/bootstrap/css/bootstrap-theme.min.css">
				<link rel="stylesheet" href="/split.css">
				<link rel="stylesheet" href="/editors.css">
				<link rel="stylesheet" href="/tree.css">
				<script src="/jquery-2.2.4.min.js"></script>
				<script src="/jquery-ui/jquery-ui.min.js"></script>
				<script src="/split.min.js"></script>
				<script src="/bootstrap/js/bootstrap.min.js"></script>
				<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
			</head>
			<body id="body" info="` + attrib + `"></body>
			<script src="/` + env.Path + `/script.js"></script>
		</html>`)

	if err := writeWithTimeout(w, source); err != nil {
		return kerr.Wrap("ICJSAIMDRF", err)
	}

	return nil
}
Example #7
0
File: run.go Project: kego/ke
func runValidateCommand(ctx context.Context, build bool, repeat bool) (err error) {

	wgctx.Add(ctx, "runValidateCommand")
	defer wgctx.Done(ctx, "runValidateCommand")

	if build {
		if err := buildValidateCommand(ctx); err != nil {
			return kerr.Wrap("FIJHGMEEUM", err)
		}
	}

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	validateCommandPath := filepath.Join(env.Dir, validateCommand)

	cmd.Print("Running validate command... ")

	combined, stdout, stderr := logger.Logger(cmd.Log)

	hashChanged := false

	exe := exec.Command(validateCommandPath)
	exe.Stdout = stdout
	exe.Stderr = stderr
	if err = exe.Run(); err != nil {

		exiterr, ok := err.(*exec.ExitError)
		if !ok {
			goto Repeat
		}

		// The program has exited with an exit code != 0. This works on both Unix and Windows.
		// Although package syscall is generally platform dependent, WaitStatus is defined for
		// both Unix and Windows and in both cases has an ExitStatus() method with the same
		// signature.
		status, ok := exiterr.Sys().(syscall.WaitStatus)
		if !ok {
			// ke: {"block": {"notest": true}}
			goto Repeat
		}

		switch status.ExitStatus() {
		case 3:
			// Exit status 3 = hash changed
			hashChanged = true
			goto Repeat
		case 4:
			// Exit status 4 = validation error
			return validate.ValidationCommandError{Struct: kerr.New("ETWHPXTUVB", strings.TrimSpace(combined.String()))}
		default:
			// ke: {"block": {"notest": true}}
			goto Repeat
		}
	}
	cmd.Println("OK.")
	return nil

Repeat:
	if repeat {
		if hashChanged {
			cmd.Println("Types have changed since last run. Rebuilding...")
		} else {
			// ke: {"block": {"notest": true}}
			cmd.Println("Command returned an error. Rebuilding...")
		}
		if err := runValidateCommand(ctx, true, false); err != nil {
			return kerr.Wrap("HOHQEISLMI", err)
		}
		return nil
	}
	return kerr.Wrap("DTTHRRJSSF", err)
}