{"id":19900588,"url":"https://github.com/gobuffalo/genny","last_synced_at":"2025-04-06T05:18:12.871Z","repository":{"id":33966853,"uuid":"140339384","full_name":"gobuffalo/genny","owner":"gobuffalo","description":"A framework for writing modular generators","archived":false,"fork":false,"pushed_at":"2022-10-06T15:29:43.000Z","size":619,"stargazers_count":65,"open_issues_count":2,"forks_count":14,"subscribers_count":10,"default_branch":"main","last_synced_at":"2024-10-29T19:21:18.897Z","etag":null,"topics":["framework","generator","go","gobuffalo","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gobuffalo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"markbates","patreon":"buffalo"}},"created_at":"2018-07-09T20:35:12.000Z","updated_at":"2023-06-04T06:52:12.000Z","dependencies_parsed_at":"2022-07-15T06:47:18.953Z","dependency_job_id":null,"html_url":"https://github.com/gobuffalo/genny","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobuffalo%2Fgenny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobuffalo%2Fgenny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobuffalo%2Fgenny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobuffalo%2Fgenny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gobuffalo","download_url":"https://codeload.github.com/gobuffalo/genny/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247436449,"owners_count":20938566,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","generator","go","gobuffalo","golang"],"created_at":"2024-11-12T20:12:42.313Z","updated_at":"2025-04-06T05:18:12.850Z","avatar_url":"https://github.com/gobuffalo.png","language":"Go","funding_links":["https://github.com/sponsors/markbates","https://patreon.com/buffalo"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/gobuffalo/buffalo/blob/main/logo.svg\" width=\"360\"\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/gobuffalo/genny/actions\"\u003e\u003cimg src=\"https://github.com/gobuffalo/genny/workflows/Tests/badge.svg\" alt=\"Actions Status\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"https://godoc.org/github.com/gobuffalo/genny\"\u003e\u003cimg src=\"https://godoc.org/github.com/gobuffalo/genny?status.svg\" alt=\"GoDoc\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://travis-ci.org/gobuffalo/genny\"\u003e\u003cimg src=\"https://travis-ci.org/gobuffalo/genny.svg?branch=master\" alt=\"Build Status\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://goreportcard.com/report/github.com/gobuffalo/genny\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/gobuffalo/genny\" alt=\"Go Report Card\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Genny\n\n## What Is Genny?\n\nGenny is a _framework_ for writing modular generators, it however, doesn't actually generate anything. It just makes it easier for you to. :)\n\n## Core Concepts\n\n### Generators\n\nA [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) is used to build a blue print of what you want to generate.\n\nA few of things that can be added to a `Generator` are:\n\n* [`github.com/gobuffalo/genny#File`](https://godoc.org/github.com/gobuffalo/genny#File)\n* [`os/exec#Cmd`](https://godoc.org/os/exec#Cmd)\n* [`github.com/gobuffalo/packd#Box`](https://godoc.org/github.com/gobuffalo/packd#Box)\n* [`net/http#Request`](https://godoc.org/net/http#Request)\n* and more\n\nA [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) does *not* actually generate anything; a [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) is needed to run the generator.\n\n```go\ng := genny.New()\n\n// add a file\ng.File(genny.NewFileS(\"index.html\", \"Hello\\n\"))\n\n// execute a command\ng.Command(exec.Command(\"go\", \"env\"))\n\n// run a function at run time\ng.RunFn(func(r *genny.Runner) error {\n  // look for the `genny` executable\n  if _, err := r.LookPath(\"genny\"); err != nil {\n    // it wasn't found, so install it\n    c := gogen.Get(\"github.com/gobuffalo/genny/genny\")\n    if err := r.Exec(c); err != nil {\n      return err\n    }\n  }\n  // call the `genny` executable with the `-h` flag.\n  return r.Exec(exec.Command(\"genny\", \"-h\"))\n})\n```\n\nWhen a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) is run each item that was added to it will be run in FIFO order. In the above example this means the following will happen:\n\n1. Create a new file `r.Root/index.html`\n1. Run the command `go env`\n1. Run a function that installs `genny`\n\n#### Runtime Checks\n\nGenny has two different components; the \"generator\" (or blueprint) and the \"runner\" which executes the generator. Often it is necessary to only run certain code when the generator is run, not built. For example, checking the existing of an executable and installing it if missing.\n\nIn these situations you will want to use a [`github.com/gobuffalo/genny#RunFn`](https://godoc.org/github.com/gobuffalo/genny#RunFn) function.\n\nIn this example at runtime the `RunFn` will be called given the `*Runner` that is calling it. When called the function will ask the [`github.com/gobuffalo/genny#Runner.LookPath`](https://godoc.org/github.com/gobuffalo/genny#Runner.LookPath) function to ask the location of the `genny` executable.\n\nIn [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner) this will simply echo back the name of the executable that has been asked for, in this case `return \"genny\", nil`.\n\nIn [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner) this will call the [`os/exec#LookPath`](https://godoc.org/os/exec#LookPath) and return its results.\n\nIf the `genny` binary is not found, it will attempt to install it. Should that succeed the method returns the execution of a call to `genny -h`.\n\n```go\ng.RunFn(func(r *genny.Runner) error {\n  // look for the `genny` executable\n  if _, err := r.LookPath(\"genny\"); err != nil {\n    // it wasn't found, so install it\n    c := gogen.Get(\"github.com/gobuffalo/genny/genny\")\n    if err := r.Exec(c); err != nil {\n      return err\n    }\n  }\n  // call the `genny` executable with the `-h` flag.\n  return r.Exec(exec.Command(\"genny\", \"-h\"))\n})\n```\n\nThe flexibility of the `*Fn` functions, combined with [`github.com/gobuffalo/genny#RunFn`](https://godoc.org/github.com/gobuffalo/genny#RunFn) make for a powerful testing combination.\n\n### Runners\n\nA [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) is used to run generators and control the environment in which those generators are run.\n\nGenny ships with three implementations of `Runner` that cover _most_ situations. They can also provide good starting points for customized implementations of `Runner`.\n\n* [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner)\n* [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner)\n* [`github.com/gobuffalo/genny/gentest#NewRunner`](https://godoc.org/github.com/gobuffalo/genny/gentest#NewRunner)\n\n#### Adding Generators\n\nTo add a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) to a [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) the [`github.com/gobuffalo/genny#Runner.With`](https://godoc.org/github.com/gobuffalo/genny#Runner.With) function can be used.\n\n```go\nrun := genny.DryRunner(context.Background())\n\n// add a generator from the `simple` package\ng := simple.New()\nrun.With(g)\n\n// add a generator from the `notsimple` package\ng := notsimple.New()\nrun.With(g)\n```\n\nEach [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) is run in FIFO order in which it was added to the [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner).\n\nIt is common to have a function that builds a new [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) or returns an `error` if there was a problem.\n\n```go\nfunc New() (*genny.Generator, error) {\n  g := simple.New()\n  // do work which might error\n  return g, nil\n}\n```\n\nThe [`github.com/gobuffalo/genny#Runner.WithNew`](https://godoc.org/github.com/gobuffalo/genny#Runner.WithNew) function was designed to make adding a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) with this return argument signature easier.\n\n```go\nif err := run.WithNew(New()); err != nil {\n  log.Fatal(err)\n}\n```\n\n#### Dry Running (**NON-DESTRUCTIVE**)\n\nThe idea of \"dry\" running means that no commands are executed, no files are written to disk, no HTTP requests are made, etc... Instead these steps are run \"dry\", which in the case of [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner) is the case.\n\n```go\nfunc main() {\n  run := genny.DryRunner(context.Background())\n\n  g := simple.New()\n  run.With(g)\n\n  if err := run.Run(); err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\n```plain\n// output\nDEBU[2018-12-06T15:13:47-05:00] Step: 4eac628c\nDEBU[2018-12-06T15:13:47-05:00] Chdir: /go/src/github.com/gobuffalo/genny/internal/_examples/dry\nDEBU[2018-12-06T15:13:47-05:00] File: /go/src/github.com/gobuffalo/genny/internal/_examples/dry/index.html\nDEBU[2018-12-06T15:13:47-05:00] Exec: go env\nDEBU[2018-12-06T15:13:47-05:00] LookPath: genny\nDEBU[2018-12-06T15:13:47-05:00] Exec: genny -h\n```\n\n```bash\n// file list\n.\n└── main.go\n\n0 directories, 1 file\n```\n\nUsing a \"dry\" runner can make testing easier when you don't have to worry about commands running, files being written, etc... It can also make it easy to provide a \"dry-run\" flag to your generators to let people see what will be generated when the generator is run for real.\n\n#### Wet Running (**DESTRUCTIVE**)\n\nWhile \"dry\" means to not execute commands or write files, \"wet\" running means the exact opposite; it will write files and execute commands.\n\nUse the [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner) when \"wet\" running is the desired outcome.\n\n```go\nfunc main() {\n  run := genny.WetRunner(context.Background())\n\n  g := simple.New()\n  run.With(g)\n\n  if err := run.Run(); err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\n```plain\nGOARCH=\"amd64\"\nGOBIN=\"\"\n// ...\nA brief description of your application\n\nUsage:\n  genny [command]\n\nAvailable Commands:\n  help        Help about any command\n  new         generates a new genny stub\n\nFlags:\n  -h, --help   help for genny\n\nUse \"genny [command] --help\" for more information about a command.\n```\n\n```bash\n// file list\n.\n├── index.html\n└── main.go\n\n0 directories, 2 files\n```\n\n```bash\n$ cat index.html\n\nHello\n```\n\n#### Changing Runner Behavior\n\nThe change the way [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner) or [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner) work, or to build your own [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) you need to implement the `*Fn` attributes on the [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner).\n\n```go\ntype Runner struct {\n  // ...\n  ExecFn     func(*exec.Cmd) error                                     // function to use when executing files\n  FileFn     func(File) (File, error)                                  // function to use when writing files\n  ChdirFn    func(string, func() error) error                          // function to use when changing directories\n  DeleteFn   func(string) error                                        // function used to delete files/folders\n  RequestFn  func(*http.Request, *http.Client) (*http.Response, error) // function used to make http requests\n  LookPathFn func(string) (string, error)                              // function used to make exec.LookPath lookups\n  // ...\n}\n```\n\nThese `*Fn` functions represent the **FINAL** end-point for the that is trying to be run.\n\nHere are two implementations of the [`github.com/gobuffalo/genny#Runner.FileFn`](https://godoc.org/github.com/gobuffalo/genny#Runner.FileFn) function.\n\nThe first will result in the file being printed to the screen. The second implementation writes the file to disk.\n\n```go\nrun.FileFn = func(f packd.SimpleFile) (packd.SimpleFile, error) {\n  io.Copy(os.Stdout, f)\n  return f, nil\n}\n\nrun.FileFn = func(f genny.File) (genny.File, error) {\n  if d, ok := f.(genny.Dir); ok {\n    if err := os.MkdirAll(d.Name(), d.Perm); err != nil {\n      return f, err\n    }\n    return d, nil\n  }\n\n  name := f.Name()\n  if !filepath.IsAbs(name) {\n    name = filepath.Join(run.Root, name)\n  }\n  dir := filepath.Dir(name)\n  if err := os.MkdirAll(dir, 0755); err != nil {\n    return f, err\n  }\n  ff, err := os.Create(name)\n  if err != nil {\n    return f, err\n  }\n  defer ff.Close()\n  if _, err := io.Copy(ff, f); err != nil {\n    return f, err\n  }\n  return f, nil\n}\n```\n\n### Files\n\nWorking with files, both creating new ones as well as, existing ones, is a core component of writing a generator. Genny understands this and offers several ways of working with files that is flexible and helps to make writing and testing your generators easier.\n\nThe [`github.com/gobuffalo/genny#File`](https://godoc.org/github.com/gobuffalo/genny#File) interface is the heart of working with files in Genny.\n\nGenny ships with several convenience method for creating a [`github.com/gobuffalo/genny#File`](https://godoc.org/github.com/gobuffalo/genny#File).\n\n* [`github.com/gobuffalo/genny#NewFile`](https://godoc.org/github.com/gobuffalo/genny#NewFile)\n* [`github.com/gobuffalo/genny#NewFileS`](https://godoc.org/github.com/gobuffalo/genny#NewFileS)\n* [`github.com/gobuffalo/genny#NewFileB`](https://godoc.org/github.com/gobuffalo/genny#NewFileB)\n* [`github.com/gobuffalo/genny#NewDir`](https://godoc.org/github.com/gobuffalo/genny#NewDir)\n\n#### Writing Files\n\nTo write a file you can add a [`github.com/gobuffalo/genny#File`](https://godoc.org/github.com/gobuffalo/genny#File) to your [`github.com/gobuffalo/genny#Generator.File`](https://godoc.org/github.com/gobuffalo/genny#Generator.File) and your file will then be handled by your `*Runner` when your generator is run.\n\n```go\ng.File(genny.NewFile(\"index.html\", strings.NewReader(\"Hello\\n\")))\ng.File(genny.NewFileS(\"strings/string.html\", \"Hello\\n\"))\ng.File(genny.NewFileB(\"bytes/byte.html\", []byte(\"Hello\\n\")))\n```\n\nIn the case of [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner) will attemp to create any directories your files require.\n\n#### Reading Files\n\nWhen writing generators you may need to read an existing file, perhaps to modify it, or perhaps read it's contents. This presents a problem in generators.\n\nThe first problem is that anytime we have to read files from disk, we make testing more difficult.\n\nThe bigger problems, however, present themselves more with \"dry\" runners (for example testing), than they do with \"wet\" runners.\n\nIf generator `A` creates a new file and generator `B` wants to modify that file in testing and \"dry\" runners this is a problem as the file may not present on disk for generator `B` to access.\n\nTo work around this issue Genny has the concept of a [`github.com/gobuffalo/genny#Disk`](https://godoc.org/github.com/gobuffalo/genny#Disk).\n\nNow, instead of asking for the file directly from the file system, we can ask for it from the [`github.com/gobuffalo/genny#Runner.Disk`](https://godoc.org/github.com/gobuffalo/genny#Runner.Disk) instead.\n\n```go\ng.RunFn(func(r *genny.Runner) error {\n  // try to find main.go either in the virtual \"disk\"\n  // or the physical one\n  f, err := r.Disk.Find(\"main.go\")\n  if err != nil {\n    return err\n  }\n  // print the contents of the file\n  fmt.Println(f.String())\n  return nil\n})\n```\n\nWhen asking for files from [`github.com/gobuffalo/genny#Runner.Disk`](https://godoc.org/github.com/gobuffalo/genny#Runner.Disk) it will first check its internal cache for the file, returning it if found. If the file is not in the cache, then it try to read it from disk at `filepath.Join(r.Root, name)`.\n\n#### Transforming Files\n\nThere are times that you may need to transform either certain files, or all files. This could be as simple as replacing a variable in a template's name to match some user input, or something more complex, such as running any templates with a given extension through a certain template engine.\n\nThe [`github.com/gobuffalo/genny#Transformer`](https://godoc.org/github.com/gobuffalo/genny#Transformer) type can be used to implement these types of file transformations.\n\nTo create a new [`github.com/gobuffalo/genny#Transformer`](https://godoc.org/github.com/gobuffalo/genny#Transformer) you can use the [`github.com/gobuffalo/genny#NewTransformer`](https://godoc.org/github.com/gobuffalo/genny#NewTransformer) function.\n\nThe example below is taken from the [`github.com/gobuffalo/plushgen`](https://godoc.org/github.com/gobuffalo/plushgen) package.\n\n```go\n// Transformer will plushify any file that has a \".plush\" extension\nfunc Transformer(ctx *plush.Context) genny.Transformer {\n  t := genny.NewTransformer(\".plush\", func(f genny.File) (genny.File, error) {\n    s, err := plush.RenderR(f, ctx)\n    if err != nil {\n      return f, errors.Wrap(err, f.Name())\n    }\n    return genny.NewFileS(f.Name(), s), nil\n  })\n  t.StripExt = true\n  return t\n}\n```\n\nThe [`github.com/gobuffalo/genny#Transformer`](https://godoc.org/github.com/gobuffalo/genny#Transformer) that is returned in the example will only be run on files that have a `.plush` extension in their name.\n\nShould a file have a `.plush` extension, it will be sent to [`github.com/gobuffalo/plush`](https://godoc.org/github.com/gobuffalo/plush) to be rendered. The result of that rendering is returned as a new [`github.com/gobuffalo/genny#File`](https://godoc.org/github.com/gobuffalo/genny#File). Finally, the extension `.plush` will be stripped from the file name.\n\n```go\ng := genny.New()\n\n// add a file\ng.File(genny.NewFileS(\"index.html.plush\", \"Hello \u003c%= name %\u003e\\n\"))\n\n// add the plush transformer\nctx := plush.NewContext()\nctx.Set(\"name\", \"World\")\ng.Transformer(plushgen.Transformer(ctx))\n```\n\n```plain\n// output\nDEBU[2018-12-07T10:35:56-05:00] Step: 09c9663e\nDEBU[2018-12-07T10:35:56-05:00] Chdir: /go/src/github.com/gobuffalo/genny/internal/_examples/dry\nDEBU[2018-12-07T10:35:56-05:00] File: /go/src/github.com/gobuffalo/genny/internal/_examples/dry/index.html\nHello World\n```\n\n### Testing\n\nTesting a generator can be difficult because creating, deleting, and modifying files can be painful to handle during testing. The same can be said of running functions and HTTP requests.\n\nThe `*Fn` attributes on [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) make it simplier to mock out different test cases.\n\nMost of the time the out of the box defaults are \"good enough\" for testing. The [`github.com/gobuffalo/genny/gentest`](https://godoc.org/github.com/gobuffalo/genny/gentest) package offers several helpers to simplify testing further.\n\nIn this example we test the \"happy\" path of a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator).\n\n```go\nfunc Test_Happy(t *testing.T) {\n  r := require.New(t)\n\n  run := gentest.NewRunner()\n  run.Disk.Add(genny.NewFileS(\"main.go\", \"my main.go file\"))\n\n  g := New()\n  run.With(g)\n\n  r.NoError(run.Run())\n  res := run.Results()\n\n  cmds := []string{\"go env\", \"genny -h\"}\n  r.NoError(gentest.CompareCommands(cmds, res.Commands))\n\n  files := []string{\"index.html\", \"main.go\"}\n  r.NoError(gentest.CompareFiles(files, res.Files))\n}\n```\n\nNotice how in the above example we had to add `main.go` to the [`github.com/gobuffalo/genny#Runner.Disk`](https://godoc.org/github.com/gobuffalo/genny#Runner.Disk). That is because the file doesn't exist in our testing directory.\n\nIn the following example we test what happens when the `genny` executable can not be found when running the [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator).\n\nWe can simulate this experience by using the [`github.com/gobuffalo/genny#Runner.LookPathFn`](https://godoc.org/github.com/gobuffalo/genny#Runner.LookPathFn) to return an error if it is asked about that particular executable.\n\n```go\nfunc Test_Missing_Genny(t *testing.T) {\n  r := require.New(t)\n\n  run := gentest.NewRunner()\n  run.Disk.Add(genny.NewFileS(\"main.go\", \"my main.go file\"))\n\n  g := New()\n  run.With(g)\n\n  // pretend we can't find genny\n  run.LookPathFn = func(s string) (string, error) {\n    if s == \"genny\" {\n      return \"\", errors.New(\"can't find genny\")\n    }\n    return s, nil\n  }\n\n  r.NoError(run.Run())\n  res := run.Results()\n\n  cmds := []string{\"go env\", \"go get github.com/gobuffalo/genny/genny\", \"genny -h\"}\n  r.NoError(gentest.CompareCommands(cmds, res.Commands))\n\n  files := []string{\"index.html\", \"main.go\"}\n  r.NoError(gentest.CompareFiles(files, res.Files))\n}\n```\n\n## The `genny` Executable\n\nGenny ships with an executable that helps to generate new generators.\n\n### Installation\n\n```bash\n$ go get -u github.com/gobuffalo/genny/genny\n```\n\n### Usage\n\n```bash\n$ genny -h\n\ntools for working with genny\n\nUsage:\n  genny [command]\n\nAvailable Commands:\n  help        Help about any command\n  new         generates a new genny stub\n\nFlags:\n  -h, --help   help for genny\n\nUse \"genny [command] --help\" for more information about a command.\n```\n\n### Generating a New Generator\n\n```bash\n$ genny new coke -h\n\nDEBU[2018-12-07T11:07:01-05:00] Step: a1d8eb2f\nDEBU[2018-12-07T11:07:01-05:00] Chdir: /go/src/github.com/gobuffalo\nDEBU[2018-12-07T11:07:01-05:00] File: /go/src/github.com/gobuffalo/coke/coke.go\nDEBU[2018-12-07T11:07:01-05:00] File: /go/src/github.com/gobuffalo/coke/coke_test.go\nDEBU[2018-12-07T11:07:01-05:00] File: /go/src/github.com/gobuffalo/coke/options.go\nDEBU[2018-12-07T11:07:01-05:00] File: /go/src/github.com/gobuffalo/coke/options_test.go\nDEBU[2018-12-07T11:07:01-05:00] File: /go/src/github.com/gobuffalo/coke/templates/example.txt\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgobuffalo%2Fgenny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgobuffalo%2Fgenny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgobuffalo%2Fgenny/lists"}