From 1e6e2fe219ab4362b7969066a12bb133cb0a069b Mon Sep 17 00:00:00 2001 From: Tony Worm Date: Wed, 7 Sep 2022 00:51:31 -0400 Subject: [PATCH] hof/create: tests, fixes, and upgrades --- .hof/shadow/cli/cmd/hof/cmd/create.go | 4 +- .hof/shadow/cli/cmd/hof/cmd/root.go | 2 +- cmd/hof/cmd/create.go | 4 +- cmd/hof/cmd/root.go | 2 +- design/cmds/create.cue | 4 +- formatters/test/fmt_test.go | 2 - lib/create/create.go | 209 ++++++++++++++++++-------- lib/create/handlers.go | 132 +++++++++++++++- lib/datautils/io/io.go | 45 +----- lib/gen/asmodule.go | 2 +- lib/gen/gen.go | 9 +- lib/gen/runtime.go | 3 + lib/mod/modder/modder_vendor.go | 4 +- lib/mod/modder/modder_write.go | 9 +- schema/create/create.cue | 13 +- test/create/cue.mods | 9 ++ test/create/local/cue.mod/module.cue | 1 + test/create/local/cue.mods | 9 ++ test/create/local/cue.sums | 8 + 19 files changed, 343 insertions(+), 128 deletions(-) create mode 100644 test/create/cue.mods create mode 100644 test/create/local/cue.mod/module.cue create mode 100644 test/create/local/cue.mods create mode 100644 test/create/local/cue.sums diff --git a/.hof/shadow/cli/cmd/hof/cmd/create.go b/.hof/shadow/cli/cmd/hof/cmd/create.go index 6cc80c207..5bc84d989 100644 --- a/.hof/shadow/cli/cmd/hof/cmd/create.go +++ b/.hof/shadow/cli/cmd/hof/cmd/create.go @@ -14,6 +14,8 @@ import ( var createLong = `hof create enables you to easily bootstrap code for full projects, components, and more. +Docs: https://docs.hofstadter.io/hof-create/ + Any generator can support the create command and most will bootstrap a generator. This means you get all the same benefits from @@ -54,7 +56,7 @@ var CreateCmd = &cobra.Command{ Use: "create ", - Short: "easily bootstrap full project, components, and more", + Short: "bootstrap projects, components, and files from any git repo", Long: createLong, diff --git a/.hof/shadow/cli/cmd/hof/cmd/root.go b/.hof/shadow/cli/cmd/hof/cmd/root.go index f040d32c4..bb4b149db 100644 --- a/.hof/shadow/cli/cmd/hof/cmd/root.go +++ b/.hof/shadow/cli/cmd/hof/cmd/root.go @@ -186,7 +186,7 @@ Usage: hof [flags] [command] [args] Main commands: - create easily bootstrap full project, components, and more + create bootstrap projects, components, and files from any git repo datamodel manage, diff, and migrate your data models gen modular and composable code gen: CUE & data + templates = _ flow run CUE pipelines with the hof/flow DAG engine diff --git a/cmd/hof/cmd/create.go b/cmd/hof/cmd/create.go index 6ad1940da..4c462648b 100644 --- a/cmd/hof/cmd/create.go +++ b/cmd/hof/cmd/create.go @@ -15,6 +15,8 @@ import ( var createLong = `hof create enables you to easily bootstrap code for full projects, components, and more. +Docs: https://docs.hofstadter.io/hof-create/ + Any generator can support the create command and most will bootstrap a generator. This means you get all the same benefits from @@ -57,7 +59,7 @@ var CreateCmd = &cobra.Command{ Use: "create ", - Short: "easily bootstrap full project, components, and more", + Short: "bootstrap projects, components, and files from any git repo", Long: createLong, diff --git a/cmd/hof/cmd/root.go b/cmd/hof/cmd/root.go index 12d83626e..871528f41 100644 --- a/cmd/hof/cmd/root.go +++ b/cmd/hof/cmd/root.go @@ -177,7 +177,7 @@ Usage: hof [flags] [command] [args] Main commands: - create easily bootstrap full project, components, and more + create bootstrap projects, components, and files from any git repo datamodel manage, diff, and migrate your data models gen modular and composable code gen: CUE & data + templates = _ flow run CUE pipelines with the hof/flow DAG engine diff --git a/design/cmds/create.cue b/design/cmds/create.cue index 36fa10d06..809e89ada 100644 --- a/design/cmds/create.cue +++ b/design/cmds/create.cue @@ -7,7 +7,7 @@ import ( #CreateCommand: schema.#Command & { Name: "create" Usage: "create " - Short: "easily bootstrap full project, components, and more" + Short: "bootstrap projects, components, and files from any git repo" Long: #CreateRootHelp Args: [{ @@ -47,6 +47,8 @@ import ( hof create enables you to easily bootstrap code for full projects, components, and more. +Docs: https://docs.hofstadter.io/hof-create/ + Any generator can support the create command and most will bootstrap a generator. This means you get all the same benefits from diff --git a/formatters/test/fmt_test.go b/formatters/test/fmt_test.go index b26d59d98..03d3899c2 100644 --- a/formatters/test/fmt_test.go +++ b/formatters/test/fmt_test.go @@ -21,5 +21,3 @@ func TestFormatters(t *testing.T) { Setup: envSetup, }) } - - diff --git a/lib/create/create.go b/lib/create/create.go index 5e27bc272..03ede768c 100644 --- a/lib/create/create.go +++ b/lib/create/create.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "strings" "cuelang.org/go/cue" @@ -12,6 +13,7 @@ import ( "github.com/hofstadter-io/hof/cmd/hof/flags" "github.com/hofstadter-io/hof/lib/cuetils" + "github.com/hofstadter-io/hof/lib/datautils/io" "github.com/hofstadter-io/hof/lib/gen" "github.com/hofstadter-io/hof/lib/repos/cache" "github.com/hofstadter-io/hof/lib/yagu" @@ -80,6 +82,7 @@ func Create(module string, rootflags flags.RootPflagpole, cmdflags flags.CreateF os.RemoveAll(tmpdir) }() + fmt.Println("init'n generator") genflags := flags.GenFlags genflags.Generator = cmdflags.Generator genflags.Outdir = cmdflags.Outdir @@ -96,12 +99,14 @@ func Create(module string, rootflags flags.RootPflagpole, cmdflags flags.CreateF // fmt.Println(" outdir: ", outdir) genflags.Outdir = outdir - // create our runtime now + // create our runtime now, maybe we want a new func for this + // since we want to ignore any current CUE module context + // everything is put into a temp dir and rendered to CWD R, err := gen.NewRuntime(nil, rootflags, genflags) if err != nil { return err } - + R.OriginalWkdir = cwd if R.Verbosity > 0 { fmt.Println("CueDirs:", R.CueModuleRoot, R.WorkingDir) @@ -116,7 +121,11 @@ func Create(module string, rootflags flags.RootPflagpole, cmdflags flags.CreateF func setupTmpdir(url, ver string) (tmpdir, subdir string, err error) { var FS billy.Filesystem - tmpdir, err = os.MkdirTemp("", "hof-") + tmpdir, err = os.MkdirTemp("", "hof-create-") + if err != nil { + return tmpdir, "", err + } + err = os.MkdirAll(tmpdir, 0755) if err != nil { return tmpdir, "", err } @@ -171,7 +180,10 @@ func setupTmpdir(url, ver string) (tmpdir, subdir string, err error) { // fmt.Println("subdir:", subdir) // load into FS + // fmt.Println("starting to read:", modroot) FS = osfs.New(modroot) + // fmt.Println("done reading") + } // fmt.Println("writing", tmpdir) @@ -184,8 +196,9 @@ func setupTmpdir(url, ver string) (tmpdir, subdir string, err error) { // run 'hof mod vendor cue' in tmpdir fmt.Println("fetching creator dependencies") out, err := yagu.Bash("hof mod vendor cue", tmpdir) - fmt.Println(out) + // fmt.Println("done fetching dependencies\n", out) if err != nil { + fmt.Println(out) return tmpdir, subdir, fmt.Errorf("while fetching creator deps %w", err) } @@ -208,18 +221,33 @@ func runCreator(R *gen.Runtime, inputs []string) (err error) { return err } + // extract generators err = R.ExtractGenerators() if err != nil { return err } - + if len(R.Generators) == 0 { + return fmt.Errorf("no generators found, please make sure there is a creator at the root of the repository") + } if len(R.Generators) > 1 { fmt.Println("Warning, you are running more than one generator. Use --list and -G if this was not your intention.") } + var inputMap map[string]any + if len(inputs) > 0 { + // load inputs + inputMap, err = loadCreateInputs(R, inputs) + if err != nil { + return err + } + if R.Verbosity > 0 { + fmt.Println("Create flag-input:", inputMap) + } + } + // handle create input / prompt for _, G := range R.Generators { - err = handleGeneratorCreate(G) + err = handleGeneratorCreate(G, inputMap) if err != nil { return err } @@ -253,8 +281,9 @@ func runCreator(R *gen.Runtime, inputs []string) (err error) { return fmt.Errorf("While writing") } + // we wait until the very end of all generators to print after messages for _, G := range R.Generators { - after := G.CueValue.LookupPath(cue.ParsePath("CreateMessage.After")) + after := G.CueValue.LookupPath(cue.ParsePath("Create.Message.After")) if after.Err() != nil { fmt.Println("error:", after.Err()) return after.Err() @@ -276,13 +305,63 @@ func runCreator(R *gen.Runtime, inputs []string) (err error) { return nil } -func handleGeneratorCreate(G *gen.Generator) error { - before := G.CueValue.LookupPath(cue.ParsePath("CreateMessage.Before")) +func loadCreateInputs(R *gen.Runtime, inputFlags []string) (input map[string]any, err error) { + if len(inputFlags) == 0 { + return nil, nil + } + + input = make(map[string]any) + + for _, inFlag := range inputFlags { + // starts with @, load file + // only one time supported right now + if strings.HasPrefix(inFlag, "@") { + // this still might not be good enough + // we may need to remember the original working directory on the runtime + fn := filepath.Join(R.OriginalWkdir, inFlag[1:]) + // fmt.Println("file flat:", inFlag, fn) + var data interface{} + data = make(map[string]any) + _, err := io.ReadFile(fn, &data) + if err != nil { + return input, err + } + // fmt.Println("(todo) input: ", fn, data) + + for k,v := range data.(map[string]any) { + input[k] = v + } + + continue + } + + // otherwise split by =, path=value + parts := strings.Split(inFlag, "=") + if len(parts) != 2 { + return input, fmt.Errorf("input flag must have 'path=value' format") + } + // todo, how to deal with types besides strings (list, int, bool) + path, value := parts[0], parts[1] + input[path] = value + // we'd really prefer to support this, but getting errors from CUE about different runtimes + // input = input.FillPath(cue.ParsePath(path), value) + } + + // fmt.Printf("pre-input: %#v\n", input) + + return input, nil +} + + +func handleGeneratorCreate(G *gen.Generator, inputMap map[string]any) (err error) { + genVal := G.CueValue + + // pritn the befor message if set, otherwise default + before := genVal.LookupPath(cue.ParsePath("Create.Message.Before")) if before.Err() != nil { fmt.Println("error:", before.Err()) return before.Err() } - if !before.IsConcrete() || !before.Exists() { fmt.Printf("Creating from %q\n", G.Name) } else { @@ -293,65 +372,75 @@ func handleGeneratorCreate(G *gen.Generator) error { fmt.Println(s) } - val := G.CueValue.LookupPath(cue.ParsePath("CreateInput")) - if val.Err() != nil { - fmt.Println("error:", val.Err()) - return val.Err() - } + if inputMap != nil { + // if the user provides a schema for input + inputVal := genVal.LookupPath(cue.ParsePath("Create.Input")) + if inputVal.Exists() && inputVal.Err() != nil { + return inputVal.Err() + } - if !val.IsConcrete() { - return fmt.Errorf("Generator is missing CreateInput") - } + // remake map with types based on schema + newMap := make(map[string]any) + for k, v := range inputMap { + // get the current input val + ival := inputVal.LookupPath(cue.ParsePath(k)) + if ival.Exists() { + switch t := v.(type) { + + // only handling string inputs + case string: + // switch 2 + switch ival.IncompleteKind() { + // another default copy over + case cue.StringKind: + newMap[k] = v + + // interseting part where we convert values + case cue.BoolKind: + fmt.Println("boolkind") + n, err := strconv.ParseBool(t) + if err != nil { + return err + } + newMap[k] = n + + case cue.IntKind: + n, err := strconv.ParseInt(t, 0, 64) + if err != nil { + return err + } + newMap[k] = n + + case cue.FloatKind: + n, err := strconv.ParseFloat(t, 64) + if err != nil { + return err + } + newMap[k] = n + + // end intersting inputs + + default: + newMap[k] = v + } + default: + newMap[k] = v + } + } else { + newMap[k] = v + } + } - prompt := G.CueValue.LookupPath(cue.ParsePath("CreatePrompt")) - if prompt.Err() != nil { - fmt.Println("error:", prompt.Err()) - return prompt.Err() - } + // fmt.Printf("newMap: %#v\n", newMap) - if !prompt.IsConcrete() || !prompt.Exists() { - return fmt.Errorf("Generator is missing CreatePrompt") + G.CueValue = G.CueValue.FillPath(cue.ParsePath("Create.Input"), newMap) } - // fmt.Printf("%s: %v\n", G.Name, val) - // fmt.Println(prompt) - - ans := map[string]any{} - // TODO deal with --input flags - - // process create prompts - // Loop through all top level fields - iter, err := prompt.List() + G.CueValue, err = runPrompt(G.CueValue) if err != nil { return err } - for iter.Next() { - value := iter.Value() - Q := map[string]any{} - err := value.Decode(&Q) - if err != nil { - return err - } - - // fmt.Println("q:", Q) - // todo, extract Name - A, err := handleQuestion(Q) - if err != nil { - return err - } - - // do we want to return a bool from handleQuestion - // to be more explicit about this check? - if A != nil { - ans[Q["Name"].(string)] = A - } - } - - // fill CreateInput from --inputs and prompt - G.CueValue = G.CueValue.FillPath(cue.ParsePath("CreateInput"), ans) - // fmt.Println("Final:", G.CueValue) - // return fmt.Errorf("intentional error") return nil } diff --git a/lib/create/handlers.go b/lib/create/handlers.go index 289eaf78b..d9c91c1cf 100644 --- a/lib/create/handlers.go +++ b/lib/create/handlers.go @@ -3,6 +3,7 @@ package create import ( "fmt" + "cuelang.org/go/cue" "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" ) @@ -20,11 +21,97 @@ func init() { "confirm": handleConfirm, "select": handleSelect, "multiselect": handleMultiselect, + "subgroup": handleSubgroup, // custom handlers } } +func runPrompt(genVal cue.Value) (result cue.Value, err error) { + // run while there are unanswered questions + // we run in an extra loop to fill back answers + // and recalculate the prompt questions each iteration + done := false + + for !done { + done = true + + inputVal := genVal.LookupPath(cue.ParsePath("Create.Input")) + if inputVal.Err() != nil { + return genVal, inputVal.Err() + } + + // fmt.Printf("outer loop input: %#v\n", inputVal) + + prompt := genVal.LookupPath(cue.ParsePath("Create.Prompt")) + if prompt.Err() != nil { + return genVal, prompt.Err() + } + if !prompt.IsConcrete() || !prompt.Exists() { + // to have a promptless generator, set it to the empty list + return genVal, fmt.Errorf("Generator is missing Create.Prompt, set to empty list for promptless") + } + + // prompt should be an ordered list of questions + iter, err := prompt.List() + if err != nil { + return genVal, err + } + + // loop over prompt questions, recursing as needed + for iter.Next() { + // todo, get label and check if input[label] is concrete + value := iter.Value() + + Q := map[string]any{} + err := value.Decode(&Q) + if err != nil { + return genVal, err + } + + // fmt.Printf("%#v\n", Q) + + name := Q["Name"].(string) + namePath := cue.ParsePath(name) + + // check if done already by inspececting in input + i := inputVal.LookupPath(namePath) + if i.Err() != nil { + if i.Exists() { + return genVal, i.Err() + } + } + if i.Exists() && i.IsConcrete() { + // question answer already exists in input + // fmt.Println("continuing: ", name) + continue + } + + // there is a question to answer + done = false + + // fmt.Println("q:", Q) + // todo, extract Name + A, err := handleQuestion(Q) + if err != nil { + if err == terminal.InterruptErr { + return genVal, fmt.Errorf("user interrupt") + } + return genVal, err + } + + // update input val + inputVal = inputVal.FillPath(namePath, A) + genVal = genVal.FillPath(cue.ParsePath("Create.Input"), inputVal) + + // restart the prompt loop + break + } + } + + return genVal, nil +} + func handleQuestion(Q map[string]any) (A any, err error) { // ask question until we get an answer or interrupt for { @@ -35,7 +122,7 @@ func handleQuestion(Q map[string]any) (A any, err error) { S, ok := s.(string) if !ok { - panic("question type not a string") + panic("question 'Type' is not set using a string format") } h, ok := handlers[S] @@ -47,7 +134,7 @@ func handleQuestion(Q map[string]any) (A any, err error) { if err != nil { if err == terminal.InterruptErr { - return nil, fmt.Errorf("user interrupt") + return nil, err } fmt.Println("error:", err) continue @@ -120,11 +207,12 @@ func handleConfirm(Q map[string]any) (A any, err error) { } var a bool err = survey.AskOne(prompt, &a) - if !a { - return nil, nil - } else { - A = a + if err != nil { + if err == terminal.InterruptErr { + return nil, err + } } + A = a if err != nil { return A, err @@ -186,3 +274,35 @@ func handleMultiselect(Q map[string]any) (A any, err error) { return A, err } + +func handleSubgroup(Q map[string]any) (A any, err error) { + // get some strings + name := Q["Name"].(string) + msg := Q["Prompt"].(string) + fmt.Println(msg) + + // get subgroup Questions + QS, ok := Q["Questions"] + if !ok { + return nil, fmt.Errorf("subgroup prompt %q is missing 'Questions' field", name) + } + + // gather nested answers + A2 := map[string]any{} + for _, Q2 := range QS.([]any) { + q2 := Q2.(map[string]any) + a2, e2 := handleQuestion(q2) + // todo, think about if/how to handle this error + if e2 != nil { + return nil, e2 + } + A2[q2["Name"].(string)] = a2 + } + + // set as nested value in A + a := map[string]any{} + a[name] = A2 + + return a, err +} + diff --git a/lib/datautils/io/io.go b/lib/datautils/io/io.go index 7a6f3abea..baf6c5e40 100644 --- a/lib/datautils/io/io.go +++ b/lib/datautils/io/io.go @@ -3,7 +3,6 @@ package io import ( "bytes" "encoding/json" - "fmt" "io" "io/ioutil" "path/filepath" @@ -12,7 +11,7 @@ import ( "github.com/clbanning/mxj" "github.com/naoina/toml" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) /* @@ -81,11 +80,6 @@ func ReadFile(filename string, obj *interface{}) (contentType string, err error) return "", err } - data, err = handleImports(data, filename) - if err != nil { - return "", err - } - ext := filepath.Ext(filename)[1:] switch ext { @@ -141,44 +135,11 @@ func ReadFile(filename string, obj *interface{}) (contentType string, err error) } } + // TODO, CUE once we can create values and share between runtimes + default: return InferDataContentType(data) } return "", errors.New("unknown content type") } - -const importTag = "#!import" - -func handleImports(orig []byte, filename string) (content []byte, err error) { - - if !bytes.Contains(orig, []byte(importTag)) { - return orig, nil - } - - dir := filepath.Dir(filename) - - lines := bytes.Split(orig, []byte("\n")) - for _, line := range lines { - if bytes.HasPrefix(line, []byte(importTag)) { - fields := bytes.Fields(line) - if len(fields) != 2 { - return orig, errors.New(fmt.Sprintf("Bad import statement '%s' in '%s'", string(line), filename)) - } - ifile := string(fields[1]) - ipath := filepath.Join(dir, ifile) - - data, err := ioutil.ReadFile(ipath) - if err != nil { - return orig, err - } - - content = append(content, data...) - } else { - content = append(content, line...) - } - content = append(content, '\n') - } - - return content, nil -} diff --git a/lib/gen/asmodule.go b/lib/gen/asmodule.go index 8f351a2ed..59f9ebf34 100644 --- a/lib/gen/asmodule.go +++ b/lib/gen/asmodule.go @@ -330,7 +330,7 @@ module {{ .Module }}/{{ .Name }} cue v0.4.3 require ( - github.com/hofstadter-io/hof v0.6.5 + github.com/hofstadter-io/hof v0.6.7-alpha.3 ) ` diff --git a/lib/gen/gen.go b/lib/gen/gen.go index ec26bdb00..16ceaede6 100644 --- a/lib/gen/gen.go +++ b/lib/gen/gen.go @@ -438,7 +438,7 @@ func InitModule(args []string, rootflags flags.RootPflagpole, cmdflags flags.Gen return err } // make some dirs - dirs := []string{"templates", "partials", "static", "examples", "gen", "schema"} + dirs := []string{"templates", "partials", "statics", "examples", "creators", "generators", "schema"} for _, dir := range dirs { err = os.MkdirAll(dir, 0755) if err != nil { @@ -510,10 +510,8 @@ import ( // your users do not set or see this field PackageName: string | *"{{ .Module }}/{{ .Name }}" - // these are the default globs to load from disk - Templates: [gen.#Templates & {Globs: ["./templates/**/*"], TrimPrefix: "./templates/"}] - Partials: [gen.#Templates & {Globs: ["./partials/**/*"], TrimPrefix: "./partials/"}] - Statics: [gen.#Statics & {Globs: ["./static/**/*"], TrimPrefix: "./static/"}] + // setup the default template locations + gen.#SubdirTemplates // The final list of files for hof to generate Out: [...gen.#File] & [ @@ -526,4 +524,3 @@ import ( ... } ` - diff --git a/lib/gen/runtime.go b/lib/gen/runtime.go index 07fc22a90..6238bee7e 100644 --- a/lib/gen/runtime.go +++ b/lib/gen/runtime.go @@ -35,6 +35,9 @@ type Runtime struct { rootToCwd string // module root -> working dir (foo/bar) cwdToRoot string // module root <- working dir (../..) + // Create related + OriginalWkdir string + // Hof related Generators map[string]*Generator Stats *RuntimeStats diff --git a/lib/mod/modder/modder_vendor.go b/lib/mod/modder/modder_vendor.go index dbfa9d9cf..b0e461c32 100644 --- a/lib/mod/modder/modder_vendor.go +++ b/lib/mod/modder/modder_vendor.go @@ -99,6 +99,7 @@ func (mdr *Modder) VendorDep(R Replace) error { // Fetch and Load module if strings.HasPrefix(R.NewPath, "/") || strings.HasPrefix(R.NewPath, "./") || strings.HasPrefix(R.NewPath, "../") { + // fmt.Println("local replace:", R) err := mdr.LoadLocalReplace(R) if err != nil { mdr.errors = append(mdr.errors, err) @@ -172,8 +173,9 @@ func (mdr *Modder) LoadLocalReplace(R Replace) error { ReplaceModule: R.NewPath, ReplaceVersion: R.NewVersion, } + // fmt.Printf("LoadLocalReplace %#+v\n", m) - m.FS = osfs.New(R.NewPath) + m.FS = osfs.New(m.ReplaceModule) err = m.LoadMetaFiles(mdr.ModFile, mdr.SumFile, mdr.MappingFile, true /* ignoreReplace directives */) if err != nil { diff --git a/lib/mod/modder/modder_write.go b/lib/mod/modder/modder_write.go index 3be525700..a6bf23cfc 100644 --- a/lib/mod/modder/modder_write.go +++ b/lib/mod/modder/modder_write.go @@ -78,7 +78,10 @@ func (mdr *Modder) WriteVendor() error { // fmt.Printf("Writing %-48s => %s\n", m.ReplaceModule + "@" + m.ReplaceVersion, baseDir) // Should we make a symlink for a local replace? - if mdr.SymlinkLocalReplaces && (strings.HasPrefix(m.ReplaceModule, "../") || strings.HasPrefix(m.ReplaceModule, "/")) { + hasLocalPrefix := strings.HasPrefix(m.ReplaceModule, "../") || strings.HasPrefix(m.ReplaceModule, "./") || strings.HasPrefix(m.ReplaceModule, "/") + // fmt.Println("symlinking?", mdr.SymlinkLocalReplaces, hasLocalPrefix, m.ReplaceModule, strings.HasPrefix(m.ReplaceModule, "/")) + // fmt.Printf("m: %#v\n", m) + if mdr.SymlinkLocalReplaces && hasLocalPrefix { // count and create backPaths string backPaths := strings.Repeat("../", strings.Count(baseDir, "/")) // create final symlink string @@ -95,6 +98,8 @@ func (mdr *Modder) WriteVendor() error { return fmt.Errorf("While creating baseDir for local replace\n%w\n", err) } + fmt.Printf("local replace: symlinking %s to %s\n", m.Module, m.ReplaceModule) + // create the actual symlink err = os.Symlink(originalBaseDir, targetSymlink) if err != nil { @@ -125,6 +130,8 @@ func (mdr *Modder) WriteVendor() error { } } + // fmt.Println("writing local vendor") + if len(mdr.VendorIncludeGlobs) > 0 || len(mdr.VendorExcludeGlobs) > 0 { // Just copy everything // TODO, these functions should just take 2 billy FS diff --git a/schema/create/create.cue b/schema/create/create.cue index 0723b7829..a3781a14e 100644 --- a/schema/create/create.cue +++ b/schema/create/create.cue @@ -1,21 +1,26 @@ package create -#Creator: { +#Creator: Create: { // schema and filled value for the create inputs // all inputs will be unified with this (files, flags, prompt) // it's contents should likely align with the prompt nesting - CreateInput?: {...} + Input: {...} // Init time inputs and prompts // if an entry will is already set by flags, it will be skipped - CreatePrompt?: [...#Question] + Prompt: [...#Question] // (todo) Messages to print at start and end - CreateMessage?: { + Message?: { Before: string After: string } + + // todo / consider + // Check: _ // check for tools on host system + // PreFlow: _ // run hof flow beforehand + // PostFlow: _ // run hof flow afterwards } #Question: { diff --git a/test/create/cue.mods b/test/create/cue.mods new file mode 100644 index 000000000..a97a4af23 --- /dev/null +++ b/test/create/cue.mods @@ -0,0 +1,9 @@ +module hof.io/test + +cue 0.4.3 + +require ( + github.com/hofstadter-io/hof v0.6.7 +) + +replace github.com/hofstadter-io/hof => /home/tony/hof/hof diff --git a/test/create/local/cue.mod/module.cue b/test/create/local/cue.mod/module.cue new file mode 100644 index 000000000..c8afdde72 --- /dev/null +++ b/test/create/local/cue.mod/module.cue @@ -0,0 +1 @@ +module: "hof.io/test" diff --git a/test/create/local/cue.mods b/test/create/local/cue.mods new file mode 100644 index 000000000..a97a4af23 --- /dev/null +++ b/test/create/local/cue.mods @@ -0,0 +1,9 @@ +module hof.io/test + +cue 0.4.3 + +require ( + github.com/hofstadter-io/hof v0.6.7 +) + +replace github.com/hofstadter-io/hof => /home/tony/hof/hof diff --git a/test/create/local/cue.sums b/test/create/local/cue.sums new file mode 100644 index 000000000..8cab1833b --- /dev/null +++ b/test/create/local/cue.sums @@ -0,0 +1,8 @@ +github.com/hofstadter-io/ghacue v0.1.1 h1:STfOkN4CrDGBnM6DcSrJp6ph8ZMY4tsyqqJIXvLKHkQ= +github.com/hofstadter-io/ghacue v0.1.1/cue.mods h1:6BbcRic6px1+lCLiW6O5dYt+GcCpnmzdpbcILHrqhOQ= +github.com/hofstadter-io/hof v0.6.6 h1:WxwnjKrxmP9NvpM3bWIYiRAHrdf5KiPYRFlY6dvjV8I= +github.com/hofstadter-io/hof v0.6.6/cue.mods h1:4VCt4OIY3Gri2GSX/u3bMQhRs6BFHmbJTn3Nq8ccqlE= +github.com/hofstadter-io/hof v0.6.7 h1:o+Kz6hcltSYXEM0mj8l/5fW3TuDbsx2LKMWHhnlTcGQ= +github.com/hofstadter-io/hof v0.6.7/cue.mods h1:oviNBMPfHXTfrFxghSPejQxzWnNV0U2Ntjd4P13CQPk= +github.com/hofstadter-io/hofmod-cli v0.7.11 h1:RxVCe+uI1r2RcmTQ6K14EAjV+x7+AwpRGy1f6DofXW0= +github.com/hofstadter-io/hofmod-cli v0.7.11/cue.mods h1:ta2i4a3ruKBtxlgnqnD9m/qj9RzXDwYNSD0uA72a79o=