{"id":13411063,"url":"https://github.com/beatlabs/harvester","last_synced_at":"2025-09-08T13:07:58.563Z","repository":{"id":35019995,"uuid":"180311582","full_name":"beatlabs/harvester","owner":"beatlabs","description":"Harvest configuration, watch and notify subscriber","archived":false,"fork":false,"pushed_at":"2025-08-31T15:29:41.000Z","size":6157,"stargazers_count":134,"open_issues_count":1,"forks_count":28,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-08-31T17:29:05.616Z","etag":null,"topics":["configuration-management","go","golang"],"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/beatlabs.png","metadata":{"files":{"readme":"README.md","changelog":"change/change.go","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-04-09T07:37:19.000Z","updated_at":"2025-08-31T15:29:43.000Z","dependencies_parsed_at":"2023-12-02T14:24:22.400Z","dependency_job_id":"3430afe2-e1f1-4a11-95ba-8b065899b225","html_url":"https://github.com/beatlabs/harvester","commit_stats":null,"previous_names":["taxibeat/harvester"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/beatlabs/harvester","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatlabs%2Fharvester","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatlabs%2Fharvester/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatlabs%2Fharvester/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatlabs%2Fharvester/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beatlabs","download_url":"https://codeload.github.com/beatlabs/harvester/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatlabs%2Fharvester/sbom","scorecard":{"id":229684,"data":{"date":"2025-08-11","repo":{"name":"github.com/beatlabs/harvester","commit":"4482a1df8299408c8b672c878cf51902b44738e8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"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":"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 0/14 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":"Maintained","score":2,"reason":"3 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml: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":"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":"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":"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":"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":"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/go.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/beatlabs/harvester/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/beatlabs/harvester/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/beatlabs/harvester/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/beatlabs/harvester/go.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/go.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/beatlabs/harvester/go.yml/master?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction 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":"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"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"}},{"name":"SAST","score":4,"reason":"SAST tool is not run on all commits -- score normalized to 4","details":["Warn: 11 commits out of 24 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"}}]},"last_synced_at":"2025-08-17T04:37:35.512Z","repository_id":35019995,"created_at":"2025-08-17T04:37:35.512Z","updated_at":"2025-08-17T04:37:35.512Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274189065,"owners_count":25237872,"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","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["configuration-management","go","golang"],"created_at":"2024-07-30T20:01:11.167Z","updated_at":"2025-09-08T13:07:58.537Z","avatar_url":"https://github.com/beatlabs.png","language":"Go","funding_links":[],"categories":["Configuration","Go","配置","Uncategorized","配置管理 `配置解析库`","配置管理"],"sub_categories":["Standard CLI","Advanced Console UIs","标准CLI","标准 CLI"],"readme":"# Harvester ![Running CI](https://github.com/beatlabs/harvester/workflows/Running%20CI/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/beatlabs/harvester/badge.svg?branch=master)](https://coveralls.io/github/beatlabs/harvester?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/beatlabs/harvester)](https://goreportcard.com/report/github.com/beatlabs/harvester) [![GoDoc](https://godoc.org/github.com/beatlabs/harvester?status.svg)](https://godoc.org/github.com/beatlabs/harvester) ![GitHub release](https://img.shields.io/github/release/beatlabs/harvester.svg)[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbeatlabs%2Fharvester.svg?type=shield\u0026issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbeatlabs%2Fharvester?ref=badge_shield\u0026issueType=license)\n\n`Harvester` is a configuration library which helps setting up and monitoring configuration values in order to dynamically\nreconfigure your application.\n\nConfiguration can be obtained from the following sources:\n\n- Seed values, are hard-coded values into your configuration struct\n- Environment values, are obtained from the environment\n- Flag values, are obtained from CLI flags with the form `-flag=value`\n- File internals in local storage. Only text files are supported, don't use it for binary.\n- Consul, which is used to get initial values and to monitor them for changes\n\nThe order is applied as it is listed above. Consul seeder and monitor are optional and will be used only if `Harvester` is created with the above components.\n\n`Harvester` expects a go structure with tags which defines one or more of the above like the following:\n\n```go\ntype Config struct {\n    IndexName      sync.String          `seed:\"customers-v1\"`\n    CacheRetention sync.Int64           `seed:\"86400\" env:\"ENV_CACHE_RETENTION_SECONDS\"`\n    LogLevel       sync.String          `seed:\"DEBUG\" flag:\"loglevel\"`\n    Signature      sync.String          `file:\"signature.txt\"`\n    Sandbox        sync.Bool            `seed:\"true\" env:\"ENV_SANDBOX\" consul:\"/config/sandbox-mode\"`\n    AccessToken    sync.Secret          `seed:\"defaultaccesstoken\" env:\"ENV_ACCESS_TOKEN\" consul:\"/config/access-token\"`\n    WorkDuration   sync.TimeDuration    `seed:\"1s\" env:\"ENV_WORK_DURATION\" consul:\"/config/work-duration\"`\n    OpeningBalance sync.Float64         `seed:\"0.0\" env:\"ENV_OPENING_BALANCE\" redis:\"opening-balance\"`\n}\n```\n\nThe above defines the following fields:\n\n- IndexName, which will be seeded with the value `customers-v1`\n- CacheRetention, which will be seeded with the value `18`, and if exists, overridden with whatever value the env var `ENV_CACHE_RETENTION_SECONDS` holds\n- LogLevel, which will be seeded with the value `DEBUG`, and if exists, overridden with whatever value the flag `loglevel` holds\n- Sandbox, which will be seeded with the value `true`, and if exists, overridden with whatever value the env var `ENV_SANDBOX` holds and then from Consul if the consul seeder and/or watcher are provided.\n- WorkDuration, which will be seeded with the value `1s`, and if exists, overridden with whatever value the env var `ENV_WORK_DURATION` holds and then from Consul if the consul seeder and/or watcher are provided.\n- OpeningBalance, which will be seeded with the value `0.0`, and if exists, overridden with whatever value the env var `ENV_OPENING_BALANCE` holds and then from Redis if the redis seeder and/or watcher are provided.\n\nThe fields have to be one of the types that the sync package supports in order to allow concurrent read and write to the fields. The following types are supported:\n\n- sync.String, allows for concurrent string manipulation\n- sync.Int64, allows for concurrent int64 manipulation\n- sync.Float64, allows for concurrent float64 manipulation\n- sync.Bool, allows for concurrent bool manipulation\n- sync.Secret, allows for concurrent secret manipulation. Secrets can only be strings\n- sync.TimeDuration, allows for concurrent time.duration manipulation.\n- sync.Regexp, allows for concurrent *regexp.Regexp manipulation.\n- sync.StringMap, allows for concurrent map[string]string manipulation.\n- sync.StringSlice, allows for concurrent []string manipulation.\n\nFor sensitive configuration (passwords, tokens, etc.) that shouldn't be printed in log, you can use the `Secret` flavor of `sync` types. If one of these is selected, then at harvester log instead of the real value the text `***` will be displayed.\n\n`Harvester` has a seeding phase and an optional monitoring phase.\n\n## Seeding phase\n  \n- Apply the seed tag value, if present\n- Apply the value contained in the env var, if present\n- Apply the value contained in the file, if present\n- Apply the value returned from Consul, if present and harvester is setup to seed from consul\n- Apply the value contained in the CLI flags, if present\n\nConditions where seeding fails:\n\n- If at the end of the seeding phase one or more fields have not been seeded\n- If the seed value is invalid\n\n### Seeder\n\n`Harvester` allows the creation of custom getters which are used by the seeder and implement the following interface:\n\n```go\ntype Getter interface {\n    Get(key string) (string, error)\n}\n```\n\nSeed and env tags are supported by default, the Consul getter has to be setup when creating a `Harvester` with the builder.\n\n## Monitoring phase (Consul only)\n  \n- Monitor a key and apply if tag key matches (Consul and Redis)\n- Monitor a key-prefix and apply if tag key matches (Consul only)\n\n### Monitor\n\n`Harvester` allows for dynamically changing the config value by monitoring a source. The following sources are available:\n\n- Consul, which supports monitoring for keys and key-prefixes.\n\nThis feature have to be setup when creating a `Harvester` with the builder.\n\n## Builder\n\nThe `Harvester` builder pattern is used to create a `Harvester` instance. The builder supports setting up:\n\n- Consul seed, for setting up seeding from Consul\n- Consul monitor, for setting up monitoring from Consul\n- Redis seed, for setting up seeding from Redis\n- Redis monitor, for setting up monitoring from Redis\n\n```go\n     h, err := harvester.New(\u0026cfg, chNotify,\n        harvester.WithConsulSeed(consulAddress, consulDC, consulToken, 0),\n        harvester.WithConsulMonitor(consulAddress, consulDC, consulToken, 0),\n        harvester.WithRedisSeed(redisClient),\n        harvester.WithRedisMonitor(redisClient, 200*time.Millisecond),\n    )    \n```\n\nThe above snippet set's up a `Harvester` instance with Consul and Redis seed and monitor.\n\n## Consul\n\nConsul has support for versioning (`ModifyIndex`) which allows us to change the value only if the version is higher than the one currently.\n\n## Examples\n\nHead over to [examples](examples) readme on how to use harvester\n\n## How to Contribute\n\nSee [Contribution Guidelines](CONTRIBUTE.md).\n\n## Code of conduct\n\nPlease note that this project is released with a [Contributor Code of Conduct](https://www.contributor-covenant.org/adopters). By participating in this project and its community you agree to abide by those terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeatlabs%2Fharvester","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeatlabs%2Fharvester","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeatlabs%2Fharvester/lists"}