{"id":16846812,"url":"https://github.com/mumoshu/gosh","last_synced_at":"2025-04-11T06:32:29.717Z","repository":{"id":49390010,"uuid":"372127209","full_name":"mumoshu/gosh","owner":"mumoshu","description":"(Re)write your bash script in Go and make it testable too","archived":false,"fork":false,"pushed_at":"2021-08-09T08:46:26.000Z","size":306,"stargazers_count":25,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-13T13:05:44.376Z","etag":null,"topics":["bash","go"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mumoshu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-30T05:00:57.000Z","updated_at":"2024-03-15T19:30:53.000Z","dependencies_parsed_at":"2022-08-26T04:12:06.092Z","dependency_job_id":null,"html_url":"https://github.com/mumoshu/gosh","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fgosh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fgosh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fgosh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fgosh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mumoshu","download_url":"https://codeload.github.com/mumoshu/gosh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248355805,"owners_count":21090087,"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":["bash","go"],"created_at":"2024-10-13T13:05:13.656Z","updated_at":"2025-04-11T06:32:29.686Z","avatar_url":"https://github.com/mumoshu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gosh\n\n`gosh` is a framework for operating and extending Linux shells with Go.\n\nThe author started developing `gosh` to:\n\n- Make it extremely easy to gradually rewrite your complex shell script into a more maintainable equivalent - A Go application\n- Make it extremely easy to write a maintainable End-to-End test that involves many shell commands and takes an hour or so to run\n\nBut you can also use it for the following use-cases:\n\n- Write Go instead of Shell scripts\n- Build your project, as an alternative to `make`\n- Write Bash functions in Go\n- Build your own shell with custom functions written in Go\n- Incrementally refactor your Bash scripts with Go\n- Test your Bash scripts, as a dependency injection system and a test framework/runner\n\nFor more information, see the respective guides below:\n\n- [Interactive Shell with Hot Reloading](#interactive-shell-with-hot-reloading)\n- [Commands and Pipelines](#commands-and-pipelines)\n- [Use as a Build Tool](#use-as-a-build-tool)\n- [Go interoperability](#go-interoperability)\n  - [Automatic Arguments](#automatic-arguments), [Automatic Flags](#automatic-flags)\n- [Diagnostic Logging](#diagnostic-logging)\n- [`go test` Integration](#go-test-integration)\n- [Ginkgo Integration](#ginkgo-integration)\n- [Writing End-to-End test](#writing-end-to-end-test)\n\nNote that `gosh` primarily targets `bash` today. But it can be easily enhanced to support other shells, too.\nAny contributions to add more shell supports are always welcomed.\n\n## Getting Started\n\nGet started by writing a Go application provides a shell that has a built-in `hello` function:\n\n```go\n$ mkdir ./myapp; cat \u003c\u003cEOS \u003e ./myapp/main.go\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mumoshu/gosh\"\n)\n\nfunc main() {\n\tsh := \u0026gosh.Shell{}\n\n\tsh.Export(\"hello\", func(ctx gosh.Context, target string) {\n\t\tctx.Stdout().Write([]byte(\"hello \" + target + \"\\n\"))\n\t})\n\n\tsh.MustExec(os.Args)\n}\nEOS\n```\n\nGo-running it takes you into a shell session that has the builtin function:\n\n```\n$ go run ./myapp\n\nbash$ hello world\nhello world\n```\n\nThis shell session rebuilds your command automatically so that you can test the modified version of `hello` function without restarting the session.\n\nOnce you're confident with what you built, you'd want to distribute it.\n\nJust use a standard `go build` command to create a single executable that provides a `bash` enviroment that provides custom functions:\n\n```\n$ go build -o myapp ./myapp\n```\n\nYou can directly invoke the custom function by providing the command name and arguments like:\n\n```\n$ myapp hello world\n```\n\n```\nhello world!\n```\n\nAs it's a bash in the end, you can run a shell script, with access to the `hello` function written in Go:\n\n```\n$ myapp \u003c\u003cEOS\nfor ((i=0; i\u003c3; i++); do\n  hello world\ndone\nEOS\n```\n\n```\nhello world!\nhello world!\nhello world!\n```\n\nThe custom functions and the shell environemnt can be used to not only accelerating your application, but also to mock your commands for testing. The possibilities are endless. If you're curious, read on.\n\n# Features\n\n## Custom Functions written in Go\n\nYou can `go run` your app to call a single custom function, or a shell script that invokes the custom function.\n\nOur [`getting-started` example](./examples/getting-started) defines a single custom function `hello` that takes only one argument to specify the part of the hello-world message printed by it.\n\nSo, `hello world` should print `hello world`.\n\nTo invoke it directly, just provide the command and the args via the command-line.\n\nWith `go run`, that can be done by:\n\n```\n$ go run ./examples/getting-started hello world\nhello world\n```\n\nIt can be executed the same way against a prebuilt binary:\n\n```\n$ go build -o getting-started ./examples/getting-started\n$ ./getting-started hello world\nhello world\n```\n\nIt works exactly like a standard shell equipped with custom functions.\n\nAs similar as you can provide shell scripts to `bash` via the standard input, you can do the same on your command:\n\n```\n$ go run ./examples/getting-started \u003c\u003cEOS\nfor ((i=0; i\u003c3; i++)); do\nhello world\ndone\nEOS\n```\n\n```\nhello world\nhello world\nhello world\n```\n\n```\n$ cat \u003c\u003cEOS \u003e test.gosh\nfor ((i=0; i\u003c3; i++)); do\nhello world\ndone\nEOS\n```\n\nThen you can point it to a file or use redirection to source the script to run, as you would usually do with bash:\n\n```\n$ go run ./examples/getting-started test.gosh\n$ go run ./examples/getting-started \u003ctest.gosh\n```\n\n## Interactive Shell with Hot Reloading\n\nYou can `go run` your app without any arguments to start an interactive shell that hot reloads the custom functions automatically:\n\n```\n$ go run ./examples/getting-started\n```\n\nOnce the new interactive shell session gets started, you use it like a regular shell.\n\nThe `getting-started` example contains a custom function written in Go, named `hello`, that prints `hello \u003cFIRST ARG\u003e` to the standard output.\n\n```go\nsh.Export(\"hello\", func(ctx gosh.Context, target string) {\n    ctx.Stdout().Write([]byte(\"hello \" + target + \"\\n\"))\n})\n```\n\nTo invoke `hello`, refer to it like a standard shell function:\n\n```\ngosh$ hello world\nhello world\n```\n\nThe interactive shell hot-reloads your Go code.\nThat is, you can modify the custom function code and just reinvoke `hello`, without restarting the shell.\n\nFor example, we modify the code so that the custom function prints it without another prefix `konnichiwa`:\n\n```\n$ code ./examples/getting-started/main.go\n```\n\n```go\nsh.Export(\"hello\", func(ctx gosh.Context, target string) {\n    ctx.Stdout().Write([]byte(\"konnichiwa \" + target + \"\\n\"))\n})\n```\n\nGo back to your termiinal and rerun `hello world` to see the code hot-reloaded:\n\n```\ngosh$ hello world\nkonnichiwa world\n```\n\n## Commands and Pipelines\n\n`gosh` has a convenient helper functions to write command executions and shell pipelines in Go, as easy as you've been in a standard *nix shell like Bash.\n\nSee the [commands](./examples/commands/commands.go) example for more information.\n\nIn the example, we implement `gocat` and `gogrep` in Go, each is the simplest possible alternatives to standard `cat` and `grep`, respectively.\n\nRunning the example takes you into a custom shell session as usual:\n\n```\n$ go run ./examples/commands/cmd\n```\n\nCreate an example input file:\n\n```bash\n$ cat \u003c\u003cEOS \u003e input.txt\nfoo\nbar\nbaz\nEOS\n```\n\nNow, let's try any of the following combinations of the standard and custom commands and see the output is consistent across runs, which means we've successfully reimplemented `cat` and `grep` in Go.\n\n```\n$ cat input.txt | grep bar\n$ gocat input.txt | grep bar\n$ cat input.txt | gogrep bar\n$ gocat input.txt | gogrep bar\n```\n\n## Use as a Build Tool\n\nAs you can seen in our [`project` example](project/build.go), `gosh` has a few utilities to help\nusing your `gosh` application as a build tool like `make`.\n\nLet's say you previously had a `Makefile` that looked like this:\n\n```makefile\n.PHONY: all build test\n\nall: build test\n\nbuild:\n    go build -o getting-started ./examples/getting-started\n\ntest:\n    go test ./...\n```\n\nYou can rewrite it by using some Go code powered by `gosh` that looks like the below:\n\n```go\nExport(\"all\", Dep(\"build\"), Dep(\"test\"), func() {\n\n})\n\nExport(\"build\", func() {\n    Run(\"go\", \"build\", \"-o\", \"getting-started\", \"./examples/getting-started\")\n})\n\nExport(\"test\", func() {\n    Run(\"go\", \"test\", \"./...\")\n})\n```\n\n`Dep` is a function provided by `gosh` to let it run the said command before running the exported function itself.\n\nSo, in the above example, running `all` triggers runs of `build` and `test` beforehand.\n\nInstead of `make all`, `make build`, and `make test` you used to run, you can now run respective `go run` commands:\n\n```\n# Runs a go build\n$ go run -tags=project ./project build\n\n# Runs a go test\n$ go run -tags=project ./project test\n\n# runs test and build\n$ go run -tags=project ./project \u003c\u003cEOS\ntest\nbuild\nEOS\n\n# Runs all\n# go run -tags=project ./project all\n```\n\nI'd personally recommend you to have a short alias, so that rerunning it becomes very easy:\n\n```\nalias project='go run -tags=project ./project'\n\n# Runs go build\nproject build\n\n# Runs go test\nproject test\n\n@ Runs test and build\nproject \u003c\u003cEOS\ntest\nbuild\nEOS\n\n# Runs all\nproject all\n```\n\nAn extra care needs to be taken if you want to run it interactively while using a Go build tag.\n\nAs [Go has no way or plan to expose the build tag at runtime](https://github.com/golang/go/issues/7007#issuecomment-66089610), you need to use another way,\na dedicated environment variable implemented by `gosh`, to tell it to use the build tag for hot-reloading.\n\nOtherwise, you get an go-build error like `package github.com/mumoshu/gosh/project: build constraints exclude all Go files in /home/mumoshu/p/gosh/project`.\n\nThe environment variable is `GOSH_BUILD_TAG`, and you should set it like:\n\n```\nGOSH_BUILD_TAG=project project\n\n# Or include it in the alias...\n\nalias project='GOSH_BUILD_TAG=project go run -tags=project ./project'\n\nproject\n```\n\n## Go interoperability\n\n`gosh` has a rich set of functionalities to make writing a Go function a.k.a custom shell function a breeze.\n\n- [Automatic Arguments](#automatic-arguments)\n- [Automatic Flags](#automatic-flags)\n\n### Automatic Arguments\n\nThis feature makes it easy to define positional arguments to your custom function. See your [args example](./args_test.go).\n\nTo begin with, let me revisit what we've defined in our early example- a single function that takes only one string parameter `target`:\n\n```\nsh.Export(\"hello\", func(ctx gosh.Context, target string) {\n    ctx.Stdout().Write([]byte(\"hello \" + target + \"\\n\"))\n})\n```\n\nAlthough a shell function's argument should be a string in general, this doesn't mean you need to manually parse it into a more useful type that aligns with your goal.\n\nJust use other supported types in the parameters as listed below. `gosh` automatically parses the argument into the type you've specified.\n\n- `int`\n- `bool`\n- `string`\n- `[]string`\n- `...string`\n- struct ([Automatic Flags](#automatic-flags))\n\n`[]string`, `...string`, and struct can be only positined last.\n\n`[]string` and `...string` is automatically captures all the remaining arguments from the index of the parameter.\n\nThat is, both `func Join(delim string, elems []string)` and `func Join(delim string, elems []string)` converts a `join , foo bar` call into `Join(\",\", []string{\"foo\", \"bar\"})` and `Join(\",\", \"foo\", \"bar\")` respectively.\n\nA struct doesn't usually map one-to-one to a string. So we treat it as a set of options that can be constructed from a list of zero or more flags. Please refer to [Automatic Flags](#automatic-flags) for more information about how flags are converted to a struct.\n\n### Automatic Flags\n\nThis feature makes it easy to define flags like `-foo=bar` for your custom function. See our [flags example](./flags_test.go).\n\nThe gist of the feature is that you can write a standard function that accepts all the optional parameters as a Go struct, like:\n\n```go\ntype Opts struct {\n\tUpperCase bool `flag:\"upper-case\"`\n}\n\nfunc Hello(ctx gosh.Context, a string, opts Opts) {\n\ta = \"hello \" + a\n\tif opts.UpperCase {\n\t\ta = strings.ToUpper(a)\n\t}\n\tfmt.Fprintf(ctx.Stdout(), \"%s\\n\", a)\n}\n```\n\nAs this is a standard Go function, you can write some Go to call it like:\n\n```go\nHello(ctx, \"world\", Opts{UpperCase: true})\n//=\u003e HELLO WORLD\n```\n\nNow, you'd export this to the `gosh`-powered shell by using `Export` as usual:\n\n```\nsh.Export(Hello)\n```\n\nThis makes it available to the custom shell from both the Go side and the shell side.\nThat is, you can call it from Go using `Run`:\n\n```go\nsh.Run(\"hello\", \"world\", Opts{UppserCase: true})\n```\n\nwhile you can call it from shell using the automatically defined flags:\n\n```\nhello world -upper-case=true\n```\n\nFor compatibility reason, you can actually use a more shell-like syntax when you call it from Go:\n\n```go\nsh.Run(\"hello\", \"world\", \"-upper-case=true\")\n```\n\nThis magic is driven by you define a struct tag. In the original example, you've seen in the struct:\n\n```go\nUpperCase bool `flag:\"upper-case\"`\n```\n\nThis reads as `the struct field \"UpperCase\" has a tag named \"flag\" whose value is set to \"upper-case\"`.\n\n`gosh` reads the field along with its tag to use it when you provided some flag-like strings in a function argument where the function parameter expected a struct value.\n\nThis way, you don't need to write a length switch-case or call many Go's `flag` functions or deal with `FlagSet` yourself. `gosh` does it all for you.\n\n## Diagnostic Logging\n\nIn case you aren't sure why your custom shell functions and the whole application doesn't work,\ntry reading diagnostic logs that contains various debugging information from `gosh`.\n\nTo access the diagnostic logs, use the file descriptor `3` to redirect it to an arbitrary destination:\n\n```\n$ go run ./examples/getting-started hello world 3\u003ediags.out\n\n$ cat diags.out\n2021-05-29T05:59:38Z    app.go:466      registering func hello\n```\n\nYou can also emit your own diagnostic logs from your custom functions, standard shell functions, shell snippets, and even another application written in a totally different progmming language.\n\nIf you want to write a diagnostic log message from a custom function written in Go, use the `gosh.Shell.Diagf` function:\n\n```\n$ code ./examples/getting-started/main.go\n```\n\n```go\nsh.Export(\"hello\", func(ctx gosh.Context, target string) {\n    // Add the below Diagf call\n    sh.Diagf(\"My own debug message someData=%s someNumber=%d\", \"foobar\", 123)\n\n    ctx.Stdout().Write([]byte(\"hello \" + target + \"\\n\"))\n})\n```\n\n```\n$ go run ./examples/getting-started hello world 3\u003ediags.out\n\n$ cat diags.out \n2021-05-29T06:03:58Z    app.go:466      registering func hello\n2021-05-29T06:03:58Z    main.go:13      My own debug message someData=foobar someNumber=123\n```\n\nIt also works with a script, without any surprise:\n\n```\n$ go run ./examples/getting-started \u003c\u003cEOS 3\u003ediags.out\nhello world\nhello world\nEOS\n\n$ cat diags.out \n2021/05/29 06:16:57 \u003cnil\u003e\n2021-05-29T06:16:54Z    app.go:466      registering func hello\n2021-05-29T06:16:54Z    app.go:466      registering func hello\n2021-05-29T06:16:54Z    app.go:466      registering func hello\n2021-05-29T06:16:54Z    main.go:13      My own debug message someData=foobar someNumber=123\n2021-05-29T06:16:55Z    app.go:466      registering func hello\n2021-05-29T06:16:55Z    app.go:466      registering func hello\n2021-05-29T06:16:55Z    main.go:13      My own debug message someData=foobar someNumber=123\n```\n\nTo write a log message from a shell script, just write to fd 3 using a standard shell syntax.\n\nIn Bash, you use `\u003e\u00263` like:\n\n```bash\necho my own debug message \u003e\u00263\n```\n\nTo test, you can e.g. Bash [`Here Strings`](https://www.gnu.org/software/bash/manual/html_node/Redirections.html#Here-Strings):\n\n```\n$ go run ./examples/getting-started \u003c\u003cEOS 3\u003ediags.out\necho my own debug message \u003e\u00263\nEOS\n\n$ cat diags.out \n2021-05-29T06:08:19Z    app.go:466      registering func hello\n2021-05-29T06:08:19Z    app.go:466      registering func hello\nmy own debug message\n```\n\nFor a bash function, it is as easy as...:\n\n```\n$ go run ./examples/getting-started \u003c\u003cEOS 3\u003ediags.out\nmyfunc() {\n  echo my own debug message from myfunc \u003e\u00263\n}\n\nmyfunc\nEOS\n\n$ cat diags.out \n2021-05-29T06:14:09Z    app.go:466      registering func hello\n2021-05-29T06:14:09Z    app.go:466      registering func hello\nmy own debug message from myfunc\n```\n\n## `go test` Integration\n\nSee the [gotest example](./examples/gotest/gotest_test.go) for how to write unit and integration tests against your `gosh` applications, shell scripts, or even a regular command that isn't implemented using `gosh`.\n\nThe below is the most standard structure of an unit test for your `gosh` application:\n\n```go\nfunc TestUnit(t *testing.T) {\n\tgotest := gotest.New()\n\n\tvar stdout bytes.Buffer\n\n\tif err := gotest.Run(\"hello\", \"world\", gosh.WriteStdout(\u0026stdout)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tassert.Equal(t, \"hello world\\n\", stdout.String())\n}\n```\n\nIn the above example, `gotest.New` is implemented by you to provide an instance of `*gosh.Shell`. You write tests against it by calling the `Run` function, using some helper like `gosh.WriteStdout` to capture what's written to the standard output by your application.\n\nIf you are curious how you would implement `gotest.New`, read on.\n\n### Structuring your gosh application for ease of testing\n\nA recommended approach to structure your `gosh` application is to put everything except the entrypoint to your application to a dedicated package.\n\nIn the `gotest` example, we have two packages:\n\n- `gotest/cmd` that contains the `main` package\n- `gotest` for everything else\n\nThe only source file that exists in the first package is `gotest/cmd/main.go`.\n\nBasically, It contains only a call to the second package:\n\n```go\npackage main\n\nfunc main() {\n\tgotest.MustExec(os.Args)\n}\n```\n\nThe `gotest.MustExec` call refers to the `MustExec` function defined in the second package.\n\nIt looks like:\n\n```go\npackage gotest\n\nfunc MustExec(osArgs []string) {\n\tNew().MustExec(osArgs)\n}\n```\n\n`New` is the function that initializes the instance of your `gosh` application:\n\n```go\npackage gotest\n\nfunc New() *gosh.Shell {\n\tsh := \u0026gosh.Shell{}\n\n\tsh.Export(\"hello\", func(ctx gosh.Context, target string) {\n\t\tctx.Stdout().Write([]byte(\"hello \" + target + \"\\n\"))\n\t})\n\n\treturn sh\n}\n```\n\nThis way, you can just call `New` to create an instance of your gosh application for testing, and then call `Run` on the application in the test, as you would do while writing the application itself.\n\n```go\npackage gotest_test\n\nfunc TestUnit(t *testing.T) {\n\tgotest := gotest.New()\n\n\tvar stdout bytes.Buffer\n\n\tif err := gotest.Run(\"hello\", \"world\", gosh.WriteStdout(\u0026stdout)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tassert.Equal(t, \"hello world\\n\", stdout.String())\n}\n```\n\n## Ginkgo Integration\n\nIf you find yourself repeating a lot of \"test setup\" code in your tests or have hard time structuring your tests against a lot of cases, you might find [Ginkgo](https://onsi.github.io/ginkgo/) helpful.\n\n\u003e Ginkgo is a Go testing framework built to help you efficiently write expressive and comprehensive tests using Behavior-Driven Development (“BDD”) style\n\u003e https://onsi.github.io/ginkgo/\n\nSee the [ginkgotest example](./examples/ginkgotest/ginkgotest_test.go) for how to write integration and End-to-End tests against your `gosh` applications, shell scripts, or even a regular command that isn't implemented using `gosh`.\n\nThe below is the most standard structure of an Ginkgo test for your `gosh` application:\n\n```go\nvar app *gosh.Shell\n\nfunc TestAcc(t *testing.T) {\n\tapp = ginkgotest.New()\n\n\tgoshtest.Run(t, app, func() {\n\t\tRegisterFailHandler(Fail)\n\t\tRunSpecs(t, \"Your App's Suite\")\n\t})\n}\n\nvar _ = Describe(\"Your App\", func() {\n\tvar (\n\t\tconfig struct {\n\t\t\tcmd  string\n\t\t\targs []interface{}\n\t\t}\n\n\t\terr    error\n\t\tstdout string\n\t)\n\n\tJustBeforeEach(func() {\n\t\tvar stdoutBuf bytes.Buffer\n\n\t\tvar args []interface{}\n\n\t\targs = append(args, config.cmd)\n\t\targs = append(args, config.args...)\n\t\targs = append(args, gosh.WriteStdout(\u0026stdoutBuf))\n\n\t\terr = app.Run(args...)\n\n\t\tstdout = stdoutBuf.String()\n\t})\n\n\tDescribe(\"hello\", func() {\n\t\tBeforeEach(func() {\n\t\t\tconfig.cmd = \"hello\"\n\t\t})\n\n\t\tContext(\"world\", func() {\n\t\t\tBeforeEach(func() {\n\t\t\t\tconfig.args = []interface{}{\"world\"}\n\t\t\t})\n\n\t\t\tIt(\"should output \\\"hello world\\\"\", func() {\n\t\t\t\tExpect(stdout).To(Equal(\"hello world\\n\"))\n\t\t\t})\n\t\t})\n    })\n})\n```\n\nIn the above example, `gotest.New` is implemented by you to provide an instance of `*gosh.Shell`. You write tests against it by calling the `Run` function, using some helper like `gosh.WriteStdout` to capture what's written to the standard output by your application.\n\nIf you are curious how you would implement `gotest.New`, read [Structuring your gosh application for ease of testing](#structuring-your-gosh-application-for-ease-of-testing).\n\nThe followings are standard functions provided by Ginkgo:\n\n- RegisterFailHandler\n- RunSpecs\n- Describe\n- JustBeforeEach / BeforeEach\n- It\n\nThe followings are standard functions provided by Gomega, which is Ginkgo's preferred test helpers and matchers library.\n\n- Expect\n- Equal\n\nPlease refer to [Ginkgo's official documentation](https://onsi.github.io/ginkgo/) for knowing what each Ginkgo and Gomega functions mean and how to write Ginkgo test scenarios.\n\n## Writinng End-to-End test\n\nAs stated in the very beginning of this documentation, one of primary goals of `gosh` is to help writing maintainable End-to-End tests.\n\n\u003e That use-case is driven by all the features explained so far. So please read the above guides beforehand, or go back to read any of those when you had hard time understanding what's explained in this section.\n\n\n\n# Acknowledgements\n\n`gosh` has been inspired by numerous open-source projects listed below.\n\nMuch appreciation to the authors and the open-source community!\n\nTask runners:\n\n- https://github.com/magefile\n\n*unix pipeline-like things:\n\n- https://github.com/b4b4r07/go-pipe\n- https://github.com/go-pipe/pipe\n- https://github.com/urjitbhatia/gopipe\n- https://github.com/mattn/go-pipeline\n\nMisc:\n\n- https://github.com/taylorflatt/remote-shell\n- https://github.com/hashicorp/go-plugin\n- https://github.com/a8m/reflect-examples\n- https://medium.com/swlh/effective-ginkgo-gomega-b6c28d476a09\n- https://medium.com/@william.la.martin/ginkgotchas-yeh-also-gomega-13e39185ec96\n- https://github.com/fsnotify/fsnotify\n\nFD handling:\n\n- https://gist.github.com/miguelmota/4ac6c2c127b6853593808d9d3bba067f\n- https://stackoverflow.com/questions/7082001/how-do-file-descriptors-work\n\nShell scripting tips:\n\n- https://www.cyberciti.biz/faq/bash-for-loop/\n- https://www.gnu.org/software/bash/manual/html_node/Redirections.html#Here-Strings\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmumoshu%2Fgosh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmumoshu%2Fgosh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmumoshu%2Fgosh/lists"}