{"id":13410994,"url":"https://github.com/ufoscout/go-up","last_synced_at":"2025-07-08T06:35:30.370Z","repository":{"id":57493367,"uuid":"121938724","full_name":"ufoscout/go-up","owner":"ufoscout","description":"go-up! A simple configuration library with recursive placeholders resolution and no magic.","archived":false,"fork":false,"pushed_at":"2020-01-14T07:21:58.000Z","size":49,"stargazers_count":43,"open_issues_count":1,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T01:47:39.298Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ufoscout.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":"2018-02-18T09:50:00.000Z","updated_at":"2023-09-26T04:39:22.000Z","dependencies_parsed_at":"2022-08-28T17:02:36.222Z","dependency_job_id":null,"html_url":"https://github.com/ufoscout/go-up","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufoscout%2Fgo-up","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufoscout%2Fgo-up/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufoscout%2Fgo-up/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufoscout%2Fgo-up/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ufoscout","download_url":"https://codeload.github.com/ufoscout/go-up/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243871644,"owners_count":20361378,"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:10.711Z","updated_at":"2025-03-17T16:30:49.649Z","avatar_url":"https://github.com/ufoscout.png","language":"Go","funding_links":[],"categories":["Configuration","配置","配置管理","Uncategorized","配置管理 `配置解析库`"],"sub_categories":["Standard CLI","Advanced Console UIs","标准CLI","标准 CLI"],"readme":"# go-up! A simple configuration library with placeholders resolution and no magic.\n\n[![Build Status](https://travis-ci.org/ufoscout/go-up.svg?branch=master)](https://travis-ci.org/ufoscout/go-up)\n[![codecov](https://codecov.io/gh/ufoscout/go-up/branch/master/graph/badge.svg)](https://codecov.io/gh/ufoscout/go-up)\n[![Go Report Card](https://goreportcard.com/badge/github.com/ufoscout/go-up)](https://goreportcard.com/report/github.com/ufoscout/go-up)\n\ngo-up provides a simple way to configure an application from multiple sources — built in resources, property files, environment variables, and whatever else you like.\n\nSome features:\n\n- Recursive placeholders resolution\n- Less than 10Kb \n- No external dependencies\n- Modular and extensible architecture\n\nThis library was started after the considerable disappointment I had when I noticed that the configuration library used by my application increased the binary file size from 3MB to 11MB!!! (See the [Rationale](#rationale) section below for detailed info)\n\nGo-Up is written to be lightweight and will always remain lean and straightforward.\n\n\nGetting Started\n---------------\n\n1. To get started, import go-up:\n\nIn your go file:\n```Go\nimport (\n\t\"github.com/ufoscout/go-up\"\n)\n```\n\nIf you use VGO, add in your go.mod file:\n```\nrequire github.com/ufoscout/go-up v0.6.1\n```\n\n\n2. Define some properties. You can use placeholders, for example, in `config.properties`:\n\n```properties\n# This line is a comment because it stats with '#'\n\nserver.port=9090\nserver.host=127.0.0.1\n\n# Placeholders here, they will be resolved at runtime\nserver.url=http://${server.host}:${server.port}/\n```\n\nKeys and values in a properties file are automatically trimmed.\n\n\n3. Build a go-up object that your properties:\n\n```Go\nignoreFileNotFound := false \nup, err := NewGoUp().\n\t\tAddFile(\"./confing.properties\", ignoreFileNotFound).\n\t\tBuild();\n```\n\n\n4. Look up properties by key:\n\n```Go\nport := up.GetIntOrDefault(\"server.port\", 8080); // returns 9090\nserverUrl := up.GetString(\"server.url\") // returns http://127.0.0.1:9090/\ndefaultVal := up.GetStringOrDefault(\"unknown_Key\", \"defaultValue\") // returns defaultValue\n```\n\n\nReaders\n-------\nIn go-up a \"Reader\" is whatever source of properties.\nBy default, go-up offers readers to load properties from:\n\n* properties files in the file system\n* Environment variables\n* Programmatically typed properties\n\nCustom properties readers can be easily created implementing the `github.com/ufoscout/go-up/reader/Reader` interface.\n\n\nPlaceholders resolution\n-----------------------\ngo-up resolves placeholders recursively. \nFor example:\n\nfileOne.properties:\n```properties\nserver.url=http://${${environment}.server.host}:${server.port}\nserver.port=8080\n```\n\nfileTwo.properties:\n```properties\nenvironment=PROD\nPROD.server.host=10.10.10.10\nDEV.server.host=127.0.0.1\n```\n\nGo code:\n```Go\nup, err := go_up.NewGoUp().\n AddFile(\"./fileOne.properties\", false).\n AddFile(\"./fileTwo.properties\", false).\n Build()\n\nfmt.Println(up.GetString(\"server.url\")) // this prints 'http://10.10.10.10:8080'\n```\n\nBy default `${` and `}` delimiters are used. Custom delimiters can be easily defined:\n\n```Go\nup, err := go_up.NewGoUp().\n Delimiters(\"%(\", \")\"). // using %( and ) as delimiters\n ... etc ...\n Build()\n```\n\nDefault Values\n--------------\nPlaceholders can have default values which are used if the key is not otherwise provided. Example:\n\nconfig.properties:\n```properties\n# the default value \"8080\" is used if 'PORT_NUMBER' is not provided\nserver.port=${PORT_NUMBER:8080}\n    \n# default is 127.0.0.1\nserver.ip=${IP:127.0.0.1}\n\nserver.url=${server.ip}/${server.port}\n```\n\nGo code:\n```Go\nup, err := go_up.NewGoUp().\n AddFile(\"./config.properties\", false).\n Build()\n\n// this prints 'http://127.0.0.1:8080'\nfmt.Println(up.GetString(\"server.url\")) \n```\n\nThe default separator for the default value is \":\". A custom value can be set through the 'DefaultValueSeparator()' method of the GoUpBuilder.\n\nReaders priority -\u003e Last one wins\n---------------------------------\nProperties defined in later readers will override properties defined in earlier readers, in case of overlapping keys.\nHence, make sure that the most specific readers are the last ones in the given list of locations.\n\nFor example:\n\nfileOne.properties:\n```properties\nserver.url=urlFromOne\n```\n\nfileTwo.properties:\n```properties\nserver.url=urlFromTwo\n```\n\n```Go\nup, err := go_up.NewGoUp().\n AddFile(\"./fileOne.properties\", false). \n AddFile(\"./fileTwo.properties\", false).\n AddReader(go_up.NewEnvReader(\"\", false, false)). // Loading environment variables\n Build()\n\n// this prints 'urlFromTwo'\nfmt.Println(up.GetString(\"server.url\"))\n\n```\n\nBTW, due to the fact that we used go_up.NewEnvReader(...)` as last reader, if at runtime an Environment variable called \"server.url\" is found, it will override the other values.\n\nFinally, it is possible to specify a custom priority:\n\n```Go\nup, err := go_up.NewGoUp().\n\n // load the properties from the file system and specify their priority\n AddFileWithPriority(\"./fileOne.properties\", false, go_up.HighestPriority).\n\n AddFile(\"./fileTwo.properties\", false). // loads file with default priority\n AddReader(go_up.NewEnvReader(\"\", false, false)). // Loads environment variables\n Build()\n\n// this prints 'urlFromOne'\nfmt.Println(up.GetString(\"server.url\"))\n```\n\nThe default priority is 100. The highest priority is 0.\nAs usual, when more readers have the same priority, the last one wins in case of overlapping keys.\n\n\nWorking with Environment Variables\n----------------------------------\nBuilt in support for environment variables is provided through the EnvReader struct:\n\n```Go\n// These should be defined in your system, not in the code. Here only as example.\nos.Setenv(\"KEY_FROM_ENV\", \"ValueFromEnv\")\nos.Setenv(\"CUSTOM_PREFIX_KEY_FROM_ENV\", \"ValueFromEnv_WithCustomPrefix\")\n\n// Loads only variables with this prefix. The prefix is removed at runtime.\nprefix := \"CUSTOM_PREFIX_\" \n// if true, the Env variable key is converted to lower case\ntoLowerCase := true \n// if true, the underscores \"_\" of the env variable key are replaced by dots \".\"\nunderscoreToDot := true \n\nup, err := go_up.NewGoUp().\n AddReader(go_up.NewEnvReader(prefix, toLowerCase, underscoreToDot)). // Loading environment variables\n Build()\n\n// this prints 'ValueFromEnv_WithCustomPrefix'\nfmt.Println(up.GetString(\"key.from.env\"))\n```\n\n\nReal life example\n-----------------\nA typical real life configuration would look like:\n\n```Go\nup, err := go_up.NewGoUp().\n\n // load a file\n AddFile(\"./default.properties\", false).\n\n // load another file\n AddFile(\"./config/config.properties\", false).\n\n // Add a not mandatory file, resource not found errors are ignored.\n // Here I am adding properties requried only during testing.\n AddFile(\"./test/test.properties\", true).\n\n // Load the Environment variables.\n // The are used as they are defined, e.g. ENV_VARIABLE=XXX\n AddReaderWithPriority(go_up.NewEnvReader(\"APP_PREFIX_\", false, false)).\n\n // Load the Environment variables and convert their keys\n // from ENV_VARIABLE=XXX to env.variable=XXX\n // This could be desired to override default properties\n AddReaderWithPriority(go_up.NewEnvReader(\"APP_PREFIX_\", true, true)).\n \n // build the go-up object\n .Build()\n```\n\ngo-up API\n-------------\ngo-up has a straightforward API that hopefully does not need detailed documentation.\n\nSome examples:\n\n```Go\nup, err := go_up.NewGoUp().\n Add(\"programmatically.added.key\", \"12345\"). // default values can be added programmatically\n AddFile(\"./myFile.properties\", false).\n Build()\n\n// get a String. The empty string value is returned if the key is not found\naString := up.GetString(\"key\")\n\n// get a String. \"defaultValue\" is returned if the key is not found\naStringOrDefault := up.GetStringOrDefault(\"key\", \"defaultValue\")\n\n// get a String. An error is returned if the key is not found\naStringOrError, error1 := up.GetStringOrFail(\"key\")\n\n// get a slice from the comma separated tokens of the property value\naslice := up.GetStringSlice(\"key\", \",\")\n```\n\nAll available methods are defined in the GoUp interface:\n\n```Go\ntype GoUp interface {\n\n  Exists(key string) bool\n\n  GetBool(key string) bool\n  GetBoolOrDefault(key string, defaultValue bool) bool\n  GetBoolOrFail(key string) (bool, error)\n   \n  GetFloat64(key string) float64\n  GetFloat64OrDefault(key string, defaultValue float64) float64\n  GetFloat64OrFail(key string) (float64, error)\n    \n  GetInt(key string) int\n  GetIntOrDefault(key string, defaultValue int) int\n  GetIntOrFail(key string) (int, error)\n   \n  GetString(key string) string\n  GetStringOrDefault(key string, defaultValue string) string\n  GetStringOrFail(key string) (string, error)\n\n  GetStringSlice(key string, separator string) []string\n  GetStringSliceOrDefault(key string, separator string, defaultValue []string) []string\n  GetStringSliceOrFail(key string, separator string) ([]string, error)\n\n}\n```\n\nGoDoc\n-----\nSee [https://godoc.org/github.com/ufoscout/go-up](https://godoc.org/github.com/ufoscout/go-up)\n\n# Rationale\nBefore Go-Up, I used to use [Viper](https://github.com/spf13/viper). \nViper is great and I surely recommend it; nevertheless, all of its features are not for free. In fact, it adds tons of dependencies that make the application much heavier.\n\nSo, if like me you aspire to code which is as light as possible and you can live with a smaller set of features (BWT go-up has some unique aces in the hole like recursive placeholders resolution!), then you should probably take into account a lighter alternative like go-up.\n\nTo show the impact that a single library can have on your application, I created three small examples (see: [go-up_examples](https://github.com/ufoscout/go-up_examples) ), these are:\n\n- *goup* : a Go application that, using **go-up**, reads \"Hello World\" from a config file and prints it\n- *plain* : a Go application that prints \"Hello World\"\n- *viper* : a Go application that, using **viper**, reads \"Hello World\" from a config file and prints it\n\nWhen built, they produce surprising results:\n\n| Library       | Produced Binary Size |\n| ------------- |---------------------:|\n| Go-Up         |             2.185 KB |\n| Plain Go      |             2.000 KB |\n| Viper         |            11.782 KB |\n\n\u003cins\u003eThe Viper based binary file is nearly 6 times bigger than the other implementations!\u003c/ins\u003e\n\n(Build performed with go1.13 linux/amd64 on Ubuntu 18.04)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fufoscout%2Fgo-up","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fufoscout%2Fgo-up","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fufoscout%2Fgo-up/lists"}