Example #1
0
File: main.go Project: kego/ke
func main() {
	// This turns off focus reporting mode that will print “^[[O” and “^[[I”
	// when the terminal window gets / loses focus
	// http://superuser.com/questions/931873/o-and-i-appearing-on-iterm2-when-focus-lost
	fmt.Print("\033[?1004l")

	ctx, cancel, err := process.Initialise(context.Background(), nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

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

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		<-c
		cancel()
	}()

	if err := process.GenerateAll(ctx, env.Path, map[string]bool{}); err != nil {
		fmt.Println(err.Error())
		wgctx.WaitAndExit(ctx, 1)
	}

	if cmd.Validate {
		err := process.RunValidateCommand(ctx)
		if err != nil {
			if !cmd.Log {
				// in log mode, we have already written the output of the exec'ed ke command,
				// so we don't need to duplicate the error message.
				if v, ok := kerr.Source(err).(validate.ValidationCommandError); ok {
					fmt.Println(v.Description)
					wgctx.WaitAndExit(ctx, 4) // Exit code 4: validation error
				}
				fmt.Println(err.Error())
			}
			wgctx.WaitAndExit(ctx, 1)
		}
	}

	if cmd.Edit {
		if err = server.Start(ctx, cancel); err != nil {
			fmt.Println(err.Error())
			wgctx.WaitAndExit(ctx, 1)
		}
	}

	wgctx.WaitAndExit(ctx, 0)

}
Example #2
0
File: parser.go Project: kego/ke
func scanForPackage(ctx context.Context, env *envctx.Env) (*system.Package, error) {
	localContext := envctx.NewContext(ctx, env)
	files := scanner.ScanDirToFiles(ctx, env.Dir, false)
	bytes := scanner.ScanFilesToBytes(ctx, files)
	for b := range bytes {
		if b.Err != nil {
			return nil, kerr.Wrap("GATNNQKNHY", b.Err, b.File)
		}
		o := &system.ObjectStub{}
		if err := system.Unmarshal(localContext, b.Bytes, o); err != nil {
			switch kerr.Source(err).(type) {
			case system.UnknownPackageError, system.UnknownTypeError:
				// don't return error
			default:
				return nil, kerr.Wrap("MTDCXBYBEJ", err, b.File)
			}
		}
		if o.Type == nil {
			return nil, kerr.New("MSNIGTIDIO", "%s has no type", b.File)
		}
		switch *o.Type {
		case *system.NewReference("kego.io/system", "package"):
			var i interface{}
			err := system.Unmarshal(localContext, b.Bytes, &i)
			if err != nil {
				switch kerr.Source(err).(type) {
				case system.UnknownPackageError, system.UnknownTypeError:
					// don't return error
				default:
					return nil, kerr.Wrap("XTEQCAYQJP", err)
				}
			}
			if pkg, ok := i.(*system.Package); ok {
				return pkg, nil
			}
		}
	}
	return nil, nil
}
Example #3
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 #4
0
func TestReferenceUnmarshal(t *testing.T) {

	reset := func() *Reference {
		// Let's pre-load with some values so we check that when we
		// load a null value, we clear all the fields
		r := NewReference("a.b/c", "d")
		return r
	}

	r := reset()
	err := r.Unpack(envctx.Empty, Pack(nil), false)
	assert.IsError(t, err, "MOQVSKJXRB")

	r = reset()
	err = r.Unpack(envctx.Empty, Pack(1.0), false)
	assert.IsError(t, err, "RFLQSBPMYM")

	r = reset()
	err = r.Unpack(envctx.Empty, Pack("a.b/c:d"), false)
	assert.IsError(t, err, "MSXBLEIGVJ")
	assert.HasError(t, err, "KJSOXDESFD")
	p, ok := kerr.Source(err).(UnknownPackageError)
	assert.True(t, ok)
	assert.Equal(t, "a.b/c", p.UnknownPackage)

	ctx := tests.Context("").Alias("c", "a.b/c").Ctx()

	r = reset()
	err = r.Unpack(ctx, Pack("a.b/c:d"), false)
	require.NoError(t, err)
	assert.NotNil(t, r)
	assert.Equal(t, "a.b/c", r.Package)
	assert.Equal(t, "d", r.Name)
	assert.Equal(t, "a.b/c:d", r.Value())

	r = reset()
	err = r.Unpack(ctx, Pack(map[string]interface{}{
		"type":  "system:reference",
		"value": "a.b/c:d",
	}), false)
	require.NoError(t, err)
	assert.NotNil(t, r)
	assert.Equal(t, "a.b/c", r.Package)
	assert.Equal(t, "d", r.Name)
	assert.Equal(t, "a.b/c:d", r.Value())

	r = reset()
	err = r.Unpack(ctx, Pack("a.b/c:@d"), false)
	require.NoError(t, err)
	assert.NotNil(t, r)
	assert.Equal(t, "a.b/c", r.Package)
	assert.Equal(t, "@d", r.Name)
	assert.Equal(t, "a.b/c:@d", r.Value())

	r = reset()
	err = r.Unpack(envctx.Empty, Pack("a:b"), false)
	assert.IsError(t, err, "MSXBLEIGVJ")
	assert.HasError(t, err, "DKKFLKDKYI")
	p, ok = kerr.Source(err).(UnknownPackageError)
	assert.True(t, ok)
	assert.Equal(t, "a", p.UnknownPackage)

	r = reset()
	err = r.UnmarshalInterface(envctx.Empty, 1)
	require.NoError(t, err)
	assert.Equal(t, *NewReference("", ""), *r)

	r = reset()
	err = r.UnmarshalInterface(envctx.Empty, "")
	require.NoError(t, err)
	assert.Equal(t, *NewReference("", ""), *r)

	r = reset()
	err = r.UnmarshalInterface(envctx.Empty, "a.b/c:d")
	assert.IsError(t, err, "ETLPLMMWCC")

	r = reset()
	err = r.UnmarshalInterface(tests.Context("a.b/c").Ctx(), "a.b/c:d")
	require.NoError(t, err)
	assert.Equal(t, *NewReference("a.b/c", "d"), *r)
}
Example #5
0
File: run_test.go Project: kego/ke
func TestRun(t *testing.T) {

	cb := tests.New().RealGopath()
	defer cb.Cleanup()

	path, dir := cb.TempPackage("d", map[string]string{
		"a.yaml": `
			type: system:type
			id: a
			fields:
				b:
					type: system:@string
					max-length: 5`,
		"d.go": `package d`,
	})

	cb.Path(path).Dir(dir).Jauto().Wg().Sauto(parser.Parse)

	env := envctx.FromContext(cb.Ctx())

	err := Generate(cb.Ctx(), env)
	require.NoError(t, err)

	b, err := ioutil.ReadFile(filepath.Join(dir, "generated.go"))
	require.NoError(t, err)
	assert.Contains(t, string(b), `pkg.Init(`)
	assert.Contains(t, string(b), `func() interface{} { return new(A) },`)
	assert.Contains(t, string(b), `func() interface{} { return new(ARule) },`)
	assert.Contains(t, string(b), `func() reflect.Type { return reflect.TypeOf((*AInterface)(nil)).Elem() },`)
	assert.Contains(t, string(b), fmt.Sprintf("%v", env.Hash))

	err = RunValidateCommand(cb.Ctx())
	require.NoError(t, err)

	file1, err := os.Stat(filepath.Join(dir, ".localke", "validate"))
	require.NoError(t, err)
	time1 := file1.ModTime()

	err = RunValidateCommand(cb.Ctx())
	require.NoError(t, err)

	cb.TempFile("c.yaml", `
		type: a
		id: c
		b: foo`)

	err = RunValidateCommand(cb.Ctx())
	require.NoError(t, err)

	// should not rebuild validate command
	file2, err := os.Stat(filepath.Join(dir, ".localke", "validate"))
	require.NoError(t, err)
	time2 := file2.ModTime()
	assert.Equal(t, time1, time2)

	cb.TempFile("e.yaml", `
		type: a
		id: e
		b: tooolong`)

	err = RunValidateCommand(cb.Ctx())
	assert.IsError(t, err, "KFNIOHWCBT")
	assert.HasError(t, err, "ETWHPXTUVB")

	cb.TempFile("f.yaml", `
		type: system:type
		id: f
		fields:
			a:
				type: system:@string`)

	// This loads the new system.Type into the system cache
	cb.Sauto(parser.Parse)
	// This generates a new generated.go
	err = Generate(cb.Ctx(), env)
	require.NoError(t, err)

	// This will re-run the build, but still return the validation error
	err = RunValidateCommand(cb.Ctx())
	assert.IsError(t, err, "KFNIOHWCBT")
	assert.HasError(t, err, "ETWHPXTUVB")

	cb.RemoveTempFile("e.yaml")

	cb.TempFile("h.yaml", `
		type: system:type
		id: h
		fields:
			a:
				type: system:@string`)

	// This loads the new system.Type into the system cache
	cb.Sauto(parser.Parse)
	// This generates a new generated.go
	err = Generate(cb.Ctx(), env)
	require.NoError(t, err)

	// This will re-run the build, but not return the validation error
	err = RunValidateCommand(cb.Ctx())
	require.NoError(t, err)

	// should rebuild validate command
	file3, err := os.Stat(filepath.Join(dir, ".localke", "validate"))
	require.NoError(t, err)
	time3 := file3.ModTime()
	assert.NotEqual(t, time1, time3)

	cb.TempFile("g.yaml", `
		type: system:type
		id: g
		fields:
			a:
				type: system:@string`)

	// We add a new type, but we haven't generated the struct, so it will fail with hash changed
	err = runValidateCommand(cb.Ctx(), false, false)
	assert.IsError(t, err, "DTTHRRJSSF")

	err = os.Remove(filepath.Join(dir, ".localke", "validate"))
	require.NoError(t, err)

	_, err = os.Stat(filepath.Join(dir, ".localke", "validate"))
	assert.True(t, os.IsNotExist(err))

	err = runValidateCommand(cb.Ctx(), false, false)
	assert.IsError(t, err, "DTTHRRJSSF")
	_, ok := kerr.Source(err).(*os.PathError)
	assert.True(t, ok)

}
Example #6
0
File: parser.go Project: kego/ke
func parse(ctx context.Context, path string, queue []string) (*sysctx.SysPackageInfo, error) {

	scache := sysctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	for _, q := range queue {
		if q == path {
			return nil, kerr.New("SCSCFJPPHD", "Circular import %v -> %v", queue, path)
		}
	}

	if _, found := scache.Get("kego.io/json"); !found {
		system.RegisterJsonTypes(ctx)
	}

	hash := &PackageHasher{Path: path, Aliases: map[string]string{}, Types: map[string]uint64{}, Exports: map[string]uint64{}}

	importPackage := func(importPath string, importAlias string) error {
		if _, found := scache.Get(importPath); !found {
			_, err := parse(ctx, importPath, append(queue, path))
			if err != nil {
				return kerr.Wrap("RIARRSCMVE", err)
			}
		}
		hash.Aliases[importAlias] = importPath
		return nil
	}

	packageDirectoryExists := true
	_, err := packages.GetDirFromPackage(ctx, path)
	if err != nil {
		_, ok := kerr.Source(err).(gopackages.NotFoundError)
		if ok {
			packageDirectoryExists = false
		}
	}
	if !packageDirectoryExists || cmd.Update {
		if err := GoGet(ctx, path); err != nil {
			return nil, kerr.Wrap("SBALWXUPKN", err)
		}
	}

	env, err := ScanForEnv(ctx, path)
	if err != nil {
		return nil, kerr.Wrap("GJRHNGGWFD", err)
	}

	// Always scan the system package first if we don't have it already
	if path != "kego.io/system" {
		if err := importPackage("kego.io/system", "system"); err != nil {
			return nil, kerr.Wrap("ORRCDNUPOX", err)
		}
	}

	for aliasName, aliasPath := range env.Aliases {
		if aliasPath == "kego.io/system" || aliasName == "system" {
			return nil, kerr.New("EWMLNJDXKC", "Illegal import %s", aliasName)
		}
		if err := importPackage(aliasPath, aliasName); err != nil {
			return nil, kerr.Wrap("NOVMGYKHHI", err)
		}
	}

	pcache := scache.SetEnv(env)

	cmd.Printf("Parsing %s...", path)

	if err := scanForTypesAndExports(ctx, env, pcache, hash); err != nil {
		return nil, kerr.Wrap("VFUNPHUFHD", err)
	}

	cmd.Println(" OK.")

	h, err := hash.Hash()
	if err != nil {
		return nil, kerr.Wrap("MIODRYNEJQ", err)
	}
	env.Hash = h

	return pcache, nil
}