{"id":13411177,"url":"https://github.com/Yiling-J/piper","last_synced_at":"2025-03-14T16:34:06.958Z","repository":{"id":57638197,"uuid":"429101439","full_name":"Yiling-J/piper","owner":"Yiling-J","description":"🛠 Viper wrapper with config inheritance and key generation","archived":false,"fork":false,"pushed_at":"2021-12-03T04:07:15.000Z","size":78,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-07-31T20:45:06.416Z","etag":null,"topics":[],"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/Yiling-J.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-11-17T15:32:19.000Z","updated_at":"2022-09-26T09:21:55.000Z","dependencies_parsed_at":"2022-09-26T20:21:12.611Z","dependency_job_id":null,"html_url":"https://github.com/Yiling-J/piper","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiling-J%2Fpiper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiling-J%2Fpiper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiling-J%2Fpiper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yiling-J%2Fpiper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Yiling-J","download_url":"https://codeload.github.com/Yiling-J/piper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221486919,"owners_count":16830966,"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":[],"created_at":"2024-07-30T20:01:11.956Z","updated_at":"2024-10-26T02:30:49.018Z","avatar_url":"https://github.com/Yiling-J.png","language":"Go","readme":"# piper - Simple Wrapper For Viper\n![example workflow](https://github.com/Yiling-J/piper/actions/workflows/go.yml/badge.svg)\n![Go Report Card](https://goreportcard.com/badge/github.com/yiling-j/piper?style=flat-square)\n\n- **Single Source of Truth**\n- **Generated Key Structs, No Typo**\n- **Config Inheritance**\n- **Multiple Config Strategies Support**\n- **Cache For Better Performance**\n\n## Why Piper\nIf you are familiar with Django, this is how Django settings module looks like:\n\n```console {12-20}\n└── settings\n    ├── base.py\n    ├── dev.py\n    ├── stage.py\n    └── prod.py\n```\n`dev.py` will inherit from `base.py`, also `stage.py` will inherit from `dev.py`.\n\nStart Django with selected setting:\n```shell\nexport DJANGO_SETTINGS_MODULE=mysite.settings.prod\ndjango-admin runserver\n```\n\nAnd this is how you access settings in Django:\n\n```python\nfrom django.conf import settings\n\nauthor = settings.Author\n```\n\nI want to have similar experience with Viper, so here comes this wrapper:\n\n```console {12-20}\n└── config\n    ├── base.toml\n    ├── dev.toml\n    ├── stage.toml\n    └── prod.toml\n```\nThis is how you access config using piper:\n\n```go\nimport \"your_project/config\"\n\nfunc main() {\n\tpiper.Load(\"config/stage.toml\")\n\tauthor = piper.GetString(config.Author)\n}\n```\n\nCheck example folder for more details.\n\n## Installation\n```shell\ngo get github.com/Yiling-J/piper/cmd\n```\n## Add Config Files\nAdd your config files to your config folder, usually your config folder should be under project root folder.\n\ntoml example, you can also use yaml or json.\n```console {12-20}\nproject\n└── config\n    ├── base.toml\n    ├── dev.toml\n    ├── stage.toml\n    └── prod.toml\n\n```\nTo support inheritance, you need to add a special key to your config file called `pp_imports`\n```\npp_imports = [\"base.toml\", \"dev.toml\"]\n```\nPiper will resolve that automatically. Also order matters here, this line means import `base.toml` first, then import `dev.toml` and merge.\nAfter all `pp_imports` merged, import current file.\n\n## Config Key Generation\nRun code generation from the root directory of the project as follows:\n```shell\ngo run github.com/Yiling-J/piper/cmd your_config_folder\n```\nIn this step piper will load all files in your config folder and merge them together.\nThen piper will generate `config.go` under your config folder, include all your config keys.\nAlso you will see the config structure when piper generating code.\n\nAfter code genertation, your config folder should look like:\n```console {12-20}\n└── config\n    ├── base.toml\n    ├── dev.toml\n    ├── stage.toml\n    ├── prod.toml\n    └── config.go\n```\n## Use Piper\n\n### Strategy I - Embed\nembed your config folder into your code, single executable when you deploy.\n```go\nimport (\n\t\"github.com/Yiling-J/piper\"\n\t\"your_project/example/config\"\n)\n\n//go:embed config/*\nvar configFS embed.FS\n\npiper.SetFS(configFS)\npiper.Load(\"config/stage.toml\")\nauthor := piper.GetString(config.Author)\n```\n\n### Strategy II - Embed with Env\nembed your config folder into your code, single executable when you deploy, and replace secret with env.\n```go\nimport (\n\t\"github.com/Yiling-J/piper\"\n\t\"your_project/example/config\"\n)\n\n//go:embed config/*\nvar configFS embed.FS\n\nos.Setenv(\"SECRET\", \"qux\")\npiper.SetFS(configFS)\n// make sure turn on AutomaticEnv first then loading config,\n// this way the env vaiable is also cached, so IGet* methods can work properly\npiper.V().AutomaticEnv()\npiper.Load(\"config/stage.toml\")\nsecret := piper.GetString(config.Secret)\n\n```\n\n### Strategy III -  Copy config directory\ncopy config folder when building docker image, so the true config folder exists.\n```go\nimport (\n\t\"github.com/Yiling-J/piper\"\n\t\"your_project/example/config\"\n)\n\npiper.Load(\"config/stage.toml\")\nauthor := piper.GetString(config.Author)\n```\n\n### Strategy IV - Mix embed and copy directory\nEmbed your config folder, but keep some secret keys in a real config file.\n```go\nimport (\n\t\"github.com/Yiling-J/piper\"\n\t\"your_project/example/config\"\n)\n\n//go:embed config/*\nvar configFS embed.FS\n\n// \"config/stage_with_secret.toml\" is not in source code,\n// may come from docker build or k8s ConfigMap\npiper.SetFS(configFS)\npiper.Load(\"config/stage_with_secret.toml\")\nauthor := piper.GetString(config.Author)\n```\nTrue directory will take precedence over embeded directory.\n\n## Access Viper\nPiper is just a wrapper, so you can always get the wrapped viper instance:\n```go\nv := piper.V()\n```\nBe careful when using viper directly to load config, piper may not work properly.\n\n## Piper or Pipers?\n\nPiper comes ready to use out of the box. There is no configuration or\ninitialization needed to begin using Piper. Since most applications will want\nto use a single central repository for their configuration, the piper package\nprovides this. It is similar to a singleton.\n\nIn all of the examples above, they demonstrate using piper in its singleton\nstyle approach.\n\n### Working with multiple pipers\n\nYou can also create many different pipers for use in your application. Each will\nhave its own unique set of configurations and values. Each can read from a\ndifferent config file. All of the functions that piper\npackage supports are mirrored as methods on a piper.\n\nExample:\n\n```go\nx := piper.New()\ny := piper.New()\n\nx.Load(\"config/x.toml\")\ny.Load(\"config/y.toml\")\n\n// access viper\n// p := x.V\n```\n\nWhen working with multiple pipers, it is up to the user to keep track of the\ndifferent pipers.\n\n## Performance\nSometimes you may find viper a little slow, because viper need to check in the following order on `Get`: flag, env, config file, key/value store.\nIf you have confidence some of your configs won't change, you can use piper's `IGet*` methods. Piper will build a configs cache on `Load`,\nand those `IGet*` methods will get config from cache directly.\n```shell\ngoos: darwin\ngoarch: amd64\npkg: github.com/Yiling-J/piper/integration\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\n\nBenchmarkGet-12           895533              1278 ns/op\nBenchmarkIGet-12        19141876                61.35 ns/op\n```\n","funding_links":[],"categories":["Configuration","配置","Uncategorized"],"sub_categories":["Advanced Console UIs","Standard CLI","标准CLI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYiling-J%2Fpiper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FYiling-J%2Fpiper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYiling-J%2Fpiper/lists"}