{"id":40754948,"url":"https://github.com/effxhq/go-lifecycle","last_synced_at":"2026-01-21T16:09:01.786Z","repository":{"id":57591344,"uuid":"368612842","full_name":"effxhq/go-lifecycle","owner":"effxhq","description":"A state-based application lifecycle library for go apps.","archived":false,"fork":false,"pushed_at":"2021-06-02T20:52:04.000Z","size":42,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-06-21T14:12:45.581Z","etag":null,"topics":["app-lifecycle","development","go","golang","microservices"],"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/effxhq.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-18T17:22:53.000Z","updated_at":"2024-01-02T19:31:21.000Z","dependencies_parsed_at":"2022-09-26T19:43:14.842Z","dependency_job_id":null,"html_url":"https://github.com/effxhq/go-lifecycle","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/effxhq/go-lifecycle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effxhq%2Fgo-lifecycle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effxhq%2Fgo-lifecycle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effxhq%2Fgo-lifecycle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effxhq%2Fgo-lifecycle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/effxhq","download_url":"https://codeload.github.com/effxhq/go-lifecycle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effxhq%2Fgo-lifecycle/sbom","scorecard":{"id":368327,"data":{"date":"2025-08-11","repo":{"name":"github.com/effxhq/go-lifecycle","commit":"049a35adc36f95d25d784353e94c26b177cbbbb4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/branch.yaml:1","Warn: no topLevel permission defined: .github/workflows/tag.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/branch.yaml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/effxhq/go-lifecycle/branch.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/branch.yaml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/effxhq/go-lifecycle/branch.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/branch.yaml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/effxhq/go-lifecycle/branch.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/branch.yaml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/effxhq/go-lifecycle/branch.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tag.yaml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/effxhq/go-lifecycle/tag.yaml/main?enable=pin","Warn: goCommand not pinned by hash: .github/workflows/tag.yaml:23","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of   1 goCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T12:24:02.859Z","repository_id":57591344,"created_at":"2025-08-18T12:24:02.859Z","updated_at":"2025-08-18T12:24:02.859Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28635927,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T15:01:31.228Z","status":"ssl_error","status_checked_at":"2026-01-21T14:42:58.942Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["app-lifecycle","development","go","golang","microservices"],"created_at":"2026-01-21T16:09:00.949Z","updated_at":"2026-01-21T16:09:01.780Z","avatar_url":"https://github.com/effxhq.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-lifecycle\n\n[![GitHub Workflow Status](https://github.com/effxhq/go-lifecycle/workflows/branch/badge.svg)](https://github.com/effxhq/go-lifecycle/actions/workflows/branch.yaml)\n[![GoDev](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white\u0026style=flat-square)](https://pkg.go.dev/github.com/effxhq/go-lifecycle?tab=doc)\n[![CodeCov](https://codecov.io/gh/effxhq/go-lifecycle/branch/main/graph/badge.svg)](https://codecov.io/gh/effxhq/go-lifecycle)\n[![Go Report Card](https://goreportcard.com/badge/effxhq/go-lifecycle)](https://goreportcard.com/report/effxhq/go-lifecycle)\n\nA state-based application lifecycle library for go. `go-lifecycle` helps manage the complexity around the\ninitialization, startup, and shutdown of applications. It abstracts away the need to manage any lifecycle hooks and\nprovides app devs with a plugin based interface. It also helps ensure that plugins are properly started and shutdown in\nthe event of an error.\n\n**Influences**\n\n* [go-kit](https://github.com/go-kit/kit)\n\n## Usage\n\nHere's an example of how the lifecycle library is used at effx. It's important to note that we haven't released any of\nour plugins as they are very company specific. You will need to write your own plugins. Should enough interest develop,\nwe would be happy to work with the community to develop some common ones.\n\n```go\npackage main\n\nimport (\n\t\"github.com/effxhq/go-lifecycle\"\n)\n\nfunc main() {\n\tapp := new(lifecycle.Application)\n\n\t// add plugins to the application\n\tapp.Initialize(\n\t\thttp_plugin.ServerPlugin(),\n\t\tgrpc_plugin.ServerPlugin(),\n\t\tgrpc_plugin.ClientPlugin(\"target\"),\n\t)\n\n\t// do one of these\n\tapp.Start() // runs application as a long running agent\n\tapp.Run()   // runs application as a short lived job\n}\n```\n\n### Passing resources through app.Context()\n\nPlugins are free to decorate the application with resources. This allows plugins to expose pre-configured resources to\napp developers. This is often useful for common boilerplate like setting up an HTTP or gRPC server.\n\n```go\napp.WithValue(lifecycle.ContextKey(\"grpc.server\"), grpcServer)\n```\n\nFor retrieval, plugins should provide helper functions for obtaining their resources from the app.\n\n```go\ngrpcServer := grpc_plugin.ServerFromContext(app.Context())\n// add grpc services\n\ntargetClientConn := grpc_plugin.ClientFromContext(app.Context(), \"target\")\n// create clients\n```\n\n### Handling configuration\n\nThis system is configuration agnostic. Your organization is free to choose its own configuration language. We largely\nuse environment variables which makes setup rather easy.\n\n## Plugin Development\n\nBefore diving into writing your own plugin, it is useful to first understand the `Application` state machine. It can\nexist in one of the following states:\n\n1. **Initialization** - The application is idle. Developers are free to install and configure plugins as they need.\n   Should the application encounter any errors, all registered plugins are shutdown.\n\n1. **Running** - The application runs each plugins `Run` step (if it has one). Should the application encounter any\n   errors, all plugins are shutdown. Particularly useful for running database migrations, one off jobs, or crons.\n\n1. **Started** - The application runs each plugins `Start` step (if it has one). Should the application encounter any\n   errors when starting, all plugins are shutdown. Once all plugins have been started, the main thread blocks and waits\n   for shut down.\n\n1. **Shutdown** - Triggered one of three ways. The first two deal with the prior two states. Should an application\n   encounter any errors when running or starting up, they trigger a shutdown. The last way an application can be\n   triggers is by sending either a `SIGTERM` or `SIGINT` signal. Once shutdown, the application runs each\n   plugins `Shutdown` step.\n\n1. **Terminated** - Once all plugins have been shutdown, the application goes into a terminated state. This happens just\n   prior to system exist. If an error occurred, the system will exit with an unhealthy status code. If there were no\n   errors, then system exists cleanly.\n\nThe diagram below shows how transitions occur between these states.\n\n[![State Machine](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggTFJcbiAgIFxuICAgKiAtLSBJbml0aWFsaXplIC0tPiAqXG4gICAqIC0tIFN0YXJ0IC0tPiBzdGFydGVkXG4gICAqIC0tIFJ1biAtLT4gcnVubmluZ1xuICAgKiAtLSBlcnIgLS0-IHNodXRkb3duXG5cbiAgIG9zLlNJR1RFUk0gLS0-IHNodXRkb3duXG4gICBzdGFydGVkIC0tIGVyciAtLT4gc2h1dGRvd25cbiAgIHJ1bm5pbmcgLS0gZXJyPyAtLT4gc2h1dGRvd25cblxuICAgc2h1dGRvd24gLS0-IHRlcm1pbmF0ZWRcbiIsIm1lcm1haWQiOnt9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9)](https://mermaid-js.github.io/mermaid-live-editor/edit##eyJjb2RlIjoiZ3JhcGggTFJcbiAgIFxuICAgKiAtLSBJbml0aWFsaXplIC0tPiAqXG4gICAqIC0tIFN0YXJ0IC0tPiBzdGFydGVkXG4gICAqIC0tIFJ1biAtLT4gcnVubmluZ1xuICAgKiAtLSBlcnIgLS0-IHNodXRkb3duXG5cbiAgIG9zLlNJR1RFUk0gLS0-IHNodXRkb3duXG4gICBzdGFydGVkIC0tIGVyciAtLT4gc2h1dGRvd25cbiAgIG1pZ3JhdGluZyAtLSBlcnI_IC0tPiBzaHV0ZG93blxuXG4gICBzaHV0ZG93biAtLT4gdGVybWluYXRlZFxuIiwibWVybWFpZCI6Int9IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ)\n\n### Using `lifecycle.PluginFuncs`\n\nUsing the `lifecycle.PluginFuncs` object is the easiest way to develop a plugin. It allows you to build partial,\nstateless plugins rather easily. For example, the code block below shows how you can write a logger plugin.\n\n```go\npackage logger_plugin\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/effxhq/go-lifecycle\"\n)\n\nvar contextKey = lifecycle.ContextKey(\"logger\")\n\nfunc FromContext(ctx context.Context) *log.Logger {\n\tval := ctx.Value(contextKey)\n\tif val == nil {\n\t\treturn nil // or default logger\n\t}\n\treturn val.(*log.Logger)\n}\n\nfunc Plugin() lifecycle.Plugin {\n\tlogger := log.Default()\n\n\treturn \u0026lifecycle.PluginFuncs{\n\t\tInitializeFunc: func(app *lifecycle.Application) error {\n\t\t\tapp.WithValue(contextKey, logger)\n\n\t\t\t// the hook is used to report errors encountered during lifecycle steps.\n\t\t\t// applications should only have one hook.\n\t\t\tapp.WithHook(func(phase string, err error) {\n\t\t\t\tif err != nil {\n\t\t\t\t\tlogger.Printf(\"[%s] encountered err: %v\", phase, err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn nil\n\t\t},\n\t\tRunFunc: func(app *lifecycle.Application) error {\n\t\t\tlogger.Printf(\"running application as job\")\n\t\t\treturn nil\n\t\t},\n\t\tStartFunc: func(app *lifecycle.Application) error {\n\t\t\tlogger.Printf(\"starting application as daemon\")\n\t\t\treturn nil\n\t\t},\n\t\tShutdownFunc: func(app *lifecycle.Application) error {\n\t\t\tlogger.Printf(\"shutting down\")\n\t\t\treturn nil\n\t\t},\n\t}\n}\n```\n\nFor cases where you might want to track some state, there's a `Plugin` interface that can be implemented.\n\n### Composing plugins\n\nPlugins support composition. This allows components to be bundled and installed together.\n\n```go\npackage company_plugin\n\nimport (\n\t\"github.com/effxhq/go-lifecycle\"\n)\n\nfunc DefaultCompanyPlugin() lifecycle.Plugin {\n\treturn \u0026lifecycle.PluginFuncs{\n\t\tInitializeFunc: func(app *lifecycle.Application) error {\n\t\t\tapp.Initialize(\n\t\t\t\thttp_plugin.ServerPlugin(),\n\t\t\t\tgrpc_plugin.ServerPlugin(),\n\t\t\t\tgrpc_plugin.ClientPlugin(\"target\"),\n\t\t\t)\n\t\t\treturn nil\n\t\t},\n\t}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffxhq%2Fgo-lifecycle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feffxhq%2Fgo-lifecycle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffxhq%2Fgo-lifecycle/lists"}