コード例 #1
0
ファイル: validate.go プロジェクト: kego/ke
func validateMain(ctx context.Context, cancel context.CancelFunc, log func(string), interrupt chan os.Signal) int {

	go func() {
		<-interrupt
		cancel()
	}()

	env := envctx.FromContext(ctx)

	changes, err := comparePackageHash(ctx, "kego.io/system")
	if !changes && err == nil {
		changes, err = comparePackageHash(ctx, env.Path)
	}
	if err != nil {
		log(err.Error())
		return 1 // Exit status 1: generic error
	}
	if changes {
		return 3 // Exit status 3: hash changed error
	}

	errors, err := validate.ValidatePackage(ctx)
	if err != nil {
		log(err.Error())
		return 1 // Exit status 1: generic error
	}
	if len(errors) > 0 {
		for _, e := range errors {
			log(fmt.Sprintf("%s: %s", e.Source.Path(), e.Description))
		}
		return 4 // Exit status 4: validation error
	}
	return 0 // Exit status 0: success
}
コード例 #2
0
ファイル: main.go プロジェクト: 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)

}
コード例 #3
0
ファイル: reference.go プロジェクト: kego/ke
func GetReferencePartsFromTypeString(ctx context.Context, typeString string) (path string, name string, err error) {

	env := envctx.FromContext(ctx)

	if strings.Contains(typeString, "/") {
		// If the type name contains a slash, I'm assuming it's a fully
		// qualified type name of the form "kego.io/system:type".
		// TODO: Improve this with a regex?
		parts := strings.Split(typeString, ":")

		// We hard-code system and json to prevent them having to always be
		// specified in the aliases
		if parts[0] == "kego.io/system" {
			return "kego.io/system", parts[1], nil
		} else if parts[0] == "kego.io/json" {
			return "kego.io/json", parts[1], nil
		}

		_, found := findKey(env.Aliases, parts[0])
		if !found && parts[0] != env.Path {
			return "", "", UnknownPackageError{
				Struct:         kerr.New("KJSOXDESFD", "Unknown package %s", parts[0]),
				UnknownPackage: parts[0],
			}
		}
		return parts[0], parts[1], nil
	} else if strings.Contains(typeString, ":") {
		// If the type name contains a colon, I'm assuming it's an abreviated
		// qualified type name of the form "system:type". We should look the
		// package name up in the aliases map.
		// TODO: Improve this with a regex?
		parts := strings.Split(typeString, ":")

		// We hard-code system and json to prevent them having to always be
		// specified in the aliases
		if parts[0] == "system" {
			return "kego.io/system", parts[1], nil
		} else if parts[0] == "json" {
			return "kego.io/json", parts[1], nil
		}

		packagePath, ok := env.Aliases[parts[0]]
		if !ok {
			return "", "", UnknownPackageError{
				Struct:         kerr.New("DKKFLKDKYI", "Unknown package %s", parts[0]),
				UnknownPackage: parts[0],
			}
		}
		return packagePath, parts[1], nil
	} else {
		return env.Path, typeString, nil
	}
}
コード例 #4
0
ファイル: initialise_test.go プロジェクト: kego/ke
func TestInitialise(t *testing.T) {

	cb := tests.New().TempGopath(true)
	defer cb.Cleanup()

	pathA, dirA := cb.TempPackage("a", map[string]string{
		"a.json": `{"type": "system:type", "id": "a"}`,
		"a.go":   "package a",
	})
	pathB, dirB := cb.TempPackage("b", map[string]string{
		"b.json": `{"type": "system:type", "id": "b"}`,
		"b.go":   "package b",
	})

	cb.OsWd(dirA)

	ctx, _, err := Initialise(cb.Ctx(), nil)
	require.NoError(t, err)
	env := envctx.FromContext(ctx)
	assert.Equal(t, dirA, env.Dir)
	assert.Equal(t, pathA, env.Path)

	cb.OsWd("/")

	ctx, _, err = Initialise(ctx, &Options{
		Path: pathB,
	})
	env = envctx.FromContext(ctx)
	require.NoError(t, err)
	assert.Equal(t, dirB, env.Dir)
	assert.Equal(t, pathB, env.Path)

	_, _, err = Initialise(ctx, &Options{
		Path: "",
	})
	assert.IsError(t, err, "ADNJKTLAWY")
	assert.HasErrorExternal(t, err, "CXOETFPTGM")
}
コード例 #5
0
ファイル: unpack_ext_test.go プロジェクト: kego/ke
func testUnpack(t *testing.T, path string) {
	ctx, _, err := process.Initialise(context.Background(), &process.Options{
		Path: path,
	})
	require.NoError(t, err)

	env := envctx.FromContext(ctx)

	files := scanner.ScanDirToFiles(ctx, env.Dir, env.Recursive)
	bytes := scanner.ScanFilesToBytes(ctx, files)
	for b := range bytes {
		_, err := node.Unmarshal(ctx, b.Bytes)
		require.NoError(t, err, b.File)
	}
}
コード例 #6
0
ファイル: run.go プロジェクト: kego/ke
func RunValidateCommand(ctx context.Context) (err error) {
	env := envctx.FromContext(ctx)

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

	build := false
	repeat := true
	if _, err := os.Stat(validateCommandPath); os.IsNotExist(err) {
		build = true
		repeat = false
	}

	if err = runValidateCommand(ctx, build, repeat); err != nil {
		return kerr.Wrap("KFNIOHWCBT", err)
	}
	return nil
}
コード例 #7
0
ファイル: server.go プロジェクト: 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

}
コード例 #8
0
ファイル: run.go プロジェクト: 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
}
コード例 #9
0
ファイル: validate.go プロジェクト: kego/ke
func ValidatePackage(ctx context.Context) (errors []ValidationError, err error) {

	env := envctx.FromContext(ctx)

	files := scanner.ScanDirToFiles(ctx, env.Dir, env.Recursive)
	bytes := scanner.ScanFilesToBytes(ctx, files)
	for c := range bytes {
		if c.Err != nil {
			return nil, kerr.Wrap("IHSVWAUAYW", c.Err)
		}
		ve, err := validateBytes(ctx, c.Bytes)
		if err != nil {
			return nil, kerr.Wrap("KWLWXKWHLF", err)
		}
		if len(ve) > 0 {
			errors = append(errors, ve...)
		}
	}

	return
}
コード例 #10
0
ファイル: validate.go プロジェクト: kego/ke
func ValidateCommand(ctx context.Context) (source []byte, err error) {

	env := envctx.FromContext(ctx)

	g := builder.WithName(env.Path, "main")
	g.Imports.Add("kego.io/process/validate/command")
	g.Imports.Anonymous("kego.io/system")
	g.Imports.Anonymous(env.Path)
	for _, p := range env.Aliases {
		g.Imports.Anonymous(p)
	}
	g.Print(`
		func main() {
			command.ValidateMain(` + strconv.Quote(env.Path) + `)
		}`)
	b, err := g.Build()
	if err != nil {
		return nil, kerr.Wrap("IIIRBBXASR", err)
	}
	return b, nil
}
コード例 #11
0
ファイル: tests_test.go プロジェクト: kego/ke
func runKe(cb *tests.ContextBuilder, name string, files map[string]string) (string, error) {

	tests := false
	for name, _ := range files {
		if strings.HasSuffix(name, "_test.go") {
			// if we add a xxx_test.go file we should also run "go test"
			tests = true
		}
	}

	path, _ := cb.RealGopath().TempPackage(name, files)

	ctx, _, err := process.Initialise(context.Background(), &process.Options{
		Path: path,
	})
	if err != nil {
		return "", err
	}

	env := envctx.FromContext(ctx)

	if err := process.Generate(ctx, env); err != nil {
		return "", err
	}

	if err := process.RunValidateCommand(ctx); err != nil {
		return "", err
	}

	if tests {
		if out, err := exec.Command("go", "test", path).CombinedOutput(); err != nil {
			return "", fmt.Errorf("%s", string(out))
		}
	}

	return env.Path, nil
}
コード例 #12
0
ファイル: tests.go プロジェクト: kego/ke
func (c *ContextBuilder) Env() *envctx.Env {
	return envctx.FromContext(c.ctx)
}
コード例 #13
0
ファイル: run_test.go プロジェクト: 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)

}
コード例 #14
0
ファイル: run.go プロジェクト: 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)
}