{"id":13411164,"url":"https://github.com/muir/nfigure","last_synced_at":"2026-01-12T12:50:24.176Z","repository":{"id":38239010,"uuid":"430295427","full_name":"muir/nfigure","owner":"muir","description":"Golang struct-tag based configfile and flag parsing","archived":false,"fork":false,"pushed_at":"2025-10-20T15:48:58.000Z","size":304,"stargazers_count":9,"open_issues_count":4,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-20T17:41:47.755Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/muir.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":".github/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":"2021-11-21T06:55:30.000Z","updated_at":"2025-10-20T15:49:01.000Z","dependencies_parsed_at":"2024-01-08T14:30:56.641Z","dependency_job_id":"d2aa6c05-3475-490f-afd3-385a8c2252cd","html_url":"https://github.com/muir/nfigure","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/muir/nfigure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muir%2Fnfigure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muir%2Fnfigure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muir%2Fnfigure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muir%2Fnfigure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/muir","download_url":"https://codeload.github.com/muir/nfigure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muir%2Fnfigure/sbom","scorecard":{"id":667633,"data":{"date":"2025-08-18","repo":{"name":"github.com/muir/nfigure","commit":"06d3de30a13d5a3d72d83b367be6bd08bc210b2b"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":4.9,"checks":[{"name":"Maintained","score":5,"reason":"6 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/9 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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:28","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:29","Info: topLevel 'contents' permission set to 'read': .github/workflows/codecov.yml:6","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:21","Info: topLevel 'contents' permission set to 'read': .github/workflows/go.yml:4","Warn: no topLevel permission defined: .github/workflows/golangci-lint.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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":6,"reason":"dependency not pinned by hash detected -- score normalized to 6","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/codecov.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/muir/nfigure/codecov.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/muir/nfigure/golangci-lint.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/muir/nfigure/golangci-lint.yml/main?enable=pin","Info:   8 out of   9 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   5 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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"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/40576783fda6698350fcbbeaea760ff827433034/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/40576783fda6698350fcbbeaea760ff827433034/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: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"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/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T18:39:56.973Z","repository_id":38239010,"created_at":"2025-08-21T18:39:56.973Z","updated_at":"2025-08-21T18:39:56.973Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":[],"created_at":"2024-07-30T20:01:11.856Z","updated_at":"2026-01-12T12:50:24.159Z","avatar_url":"https://github.com/muir.png","language":"Go","funding_links":[],"categories":["Configuration","配置"],"sub_categories":["Standard CLI","Advanced Console UIs","标准CLI"],"readme":"# nfigure - per-library configuration\n\n[![GoDoc](https://godoc.org/github.com/muir/nfigure?status.png)](https://pkg.go.dev/github.com/muir/nfigure)\n![unit tests](https://github.com/muir/nfigure/actions/workflows/go.yml/badge.svg)\n[![report card](https://goreportcard.com/badge/github.com/muir/nfigure)](https://goreportcard.com/report/github.com/muir/nfigure)\n[![codecov](https://codecov.io/gh/muir/nfigure/branch/main/graph/badge.svg)](https://codecov.io/gh/muir/nfigure)\n\nInstall:\n\n\tgo get github.com/muir/nfigure\n\n---\n\nNfigure is a reflective configuration library.  It supports:\n\n- Describing what to configure using struct tags\n- Configuration from: multiple configuration file formats, and multiple files\n- Configuration from: environment variables\n- Configuration from: the command line\n- Posix-style and Go-style command line parsing\n- Support for subcommands when parsing command lines\n- Multi-stage binding to allow independently-developed libraries to express their configruration needs ahead of program startup\n- Custom type support using [reflectutils.RegisterStringSetter()](https://pkg.go.dev/github.com/muir/reflectutils#RegisterStringSetter).\n- Filling all normal Go types, array, slices, maps, and time.Duration\n- Ability to export flag-based configuration requests to the \"flag\" module (useful for libraries)\n\nWhile nfigure has lots of flexibility and many features, using it should be simple.\n\n## Example: In a library, in-house or published for others to use\n\nIt can pre-register configuration at the library level, before program startup.  This allows\nlibrary-specific configuration to be handled at the library-level rather than pushed to \na central main.\n\n```go\ntype myLibraryConfig struct {\n\tField1\tstring\t  `env=\"FIELD1\" flag:\"field1\" default:\"f1\" help:\"Field1 controls the first field\"`\n\tField2\tint\t  `config:\"mylibrary.field2\"` \n\tField3\t[]string  `flag:\"field3\"`\n}\n\ntype MyLibrary struct {\n\tconfig\tmyLibraryConfig\n}\n\nfunc createMyLibrary(nreg *nfigure.Registry) *MyLibrary {\n\tlib := MyLibrary{}\n\tnreg.Request(\u0026lib.config,\n\t\tnfigure.Prefix(\"myLibrary\"),\n\t\tnfigure.ConfigFileFrom(`env=\"MYLIBRARY_CONFIG_FILE\" flag:\"mylibrary-config\"`),\n\t)\n\t_ = nreg.Configure()\n\treturn \u0026lib\n}\n```\n\n## Example: At the program level\n\nThis is an example using [nserve](https://github.com/muir/nject/tree/main/nserve).\nWhere this gets interesting is if multiple\nbinaries are built from the same source, the set of libraires can exist in a\nlist and only the ones that are needed for particular executables will have their\nconfiguration evaluated.\n\n```go\ntype programLevelConfig struct {\n\tField1\tstring `env=\"field1\" default:\"F1\"`\n\tField4\tfloat64\t`flag:\"field4\" default:\"3.9\"`\n}\n\nfunc createMyApp(myLibrary *mylibrary.MyLibrary, nreg *nfigure.Registery) error {\n\t// start servers, do not return until time to shut down\n\tvar config programLevelConfig\n\tnreg.Request(\u0026config, \"main\")\n\t_ = nreg.Configure()\n}\n\nfunc main() {\n\tapp, _ := nserve.CreateApp(\"myApp\", \n\t\tnfigure.NewRegistryFactory(),\n\t\tcreateMyLibrary, \n\t\tcreateMyApp)\n\t_ = app.Do(nserve.Start)\n\t_ = app.Do(nserve.Stop)\n}\n```\n\n## Supported tags\n\nAssuming a command line parser was bound, the follwing tags are supported:\n\n- `nfigure`: the meta tag, used to control filler interactions\n- `default`: a filler that provides a literal value\n- `env`: fill values from environment variables\n- `config`: fill values from configuration files\n- `flag`: fill values from the command line (Go style or Posix style)\n- `help`: per-item help text for command line Usage\n\n## Environment variables\n\nUsage:\n\n- `env:\"VARNAME\"` specifies that a value can or should be loaded from an environment variable\n\n## Command line parsing\n\nBoth Go-style and Posix-style command line parsing is supported.  In addition to the\nbase features, counters are supported.  Filling maps, arrays, and slices is supported.\n\n- `flag:\"name\"` specifies the name of the command line flag to fill a value.\n- `flag:\"name n\"` specifies a single letter alternative\n- `flag:\"name,split=comma` for array values, specifies that strings will be split on comma, flag can only be given once\n- `flag:\"name,explode=true` for array values, specifies that the flag can be given multiple times and is not split\n- `flag:\"name,counter` for numberic values, counts the number of times the flag is used, flag cannot take argument\n- `flag:\"name,map=explode,split=equal` for maps, support -name a=b -name b=c\n- `flag:\"name,map=prefix` for maps, support --namex=a --nameb=c\n\n### Posix-style\n\nWhen using Posix-style flags (`PosixFlagHandler()`), flags whose names are only a single rune\ncan be combined on the command line:\n\n\t--debug \n\t-d\n\t--verbose\n\t-v\n\t-dv (debug and verbose)\n\nFor boolean values, negation is \"--no-\":\n\n\t--no-verbose\n\n## Best Practices\n\n### Best Practices for existing libraries\n\nLibraries that are already published and using the standard \"flag\" package\ncan be refactored to use nfigure.  If they register themselves with flag during\ninit, then that behavior should be retained:\n\n```go\npackage mylibrary \n\nimport (\n\t\"flag\"\n\t\"github.com/muir/nfigure\"\n)\n\ntype MyConfig struct {\n\tMyField string `flag:\"myfield\"`\n}\nvar myConfig MyConfig\n\nsub init() {\n\terr := nfigure.ExportToFlagSet(flag.CommandLine, \"flag\", \u0026myConfig)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n```\n\nIn a program that is using nfigure, MyConfig can be explicitly imported:\n\n```go\nregistery.Request(\u0026mylibrary.MyConfig)\n```\n\nHowever, if there are other libraries that only support \"flag\" and they're being\nimported:\n\n```go\nGoFlagHandler(nfigure.ImportFlagSet(flag.CommandLine))\n```\n\nThen MyConfig should not also be explicity imported since that would end up\nwith the flags being defined twice.\n\n### Best practices for new libraries\n\nNew libraries should use nfigure to handle their configruation needs.  The suggested\nway to do this is to have a New function that takes a registry as arguments.\n\nSeparate New() and Start() so that configuation can happen after New() but before Start().\n\nUsers of your library can use NewWithRegistry() if they're using nfigure.  For other\nusers, they can fill MyConfig by hand or use \"flag\" to populate the configuration\n\n#### Library writer boilerplate\n```go\nimport \"github.com/muir/nfigure\"\n\nfunc NewWithRegistry(registry *nfigure.Registry) MySelf {\n\tvar config Config\n\tregistry.Request(\u0026config)\n\t...\n}\n\nfunc NewWithConfig(config *Config) MySelf {\n\t...\n}\n\nfunc (m MySelf) Start() {\n\t...\n}\n```\n\n### Library user not using nfigure boilerplate:\n\n```go\nimport (\n\t\"flag\"\n\t\"library\"\n\t\"github.com/muir/nfigure\"\n)\n\nfunc main() {\n\tvar libConfig library.Config\n\tnfigure.MustExportToFlagSet(flag.CommandLine, \"flag\", \u0026libConfig)\n\tlib := library.NewWithConfig(libConfig) // config is not used yet\n\tflag.Parse()\n\tlib.Start() // call to Start must be after config is filled in\n}\n```\n\n#### Library user using nfigure boilerplate:\n\n```go\nimport (\n\t\"flag\"\n\t\"library\"\n\t\"github.com/muir/nfigure\"\n)\n\nfunc main() {\n\tregistry := nfigure.NewRegistry(WithFiller(\"flag\", GoFlagHandler()))\n\tlib := registry.NewWithRegistry(registry)\n\tregistry.Configure()\n\tlib.Start()\n}\n```\n\n### Best practices for program writers\n\nUse nfigure everywhere!  Be careful not to combine ImportFlagSet with\nregistry.Request() of the same models that are ExportToFlagSet()ed \nin library inits.\n\nSeparate library creation from library starting.  Allow configuration\nto be deferred until until library start.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuir%2Fnfigure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmuir%2Fnfigure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuir%2Fnfigure/lists"}