DERO-HE STARGATE Testnet Release27
This commit is contained in:
parent
ac9d12577c
commit
2ef26faa40
@ -123,9 +123,9 @@ func (chain *Blockchain) Get_Difficulty_At_Tips(tips []crypto.Hash) *big.Int {
|
|||||||
step := new(big.Int)
|
step := new(big.Int)
|
||||||
|
|
||||||
if globals.IsMainnet() {
|
if globals.IsMainnet() {
|
||||||
MinimumDifficulty = new(big.Int).SetUint64(config.MAINNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
MinimumDifficulty = new(big.Int).SetUint64(config.Settings.MAINNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
||||||
} else {
|
} else {
|
||||||
MinimumDifficulty = new(big.Int).SetUint64(config.TESTNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
MinimumDifficulty = new(big.Int).SetUint64(config.Settings.TESTNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
||||||
}
|
}
|
||||||
GenesisDifficulty := new(big.Int).SetUint64(1)
|
GenesisDifficulty := new(big.Int).SetUint64(1)
|
||||||
|
|
||||||
|
@ -327,12 +327,12 @@ func (pool *Mempool) Mempool_Print() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
loggerpool.Info(fmt.Sprintf("Total TX in mempool = %d\n", len(klist)))
|
loggerpool.Info(fmt.Sprintf("Total TX in mempool = %d\n", len(klist)))
|
||||||
loggerpool.Info(fmt.Sprintf("%20s %14s %7s %7s %6s %32s\n", "Added", "Size", "Height", "TXID"))
|
loggerpool.Info(fmt.Sprintf("%20s %7s %6s %32s\n", "Added", "Size", "Height", "TXID"))
|
||||||
|
|
||||||
for i := range klist {
|
for i := range klist {
|
||||||
k := klist[i]
|
k := klist[i]
|
||||||
v := vlist[i]
|
v := vlist[i]
|
||||||
loggerpool.Info(fmt.Sprintf("%20s %14s %7d %7d %6d %32s\n", time.Unix(int64(v.Added), 0).UTC().Format(time.RFC3339),
|
loggerpool.Info(fmt.Sprintf("%20s %7d %6d %32s\n", time.Unix(int64(v.Added), 0).UTC().Format(time.RFC3339),
|
||||||
len(v.Tx.Serialize()), v.Height, k))
|
len(v.Tx.Serialize()), v.Height, k))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func Test_mempool(t *testing.T) {
|
|||||||
|
|
||||||
// this tx is from internal testnet
|
// this tx is from internal testnet
|
||||||
// tx_id 499002f3fb7fea8a71dac93dea65c0ff74be05b0858078a27a48d78b71eacf87
|
// tx_id 499002f3fb7fea8a71dac93dea65c0ff74be05b0858078a27a48d78b71eacf87
|
||||||
tx_hex := "010000030101000000000000000000000000000000000000000000000000000000000000000000002aa937d8879cebc74425f4fee7bcc81b424dd6f2130d0548a0c4afa7f9e40b1c41ee8f9487971ee3ecf35cef40d26b6382b49b6315370f96696cda3394a550c4bd5d301a3b91737c354573ffa0d9e921a523af274b2ccc5d7078efd5c489c8deba43a0205aafd9f89b80cd432825f2f0bcbfa61f82bc30c2a83a7976e6743aad48fbdb8c7799cbf3131d156d2ac7b780c80202000d136ae832342f1ecf99a935e1dcdc255c7f33297b204383e7539f31278fd097001e261e85c34489c313aaa05ce4abb3772d31e5d192321dc2cce7fe45b21f4d114244b8fbf5a6cc1f0119b69ed785e7c8739c306b4a35278159bbb6977a28ea71ea0ffc5a6398ec935e000b8b5806e6e7a0bd8b57e24edd21cfafa79a54bf7d3bc74643294b88920b3b900113dad7529adb51a5b634140f01c8389c57c92af984d16cf4400a463a448fd7e7007ed6b651c46aab06f6f9677d50ee78cf98a3e121b325a0cab4bf67910f9a92d0153222d7f87cedc63a5b773a5c5482479e554d56d906eaf5d5f9b1c37d9f52170029bd3c397bcb89dca495d7ee8cab0e5e4cd8b204dff933e8673cacece063830d010676be97476c5028c633ca3160a7367352750304ee2be99bf6311c198b1207a20006364d038c0d479f5264c085c8f210ea88f8a7f08535240c3541c641aadae496002449b5477c26ff74be1159c36d71d9dbca3ab2c2a0804590637fb79e0d89e5d801053afa58bcdbf17aa0ce544e3ca12e9b5c204aaea6a0b5698fef75669c1d91690114acfe9439eff58fe532aa243187adadf7118ba78f9772429b3041a07cb203df0125ffa09b619faa1b890d0a5323a4f162b4d0d1a6864f9a0261820bfc901dea4501098ad5f3c3bb0629de269f3e01633b4e0ee6a8f74e648aa40f57dd2496ed7230010f100ede75074bafc160fc1327545a44fd8d34627b136fed6de0bfa72df0e1330116108dc5545f42b9f5b8e302d1546930d9840c18fc92e425bacd0856e842f60f011b7f689aa43b95bbfd02ee20aab2cac2077c9639b9e4f84b8b221b37cdaf13a2010b979e41274c4338395dfc447c0d4a548caf414ee425aab1748475cca9faef4e012051ff90139adccfbf0459e3a5c0638dfb1654ee728cba59c6803ab1daaddda9011ef927e921f956134892499c7513bc2e077986295c050633a1566d449fd0954a00071f56b24c65aaa4549be3c859ac8ec75fe302d89e8755166da3f7d0d08ae285011983fdc2aa2a0611187b0b86119508e3f7642dfe7380f5bdbe6ad27f0dc4ccf201127e3c3923518fec22a4ead80845bbe6008d9b790bd50ec6ef83b087d0490995011982ad7c49288a3115c638a04e14a0c74a75008359b40aff422e9972e276929800237a7bfd1fe105865abbc36e926de565530da393da1ddc0b1005bb0c31504402002569e7e95e54711903c29f63cb8c6b58d47ea3299c455c8738b543d552e8dffe012ea47467abedda0d168d06bcf2f45ab8fa090b90d6e4662d4052df0488084403057542fc7603a7d717dd8267dc036330e3083d2a1562bf5b1ecfbce99804c05100000000000000000000000000000000000000000000000000000000000000000e377bd9c8a2b6faf14c766e51fc9415ed979d604e3d5dcc800b3c5a9c6bec1f239a65bdeb2028926a6fa13fdf2caf0a09ba17e1420b74bef294aa4c9731d2c71f46ae2c57612f525f14cf49e86be5a984aab4d8c25ec3818e19612ba07722d500082c5b600665b3a718fad7516b976749ac3692a6ccf60225c04d1da272a95780012c932f36c1cca1753377ad16b2dbecabb58a2efbf8e88a066d80efba20addf8208d52649fc24ba86ecd87da10efd8ab480c48cadf4ee205e60d10f6e1f1527dd01171915999416a85d08a06e4a8ed18797fcd62910e6624222e120bdd60a00db22fb4d9f0a7aee7570f72b99c673195c67b7dc5e53aa5a32ffc04b409aa79cb52faa6cb8374cd242bd90fd2da9a891fc18022112ecb943b8b0f6c69a4b2e33ad0b16abbd22b38f11f1e76bfd6c1950864753ef84ce0ba869b82f909fbe2751ef0e02b170d107ec7cf6c25d9fdbdd86f5e4790e532254f89ad6cd477bf30816e207ce092de3148c4fd818812abfcadc03a325991fb7dd48b5a54266137709715a11556344ae9bbb2afd1e7ea7fa417f1308d20fdef8dd2168f89101d0643ce62217ee68724101b6e8644ad0860940f95b1674a1befa7ec45235d2c93043087e9001211d72b9f744364770a276f99a183dc184f90ad09d034119f5e4754249046bb201156984d6a418664bda88f98075679d6bb8cc4c644ff727278931797db12a2c65012698726eabb2580faecafa12befe8855a7074da2b4f2d2cd74fd481e818f2287001d445415f6abc3b4a9ff7043906d8f83d5c61ad853b12a72c152a21b9627d8bf00004d5a3deaf642e40e1401ead28babb7ce0263afcc3b3f1e7876a31d6fbea5e90125bff3189cf6ab0e68caa851e60fc92483d3bc2a5ecd511594e7f80f565301c101247b8928fb1866a98b99add332a7e6065cf27ed8a7d48d7775727299308f0ab9002cb7663076e2883fe31b3397f6f5460491835f1a23c173f858fb93cbc312cf360117c5b8e4a25c2e3228b6f4d06525f96e818772292b095b7d344eb3f9f18735bd000889c0fcc3c2f25fe74c915b178db653b0d7725efdc6e2e80a2770738cc3bff9002a578a39364fa16dc82fd7f15872da799f2ddc6422491b9e5c6447ea87adb01a00187461816522889fc2bcb1dc1b0b5d2537df3ae2bb58cbef2b58cb678ba6b9f30121f1cf89f1e9840aace8469b06edf81e9930ddfb8aef4a2c3d46e8551cc40ab400"
|
tx_hex := "01000003519b9e874a9dd7fcbfb7802268123dcf970c336891101a3a0705855b72b9eb08c80100000000000000000000000000000000000000000000000000000000000000000000f394d06566a81b024fd8624b4c8592f8b9344e58f227261eb08d821bd190b4ac9c7df0660038ddcf881602a14fe5ad8eebff83d3ce3541a1f232fe61acf71109e417160936863e8c0597c798b73ef08e76b8eeebcaf20260ba91fcdef5f626361f824dc6197b02c599d4511c624a933226d8fccc125611ec80d8ad5acea4baf2f1c77e4c5525d9859b40f43daaf3d4e4450203d80419dcb026b9adbd7d6912284ee9ce20f3721076947f16e1faefd9b8c35116f4cf0044ead0a12fda3f3bcf2ed91627b922c02c10d112df9e782d3750cda151f6ba2ebe6495065141f32800894566011198c4055458005a31d3e285037446ba14bac54420bcab87cbebeaccab1f158b011e90132b7310e6c866e348cab68d21bad1d9f811b4d667a1e40bdef1a19259d3000b972ae06da958658b12ca4a45c2c6ea50d305be86f7e979fc962c687eb012f401a2c4bf0beb6c713343b94d65ee6d937660480daf670ab5b0ed421a9103e8a25119c836dd799600e67e4ec66ef680833398a99b16956aed2ee5bd9e6c8e68009a01145636e5e448d6c8ab902db394aa9c5aeee92dd150e1150d5b8f6674f330c279010912df27232ae126525a130ed8a9b4dee62eb57ce1d8a42ae9bcef867b10ce94012f94aa78846fcdda504243f83f3122aee5dc38dd32fa2a90224b3a40b4cf79c301129028709e39591c55e6e477a36cc26dc2efd7183fb59f51635dbd114dfed9d5010d66f31b781fa181c2114600ee1f03d55439355265700fb9d35ac684a19db398001e12558cc17e59fb85fb825481dee6fef3612c043bb38ca4833183da5381364600077a99d4d2df2f06067359f178a40f98a3e0943309c5ce3ce5398b9909174254012aa49cfc78ebec51c00e7824db0d8d41ef43fbb0396824d2c31b7db64124cc61000a8b51bd6c94a502f7aea3e5001f00d76b165ad580e48f8aa72813da8240286e00055f3480aa7cdb68a20d4b1e567d42389b44cf4e345da9e5a5655a80418695bc012c8f1e1ef8af646b4fb08afaeb3febde17c4c0d04b30dfd22d96a32ced9c9439010c40484e1547d307ad56b79bb7450e4eaaed7ed79bd5392dffd7449043f8783a0015104264f8d80356176ca6fdf4777e4ebc8edcbab50f8b6c366ac75ebd6bb5c701078d55053d2d41f21f51d5e617f282ba9dfa2576f0231048dd73998ceaa699fd0105821b61addd6935c7d80e65c706f0f2aa2d73ad22c5548c9c8b92cd82689dc40115f576c413a37014b9ee5377b121c69ad6e0f00f2b4f6c99d1f68b00a5d923370119b29fe6e9cf35f1344e24d52ee1fdb6521de3176f3d2b2a2b9327ae6e6cfbb10017198369a6ff180253700361c40e7f378c8e7ead6e04f01dbf5b4c47e7e1e4ff001abaf84628764eccbc5d0f66dc0a99505bb0e598362c45312470526afad11caf00242c6a902c564b3b363eb4ae08f643f18571779428358bfbdaf9856b0ebf229f0100000000000000000000000000000000000000000000000000000000000000001b7a78b6f573d4ac5d3a8d7d10dafac0dbc92604474417b29d40448d9fbc821d0fe3f73da41b27752d5db0658fcc0bff7da21112417ac719e491fdd43c8e5d0b07bcca22c27367a276e6a4e0cfb1bfbc772842898356ad4723101d4ada5137f409bb3b290d7519da3af15feb09e183e2be809690099551f1cb984deb1295b7611dd0cfd9000482314a388512d278fc22b2f6e3fc6e95023b4bd228e22626b7440119fa2c6f45a33b008ef5b23a5522ebbf0bf5f26d8263613a7f9b5ad9a192357600107a023453f7f79f413756dfba0096ae5d13158bbbb147089a020e819ef6640728acf01c811b967d1a14d767ac20088c5ca1613aa5d94ebe7fe2840b6431d8d002137dae183071d12cd9fb14c4b4738ef750ff514ce26257d1245eaedfea625516bfcb075a33a8910dc4e40742bfaa70d95591711e9db33d0db0be06cc819817072c22a9c81f4369f68bb1ef71c7f7458671d647caef18b30cca91da4f1b201d082a03306b4a07d1f0e1a8cc8844038fc524201a6c3a346a971203fd45458d292987d44049eecdac25c5622465a8d683a43621612d2fb283724b17a7bfa3bad2266a6fb1cf669eef21c90e24e85d6ab48e8b237355e964407dde00d65fb442dd210ce0378349f533c0e8ab65293de5319e246a044ed22b1e6db28add1508dd6f0da22954eea8a431b211a8b54147e32fec6593f9fb731389f62f94d5047e0f3301216576c71a79629bc3879325280e216d59cf7e6ff2d6a0babe418f8f2ed603ce010e16785f8c855043221ed5a603d4ed4e8e90059669543738ceb2a04dc996079c01264e9beaf4bb82e1b8f47bb0c239167064001e306eeef2b5495ec42754806b65012357f738543b13594ff081a070dbc0575645ac5ce8e0f89d3afdeb3d49e9ddae012a17d5095b9cef08f6eee2ce733dbf2cfcdb8c23e39410ed973e68cf8505266d00280c52b45752dd12ba84499618898111a8f9b19f2b76c06917e57eef221f3387011cc3360b478d144118a82a3c4391814f34aac2fb0f2d043394304bed179689e501292ca5631820f5f465242b4cc517f06c153c176a43f77daca463b7e2d3e2d7ba001aa0b2c90c3172cd2556137d78748f47924432449210d8b2f5e27adb6210bc77012c92a12526a5b0b1ef60994eb4d08e9bb5a3ea330d417838a52ad8636d37fa9e00131153c9affeefd6b98ecb6a80f5e6ea97417376030325182c064e12dd353c32011f4e25c49eea09adaa46d56642e2ea0afced2d75036acd3839435863f1ec3bf1002e19fe8456a7236c8fb77a15f87164debaad865e85c3468152a5deaffde3da6a01"
|
||||||
|
|
||||||
var tx, dup_tx transaction.Transaction
|
var tx, dup_tx transaction.Transaction
|
||||||
|
|
||||||
|
@ -166,12 +166,15 @@ func (chain *Blockchain) Create_new_miner_block(miner_address rpc.Address) (cbl
|
|||||||
}
|
}
|
||||||
|
|
||||||
height := chain.Calculate_Height_At_Tips(bl.Tips) // we are 1 higher than previous highest tip
|
height := chain.Calculate_Height_At_Tips(bl.Tips) // we are 1 higher than previous highest tip
|
||||||
|
|
||||||
history := map[crypto.Hash]bool{}
|
history := map[crypto.Hash]bool{}
|
||||||
|
|
||||||
var history_array []crypto.Hash
|
var history_array []crypto.Hash
|
||||||
for i := range bl.Tips {
|
for i := range bl.Tips {
|
||||||
history_array = append(history_array, chain.get_ordered_past(bl.Tips[i], 26)...)
|
h := height - 20
|
||||||
|
if h < 0 {
|
||||||
|
h = 0
|
||||||
|
}
|
||||||
|
history_array = append(history_array, chain.get_ordered_past(bl.Tips[i], h)...)
|
||||||
}
|
}
|
||||||
for _, h := range history_array {
|
for _, h := range history_array {
|
||||||
history[h] = true
|
history[h] = true
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/satori/go.uuid"
|
import "github.com/satori/go.uuid"
|
||||||
|
import "github.com/caarlos0/env/v6"
|
||||||
import "github.com/deroproject/derohe/cryptography/crypto"
|
import "github.com/deroproject/derohe/cryptography/crypto"
|
||||||
|
|
||||||
// all global configuration variables are picked from here
|
// all global configuration variables are picked from here
|
||||||
@ -52,12 +53,20 @@ const MAX_RINGSIZE = 128 // <= 128, ringsize will be accepted
|
|||||||
// Minimum FEE calculation constants are here
|
// Minimum FEE calculation constants are here
|
||||||
const FEE_PER_KB = uint64(100) // .00100 dero per kb
|
const FEE_PER_KB = uint64(100) // .00100 dero per kb
|
||||||
|
|
||||||
const MAINNET_BOOTSTRAP_DIFFICULTY = uint64(80000000) // atlantis mainnet botstrapped at 80 MH/s
|
|
||||||
const MAINNET_MINIMUM_DIFFICULTY = uint64(800000000) // 80 MH/s
|
|
||||||
|
|
||||||
// testnet bootstraps at 1 MH
|
type SettingsStruct struct {
|
||||||
const TESTNET_BOOTSTRAP_DIFFICULTY = uint64(10000) // testnet bootstrap at 50KH/s
|
MAINNET_BOOTSTRAP_DIFFICULTY uint64 `env:"MAINNET_BOOTSTRAP_DIFFICULTY" envDefault:"80000000"`
|
||||||
const TESTNET_MINIMUM_DIFFICULTY = uint64(10000) // 10KH/s
|
MAINNET_MINIMUM_DIFFICULTY uint64 `env:"MAINNET_MINIMUM_DIFFICULTY" envDefault:"80000000"`
|
||||||
|
|
||||||
|
TESTNET_BOOTSTRAP_DIFFICULTY uint64 `env:"TESTNET_BOOTSTRAP_DIFFICULTY" envDefault:"10000"`
|
||||||
|
TESTNET_MINIMUM_DIFFICULTY uint64 `env:"TESTNET_MINIMUM_DIFFICULTY" envDefault:"10000"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var Settings SettingsStruct
|
||||||
|
|
||||||
|
var _ = env.Parse(&Settings)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this single parameter controls lots of various parameters
|
// this single parameter controls lots of various parameters
|
||||||
// within the consensus, it should never go below 7
|
// within the consensus, it should never go below 7
|
||||||
|
@ -20,4 +20,4 @@ import "github.com/blang/semver/v4"
|
|||||||
|
|
||||||
// right now it has to be manually changed
|
// right now it has to be manually changed
|
||||||
// do we need to include git commitsha??
|
// do we need to include git commitsha??
|
||||||
var Version = semver.MustParse("3.4.80-1.DEROHE.STARGATE+20112021")
|
var Version = semver.MustParse("3.4.82-1.DEROHE.STARGATE+20112021")
|
||||||
|
@ -146,13 +146,13 @@ func Connection_Pending_Clear() {
|
|||||||
if time.Now().Sub(v.update_received).Round(time.Second).Seconds() > 20 {
|
if time.Now().Sub(v.update_received).Round(time.Second).Seconds() > 20 {
|
||||||
v.exit()
|
v.exit()
|
||||||
Connection_Delete(v)
|
Connection_Delete(v)
|
||||||
v.logger.Info("Purging connection due since idle")
|
v.logger.V(1).Info("Purging connection due since idle")
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsAddressInBanList(Address(v)) {
|
if IsAddressInBanList(Address(v)) {
|
||||||
v.exit()
|
v.exit()
|
||||||
Connection_Delete(v)
|
Connection_Delete(v)
|
||||||
v.logger.Info("Purging connection due to ban list")
|
v.logger.V(1).Info("Purging connection due to ban list")
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -112,7 +112,7 @@ func Test_TX_Valid(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "4 ring test",
|
name: "4 ring test",
|
||||||
txhex: "010000030101000000000000000000000000000000000000000000000000000000000000000000002aa937d8879cebc74425f4fee7bcc81b424dd6f2130d0548a0c4afa7f9e40b1c41ee8f9487971ee3ecf35cef40d26b6382b49b6315370f96696cda3394a550c4bd5d301a3b91737c354573ffa0d9e921a523af274b2ccc5d7078efd5c489c8deba43a0205aafd9f89b80cd432825f2f0bcbfa61f82bc30c2a83a7976e6743aad48fbdb8c7799cbf3131d156d2ac7b780c80202000d136ae832342f1ecf99a935e1dcdc255c7f33297b204383e7539f31278fd097001e261e85c34489c313aaa05ce4abb3772d31e5d192321dc2cce7fe45b21f4d114244b8fbf5a6cc1f0119b69ed785e7c8739c306b4a35278159bbb6977a28ea71ea0ffc5a6398ec935e000b8b5806e6e7a0bd8b57e24edd21cfafa79a54bf7d3bc74643294b88920b3b900113dad7529adb51a5b634140f01c8389c57c92af984d16cf4400a463a448fd7e7007ed6b651c46aab06f6f9677d50ee78cf98a3e121b325a0cab4bf67910f9a92d0153222d7f87cedc63a5b773a5c5482479e554d56d906eaf5d5f9b1c37d9f52170029bd3c397bcb89dca495d7ee8cab0e5e4cd8b204dff933e8673cacece063830d010676be97476c5028c633ca3160a7367352750304ee2be99bf6311c198b1207a20006364d038c0d479f5264c085c8f210ea88f8a7f08535240c3541c641aadae496002449b5477c26ff74be1159c36d71d9dbca3ab2c2a0804590637fb79e0d89e5d801053afa58bcdbf17aa0ce544e3ca12e9b5c204aaea6a0b5698fef75669c1d91690114acfe9439eff58fe532aa243187adadf7118ba78f9772429b3041a07cb203df0125ffa09b619faa1b890d0a5323a4f162b4d0d1a6864f9a0261820bfc901dea4501098ad5f3c3bb0629de269f3e01633b4e0ee6a8f74e648aa40f57dd2496ed7230010f100ede75074bafc160fc1327545a44fd8d34627b136fed6de0bfa72df0e1330116108dc5545f42b9f5b8e302d1546930d9840c18fc92e425bacd0856e842f60f011b7f689aa43b95bbfd02ee20aab2cac2077c9639b9e4f84b8b221b37cdaf13a2010b979e41274c4338395dfc447c0d4a548caf414ee425aab1748475cca9faef4e012051ff90139adccfbf0459e3a5c0638dfb1654ee728cba59c6803ab1daaddda9011ef927e921f956134892499c7513bc2e077986295c050633a1566d449fd0954a00071f56b24c65aaa4549be3c859ac8ec75fe302d89e8755166da3f7d0d08ae285011983fdc2aa2a0611187b0b86119508e3f7642dfe7380f5bdbe6ad27f0dc4ccf201127e3c3923518fec22a4ead80845bbe6008d9b790bd50ec6ef83b087d0490995011982ad7c49288a3115c638a04e14a0c74a75008359b40aff422e9972e276929800237a7bfd1fe105865abbc36e926de565530da393da1ddc0b1005bb0c31504402002569e7e95e54711903c29f63cb8c6b58d47ea3299c455c8738b543d552e8dffe012ea47467abedda0d168d06bcf2f45ab8fa090b90d6e4662d4052df0488084403057542fc7603a7d717dd8267dc036330e3083d2a1562bf5b1ecfbce99804c05100000000000000000000000000000000000000000000000000000000000000000e377bd9c8a2b6faf14c766e51fc9415ed979d604e3d5dcc800b3c5a9c6bec1f239a65bdeb2028926a6fa13fdf2caf0a09ba17e1420b74bef294aa4c9731d2c71f46ae2c57612f525f14cf49e86be5a984aab4d8c25ec3818e19612ba07722d500082c5b600665b3a718fad7516b976749ac3692a6ccf60225c04d1da272a95780012c932f36c1cca1753377ad16b2dbecabb58a2efbf8e88a066d80efba20addf8208d52649fc24ba86ecd87da10efd8ab480c48cadf4ee205e60d10f6e1f1527dd01171915999416a85d08a06e4a8ed18797fcd62910e6624222e120bdd60a00db22fb4d9f0a7aee7570f72b99c673195c67b7dc5e53aa5a32ffc04b409aa79cb52faa6cb8374cd242bd90fd2da9a891fc18022112ecb943b8b0f6c69a4b2e33ad0b16abbd22b38f11f1e76bfd6c1950864753ef84ce0ba869b82f909fbe2751ef0e02b170d107ec7cf6c25d9fdbdd86f5e4790e532254f89ad6cd477bf30816e207ce092de3148c4fd818812abfcadc03a325991fb7dd48b5a54266137709715a11556344ae9bbb2afd1e7ea7fa417f1308d20fdef8dd2168f89101d0643ce62217ee68724101b6e8644ad0860940f95b1674a1befa7ec45235d2c93043087e9001211d72b9f744364770a276f99a183dc184f90ad09d034119f5e4754249046bb201156984d6a418664bda88f98075679d6bb8cc4c644ff727278931797db12a2c65012698726eabb2580faecafa12befe8855a7074da2b4f2d2cd74fd481e818f2287001d445415f6abc3b4a9ff7043906d8f83d5c61ad853b12a72c152a21b9627d8bf00004d5a3deaf642e40e1401ead28babb7ce0263afcc3b3f1e7876a31d6fbea5e90125bff3189cf6ab0e68caa851e60fc92483d3bc2a5ecd511594e7f80f565301c101247b8928fb1866a98b99add332a7e6065cf27ed8a7d48d7775727299308f0ab9002cb7663076e2883fe31b3397f6f5460491835f1a23c173f858fb93cbc312cf360117c5b8e4a25c2e3228b6f4d06525f96e818772292b095b7d344eb3f9f18735bd000889c0fcc3c2f25fe74c915b178db653b0d7725efdc6e2e80a2770738cc3bff9002a578a39364fa16dc82fd7f15872da799f2ddc6422491b9e5c6447ea87adb01a00187461816522889fc2bcb1dc1b0b5d2537df3ae2bb58cbef2b58cb678ba6b9f30121f1cf89f1e9840aace8469b06edf81e9930ddfb8aef4a2c3d46e8551cc40ab400",
|
txhex: "01000003519b9e874a9dd7fcbfb7802268123dcf970c336891101a3a0705855b72b9eb08c80100000000000000000000000000000000000000000000000000000000000000000000f394d06566a81b024fd8624b4c8592f8b9344e58f227261eb08d821bd190b4ac9c7df0660038ddcf881602a14fe5ad8eebff83d3ce3541a1f232fe61acf71109e417160936863e8c0597c798b73ef08e76b8eeebcaf20260ba91fcdef5f626361f824dc6197b02c599d4511c624a933226d8fccc125611ec80d8ad5acea4baf2f1c77e4c5525d9859b40f43daaf3d4e4450203d80419dcb026b9adbd7d6912284ee9ce20f3721076947f16e1faefd9b8c35116f4cf0044ead0a12fda3f3bcf2ed91627b922c02c10d112df9e782d3750cda151f6ba2ebe6495065141f32800894566011198c4055458005a31d3e285037446ba14bac54420bcab87cbebeaccab1f158b011e90132b7310e6c866e348cab68d21bad1d9f811b4d667a1e40bdef1a19259d3000b972ae06da958658b12ca4a45c2c6ea50d305be86f7e979fc962c687eb012f401a2c4bf0beb6c713343b94d65ee6d937660480daf670ab5b0ed421a9103e8a25119c836dd799600e67e4ec66ef680833398a99b16956aed2ee5bd9e6c8e68009a01145636e5e448d6c8ab902db394aa9c5aeee92dd150e1150d5b8f6674f330c279010912df27232ae126525a130ed8a9b4dee62eb57ce1d8a42ae9bcef867b10ce94012f94aa78846fcdda504243f83f3122aee5dc38dd32fa2a90224b3a40b4cf79c301129028709e39591c55e6e477a36cc26dc2efd7183fb59f51635dbd114dfed9d5010d66f31b781fa181c2114600ee1f03d55439355265700fb9d35ac684a19db398001e12558cc17e59fb85fb825481dee6fef3612c043bb38ca4833183da5381364600077a99d4d2df2f06067359f178a40f98a3e0943309c5ce3ce5398b9909174254012aa49cfc78ebec51c00e7824db0d8d41ef43fbb0396824d2c31b7db64124cc61000a8b51bd6c94a502f7aea3e5001f00d76b165ad580e48f8aa72813da8240286e00055f3480aa7cdb68a20d4b1e567d42389b44cf4e345da9e5a5655a80418695bc012c8f1e1ef8af646b4fb08afaeb3febde17c4c0d04b30dfd22d96a32ced9c9439010c40484e1547d307ad56b79bb7450e4eaaed7ed79bd5392dffd7449043f8783a0015104264f8d80356176ca6fdf4777e4ebc8edcbab50f8b6c366ac75ebd6bb5c701078d55053d2d41f21f51d5e617f282ba9dfa2576f0231048dd73998ceaa699fd0105821b61addd6935c7d80e65c706f0f2aa2d73ad22c5548c9c8b92cd82689dc40115f576c413a37014b9ee5377b121c69ad6e0f00f2b4f6c99d1f68b00a5d923370119b29fe6e9cf35f1344e24d52ee1fdb6521de3176f3d2b2a2b9327ae6e6cfbb10017198369a6ff180253700361c40e7f378c8e7ead6e04f01dbf5b4c47e7e1e4ff001abaf84628764eccbc5d0f66dc0a99505bb0e598362c45312470526afad11caf00242c6a902c564b3b363eb4ae08f643f18571779428358bfbdaf9856b0ebf229f0100000000000000000000000000000000000000000000000000000000000000001b7a78b6f573d4ac5d3a8d7d10dafac0dbc92604474417b29d40448d9fbc821d0fe3f73da41b27752d5db0658fcc0bff7da21112417ac719e491fdd43c8e5d0b07bcca22c27367a276e6a4e0cfb1bfbc772842898356ad4723101d4ada5137f409bb3b290d7519da3af15feb09e183e2be809690099551f1cb984deb1295b7611dd0cfd9000482314a388512d278fc22b2f6e3fc6e95023b4bd228e22626b7440119fa2c6f45a33b008ef5b23a5522ebbf0bf5f26d8263613a7f9b5ad9a192357600107a023453f7f79f413756dfba0096ae5d13158bbbb147089a020e819ef6640728acf01c811b967d1a14d767ac20088c5ca1613aa5d94ebe7fe2840b6431d8d002137dae183071d12cd9fb14c4b4738ef750ff514ce26257d1245eaedfea625516bfcb075a33a8910dc4e40742bfaa70d95591711e9db33d0db0be06cc819817072c22a9c81f4369f68bb1ef71c7f7458671d647caef18b30cca91da4f1b201d082a03306b4a07d1f0e1a8cc8844038fc524201a6c3a346a971203fd45458d292987d44049eecdac25c5622465a8d683a43621612d2fb283724b17a7bfa3bad2266a6fb1cf669eef21c90e24e85d6ab48e8b237355e964407dde00d65fb442dd210ce0378349f533c0e8ab65293de5319e246a044ed22b1e6db28add1508dd6f0da22954eea8a431b211a8b54147e32fec6593f9fb731389f62f94d5047e0f3301216576c71a79629bc3879325280e216d59cf7e6ff2d6a0babe418f8f2ed603ce010e16785f8c855043221ed5a603d4ed4e8e90059669543738ceb2a04dc996079c01264e9beaf4bb82e1b8f47bb0c239167064001e306eeef2b5495ec42754806b65012357f738543b13594ff081a070dbc0575645ac5ce8e0f89d3afdeb3d49e9ddae012a17d5095b9cef08f6eee2ce733dbf2cfcdb8c23e39410ed973e68cf8505266d00280c52b45752dd12ba84499618898111a8f9b19f2b76c06917e57eef221f3387011cc3360b478d144118a82a3c4391814f34aac2fb0f2d043394304bed179689e501292ca5631820f5f465242b4cc517f06c153c176a43f77daca463b7e2d3e2d7ba001aa0b2c90c3172cd2556137d78748f47924432449210d8b2f5e27adb6210bc77012c92a12526a5b0b1ef60994eb4d08e9bb5a3ea330d417838a52ad8636d37fa9e00131153c9affeefd6b98ecb6a80f5e6ea97417376030325182c064e12dd353c32011f4e25c49eea09adaa46d56642e2ea0afced2d75036acd3839435863f1ec3bf1002e19fe8456a7236c8fb77a15f87164debaad865e85c3468152a5deaffde3da6a01",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
vendor/github.com/caarlos0/env/v6/.github/FUNDING.yml
generated
vendored
Normal file
1
vendor/github.com/caarlos0/env/v6/.github/FUNDING.yml
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
github: [caarlos0]
|
18
vendor/github.com/caarlos0/env/v6/.github/dependabot.yml
generated
vendored
Normal file
18
vendor/github.com/caarlos0/env/v6/.github/dependabot.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
time: "08:00"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
- "automerge"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
time: "08:00"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
- "automerge"
|
61
vendor/github.com/caarlos0/env/v6/.github/workflows/build.yml
generated
vendored
Normal file
61
vendor/github.com/caarlos0/env/v6/.github/workflows/build.yml
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version: [~1.17]
|
||||||
|
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
-
|
||||||
|
name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go-version }}
|
||||||
|
-
|
||||||
|
name: Cache Go modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
-
|
||||||
|
name: CI
|
||||||
|
run: make setup ci
|
||||||
|
-
|
||||||
|
name: Upload coverage
|
||||||
|
uses: codecov/codecov-action@v2
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
file: ./coverage.txt
|
||||||
|
-
|
||||||
|
name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v2
|
||||||
|
if: success() && startsWith(github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest'
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
args: release --rm-dist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }}
|
||||||
|
TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }}
|
||||||
|
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
|
||||||
|
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
|
22
vendor/github.com/caarlos0/env/v6/.github/workflows/lint.yml
generated
vendored
Normal file
22
vendor/github.com/caarlos0/env/v6/.github/workflows/lint.yml
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: golangci-lint
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ~1.16
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
skip-go-installation: true
|
4
vendor/github.com/caarlos0/env/v6/.gitignore
generated
vendored
Normal file
4
vendor/github.com/caarlos0/env/v6/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
coverage.txt
|
||||||
|
bin
|
||||||
|
card.png
|
||||||
|
dist
|
8
vendor/github.com/caarlos0/env/v6/.golangci.yml
generated
vendored
Normal file
8
vendor/github.com/caarlos0/env/v6/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- thelper
|
||||||
|
- gofumpt
|
||||||
|
- tparallel
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
- wastedassign
|
3
vendor/github.com/caarlos0/env/v6/.goreleaser.yml
generated
vendored
Normal file
3
vendor/github.com/caarlos0/env/v6/.goreleaser.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
includes:
|
||||||
|
- from_url:
|
||||||
|
url: https://raw.githubusercontent.com/caarlos0/.goreleaserfiles/main/lib.yml
|
21
vendor/github.com/caarlos0/env/v6/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/caarlos0/env/v6/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2019 Carlos Alexandro Becker
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
37
vendor/github.com/caarlos0/env/v6/Makefile
generated
vendored
Normal file
37
vendor/github.com/caarlos0/env/v6/Makefile
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
SOURCE_FILES?=./...
|
||||||
|
TEST_PATTERN?=.
|
||||||
|
|
||||||
|
export GO111MODULE := on
|
||||||
|
|
||||||
|
setup:
|
||||||
|
go mod tidy
|
||||||
|
.PHONY: setup
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build
|
||||||
|
.PHONY: build
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
|
||||||
|
.PHONY: test
|
||||||
|
|
||||||
|
cover: test
|
||||||
|
go tool cover -html=coverage.txt
|
||||||
|
.PHONY: cover
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
|
||||||
|
.PHONY: fmt
|
||||||
|
|
||||||
|
lint:
|
||||||
|
./bin/golangci-lint run ./...
|
||||||
|
.PHONY: lint
|
||||||
|
|
||||||
|
ci: build test
|
||||||
|
.PHONY: ci
|
||||||
|
|
||||||
|
card:
|
||||||
|
wget -O card.png -c "https://og.caarlos0.dev/**env**: parse envs to structs.png?theme=light&md=1&fontSize=100px&images=https://github.com/caarlos0.png"
|
||||||
|
.PHONY: card
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := ci
|
459
vendor/github.com/caarlos0/env/v6/README.md
generated
vendored
Normal file
459
vendor/github.com/caarlos0/env/v6/README.md
generated
vendored
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
# env
|
||||||
|
|
||||||
|
[![Build Status](https://img.shields.io/github/workflow/status/caarlos0/env/build?style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build)
|
||||||
|
[![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
|
||||||
|
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v6)
|
||||||
|
|
||||||
|
Simple lib to parse envs to structs in Go.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Get the module with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get github.com/caarlos0/env/v6
|
||||||
|
```
|
||||||
|
|
||||||
|
The usage looks like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
Password string `env:"PASSWORD,unset"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||||
|
Duration time.Duration `env:"DURATION"`
|
||||||
|
TempFolder string `env:"TEMP_FOLDER" envDefault:"${HOME}/tmp" envExpand:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config{}
|
||||||
|
if err := env.Parse(&cfg); err != nil {
|
||||||
|
fmt.Printf("%+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v\n", cfg)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can run it like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go
|
||||||
|
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️⚠️⚠️ **Attention:** _unexported fields_ will be **ignored**.
|
||||||
|
|
||||||
|
## Supported types and defaults
|
||||||
|
|
||||||
|
Out of the box all built-in types are supported, plus a few others that
|
||||||
|
are commonly used.
|
||||||
|
|
||||||
|
Complete list:
|
||||||
|
|
||||||
|
- `string`
|
||||||
|
- `bool`
|
||||||
|
- `int`
|
||||||
|
- `int8`
|
||||||
|
- `int16`
|
||||||
|
- `int32`
|
||||||
|
- `int64`
|
||||||
|
- `uint`
|
||||||
|
- `uint8`
|
||||||
|
- `uint16`
|
||||||
|
- `uint32`
|
||||||
|
- `uint64`
|
||||||
|
- `float32`
|
||||||
|
- `float64`
|
||||||
|
- `string`
|
||||||
|
- `time.Duration`
|
||||||
|
- `encoding.TextUnmarshaler`
|
||||||
|
- `url.URL`
|
||||||
|
|
||||||
|
Pointers, slices and slices of pointers of those types are also supported.
|
||||||
|
|
||||||
|
You can also use/define a [custom parser func](#custom-parser-funcs) for any
|
||||||
|
other type you want.
|
||||||
|
|
||||||
|
If you set the `envDefault` tag for something, this value will be used in the
|
||||||
|
case of absence of it in the environment.
|
||||||
|
|
||||||
|
By default, slice types will split the environment value on `,`; you can change
|
||||||
|
this behavior by setting the `envSeparator` tag.
|
||||||
|
|
||||||
|
If you set the `envExpand` tag, environment variables (either in `${var}` or
|
||||||
|
`$var` format) in the string will be replaced according with the actual value
|
||||||
|
of the variable.
|
||||||
|
|
||||||
|
## Custom Parser Funcs
|
||||||
|
|
||||||
|
If you have a type that is not supported out of the box by the lib, you are able
|
||||||
|
to use (or define) and pass custom parsers (and their associated `reflect.Type`)
|
||||||
|
to the `env.ParseWithFuncs()` function.
|
||||||
|
|
||||||
|
In addition to accepting a struct pointer (same as `Parse()`), this function
|
||||||
|
also accepts a `map[reflect.Type]env.ParserFunc`.
|
||||||
|
|
||||||
|
`env` also ships with some pre-built custom parser funcs for common types. You
|
||||||
|
can check them out [here](parsers/).
|
||||||
|
|
||||||
|
If you add a custom parser for, say `Foo`, it will also be used to parse
|
||||||
|
`*Foo` and `[]Foo` types.
|
||||||
|
|
||||||
|
This directory contains pre-built, custom parsers that can be used with `env.ParseWithFuncs`
|
||||||
|
to facilitate the parsing of envs that are not basic types.
|
||||||
|
|
||||||
|
Check the example in the [go doc](http://godoc.org/github.com/caarlos0/env)
|
||||||
|
for more info.
|
||||||
|
|
||||||
|
### A note about `TextUnmarshaler` and `time.Time`
|
||||||
|
|
||||||
|
Env supports by default anything that implements the `TextUnmarshaler` interface.
|
||||||
|
That includes things like `time.Time` for example.
|
||||||
|
The upside is that depending on the format you need, you don't need to change anything.
|
||||||
|
The downside is that if you do need time in another format, you'll need to create your own type.
|
||||||
|
|
||||||
|
Its fairly straightforward:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyTime time.Time
|
||||||
|
|
||||||
|
func (t *MyTime) UnmarshalText(text []byte) error {
|
||||||
|
tt, err := time.Parse("2006-01-02", string(text))
|
||||||
|
*t = MyTime(tt)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
SomeTime MyTime `env:"SOME_TIME"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And then you can parse `Config` with `env.Parse`.
|
||||||
|
|
||||||
|
## Required fields
|
||||||
|
|
||||||
|
The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added to ensure that some environment variable is set.
|
||||||
|
In the example above, an error is returned if the `config` struct is changed to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type config struct {
|
||||||
|
SecretKey string `env:"SECRET_KEY,required"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Not Empty fields
|
||||||
|
|
||||||
|
While `required` demands the environment variable to be check, it doesn't check its value.
|
||||||
|
If you want to make sure the environment is set and not empty, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type config struct {
|
||||||
|
SecretKey string `env:"SECRET_KEY,notEmpty"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unset environment variable after reading it
|
||||||
|
|
||||||
|
The `env` tag option `unset` (e.g., `env:"tagKey,unset"`) can be added
|
||||||
|
to ensure that some environment variable is unset after reading it.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type config struct {
|
||||||
|
SecretKey string `env:"SECRET_KEY,unset"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## From file
|
||||||
|
|
||||||
|
The `env` tag option `file` (e.g., `env:"tagKey,file"`) can be added
|
||||||
|
to in order to indicate that the value of the variable shall be loaded from a file. The path of that file is given
|
||||||
|
by the environment variable associated with it
|
||||||
|
Example below
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Secret string `env:"SECRET,file"`
|
||||||
|
Password string `env:"PASSWORD,file" envDefault:"/tmp/password"`
|
||||||
|
Certificate string `env:"CERTIFICATE,file" envDefault:"${CERTIFICATE_FILE}" envExpand:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config{}
|
||||||
|
if err := env.Parse(&cfg); err != nil {
|
||||||
|
fmt.Printf("%+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v\n", cfg)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ echo qwerty > /tmp/secret
|
||||||
|
$ echo dvorak > /tmp/password
|
||||||
|
$ echo coleman > /tmp/certificate
|
||||||
|
|
||||||
|
$ SECRET=/tmp/secret \
|
||||||
|
CERTIFICATE_FILE=/tmp/certificate \
|
||||||
|
go run main.go
|
||||||
|
{Secret:qwerty Password:dvorak Certificate:coleman}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
|
||||||
|
By setting the `Options.Environment` map you can tell `Parse` to add those `keys` and `values`
|
||||||
|
as env vars before parsing is done. These envs are stored in the map and never actually set by `os.Setenv`.
|
||||||
|
This option effectively makes `env` ignore the OS environment variables: only the ones provided in the option are used.
|
||||||
|
|
||||||
|
This can make your testing scenarios a bit more clean and easy to handle.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Password string `env:"PASSWORD"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := &Config{}
|
||||||
|
opts := &env.Options{Environment: map[string]string{
|
||||||
|
"PASSWORD": "MY_PASSWORD",
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Load env vars.
|
||||||
|
if err := env.Parse(cfg, opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the loaded data.
|
||||||
|
fmt.Printf("%+v\n", cfg.envData)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Changing default tag name
|
||||||
|
|
||||||
|
You can change what tag name to use for setting the env vars by setting the `Options.TagName`
|
||||||
|
variable.
|
||||||
|
|
||||||
|
For example
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Password string `json:"PASSWORD"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := &Config{}
|
||||||
|
opts := &env.Options{TagName: "json"}
|
||||||
|
|
||||||
|
// Load env vars.
|
||||||
|
if err := env.Parse(cfg, opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the loaded data.
|
||||||
|
fmt.Printf("%+v\n", cfg.envData)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prefixes
|
||||||
|
|
||||||
|
You can prefix sub-structs env tags, as well as a whole `env.Parse` call.
|
||||||
|
|
||||||
|
Here's an example flexing it a bit:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComplexConfig struct {
|
||||||
|
Foo Config `envPrefix:"FOO_"`
|
||||||
|
Clean Config
|
||||||
|
Bar Config `envPrefix:"BAR_"`
|
||||||
|
Blah string `env:"BLAH"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := ComplexConfig{}
|
||||||
|
if err := Parse(&cfg, Options{
|
||||||
|
Prefix: "T_",
|
||||||
|
Environment: map[string]string{
|
||||||
|
"T_FOO_HOME": "/foo",
|
||||||
|
"T_BAR_HOME": "/bar",
|
||||||
|
"T_BLAH": "blahhh",
|
||||||
|
"T_HOME": "/clean",
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load env vars.
|
||||||
|
if err := env.Parse(cfg, opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the loaded data.
|
||||||
|
fmt.Printf("%+v\n", cfg.envData)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### On set hooks
|
||||||
|
|
||||||
|
You might want to listen to value sets and, for example, log something or do some other kind of logic.
|
||||||
|
You can do this by passing a `OnSet` option:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Username string `env:"USERNAME" envDefault:"admin"`
|
||||||
|
Password string `env:"PASSWORD"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := &Config{}
|
||||||
|
opts := &env.Options{
|
||||||
|
OnSet: func(tag string, value interface{}, isDefault bool) {
|
||||||
|
fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load env vars.
|
||||||
|
if err := env.Parse(cfg, opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the loaded data.
|
||||||
|
fmt.Printf("%+v\n", cfg.envData)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Making all fields to required
|
||||||
|
|
||||||
|
You can make all fields that don't have a default value be required by setting the `RequiredIfNoDef: true` in the `Options`.
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Username string `env:"USERNAME" envDefault:"admin"`
|
||||||
|
Password string `env:"PASSWORD"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := &Config{}
|
||||||
|
opts := &env.Options{RequiredIfNoDef: true}
|
||||||
|
|
||||||
|
// Load env vars.
|
||||||
|
if err := env.Parse(cfg, opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the loaded data.
|
||||||
|
fmt.Printf("%+v\n", cfg.envData)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Defaults from code
|
||||||
|
|
||||||
|
You may define default value also in code, by initialising the config data before it's filled by `env.Parse`.
|
||||||
|
Default values defined as struct tags will overwrite existing values during Parse.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Username string `env:"USERNAME" envDefault:"admin"`
|
||||||
|
Password string `env:"PASSWORD"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var cfg = Config{
|
||||||
|
Username: "test",
|
||||||
|
Password: "123456",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := env.Parse(&cfg); err != nil {
|
||||||
|
fmt.Println("failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v", cfg) // {Username:admin Password:123456}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stargazers over time
|
||||||
|
|
||||||
|
[![Stargazers over time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env)
|
477
vendor/github.com/caarlos0/env/v6/env.go
generated
vendored
Normal file
477
vendor/github.com/caarlos0/env/v6/env.go
generated
vendored
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nolint: gochecknoglobals
|
||||||
|
var (
|
||||||
|
// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
|
||||||
|
// Struct to Parse.
|
||||||
|
ErrNotAStructPtr = errors.New("env: expected a pointer to a Struct")
|
||||||
|
|
||||||
|
defaultBuiltInParsers = map[reflect.Kind]ParserFunc{
|
||||||
|
reflect.Bool: func(v string) (interface{}, error) {
|
||||||
|
return strconv.ParseBool(v)
|
||||||
|
},
|
||||||
|
reflect.String: func(v string) (interface{}, error) {
|
||||||
|
return v, nil
|
||||||
|
},
|
||||||
|
reflect.Int: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseInt(v, 10, 32)
|
||||||
|
return int(i), err
|
||||||
|
},
|
||||||
|
reflect.Int16: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseInt(v, 10, 16)
|
||||||
|
return int16(i), err
|
||||||
|
},
|
||||||
|
reflect.Int32: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseInt(v, 10, 32)
|
||||||
|
return int32(i), err
|
||||||
|
},
|
||||||
|
reflect.Int64: func(v string) (interface{}, error) {
|
||||||
|
return strconv.ParseInt(v, 10, 64)
|
||||||
|
},
|
||||||
|
reflect.Int8: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseInt(v, 10, 8)
|
||||||
|
return int8(i), err
|
||||||
|
},
|
||||||
|
reflect.Uint: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseUint(v, 10, 32)
|
||||||
|
return uint(i), err
|
||||||
|
},
|
||||||
|
reflect.Uint16: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseUint(v, 10, 16)
|
||||||
|
return uint16(i), err
|
||||||
|
},
|
||||||
|
reflect.Uint32: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseUint(v, 10, 32)
|
||||||
|
return uint32(i), err
|
||||||
|
},
|
||||||
|
reflect.Uint64: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseUint(v, 10, 64)
|
||||||
|
return i, err
|
||||||
|
},
|
||||||
|
reflect.Uint8: func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.ParseUint(v, 10, 8)
|
||||||
|
return uint8(i), err
|
||||||
|
},
|
||||||
|
reflect.Float64: func(v string) (interface{}, error) {
|
||||||
|
return strconv.ParseFloat(v, 64)
|
||||||
|
},
|
||||||
|
reflect.Float32: func(v string) (interface{}, error) {
|
||||||
|
f, err := strconv.ParseFloat(v, 32)
|
||||||
|
return float32(f), err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTypeParsers = map[reflect.Type]ParserFunc{
|
||||||
|
reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) {
|
||||||
|
u, err := url.Parse(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse URL: %v", err)
|
||||||
|
}
|
||||||
|
return *u, nil
|
||||||
|
},
|
||||||
|
reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) {
|
||||||
|
s, err := time.ParseDuration(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse duration: %v", err)
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParserFunc defines the signature of a function that can be used within `CustomParsers`.
|
||||||
|
type ParserFunc func(v string) (interface{}, error)
|
||||||
|
|
||||||
|
// OnSetFn is a hook that can be run when a value is set.
|
||||||
|
type OnSetFn func(tag string, value interface{}, isDefault bool)
|
||||||
|
|
||||||
|
// Options for the parser.
|
||||||
|
type Options struct {
|
||||||
|
// Environment keys and values that will be accessible for the service.
|
||||||
|
Environment map[string]string
|
||||||
|
|
||||||
|
// TagName specifies another tagname to use rather than the default env.
|
||||||
|
TagName string
|
||||||
|
|
||||||
|
// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault'
|
||||||
|
RequiredIfNoDef bool
|
||||||
|
|
||||||
|
// OnSet allows to run a function when a value is set
|
||||||
|
OnSet OnSetFn
|
||||||
|
|
||||||
|
// Prefix define a prefix for each key
|
||||||
|
Prefix string
|
||||||
|
|
||||||
|
// Sets to true if we have already configured once.
|
||||||
|
configured bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure will do the basic configurations and defaults.
|
||||||
|
func configure(opts []Options) []Options {
|
||||||
|
// If we have already configured the first item
|
||||||
|
// of options will have been configured set to true.
|
||||||
|
if len(opts) > 0 && opts[0].configured {
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Created options with defaults.
|
||||||
|
opt := Options{
|
||||||
|
TagName: "env",
|
||||||
|
Environment: toMap(os.Environ()),
|
||||||
|
configured: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over all opts structs and set
|
||||||
|
// to opt if value is not default/empty.
|
||||||
|
for _, item := range opts {
|
||||||
|
if item.Environment != nil {
|
||||||
|
opt.Environment = item.Environment
|
||||||
|
}
|
||||||
|
if item.TagName != "" {
|
||||||
|
opt.TagName = item.TagName
|
||||||
|
}
|
||||||
|
if item.OnSet != nil {
|
||||||
|
opt.OnSet = item.OnSet
|
||||||
|
}
|
||||||
|
if item.Prefix != "" {
|
||||||
|
opt.Prefix = item.Prefix
|
||||||
|
}
|
||||||
|
opt.RequiredIfNoDef = item.RequiredIfNoDef
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Options{opt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOnSetFn(opts []Options) OnSetFn {
|
||||||
|
return opts[0].OnSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTagName returns the tag name.
|
||||||
|
func getTagName(opts []Options) string {
|
||||||
|
return opts[0].TagName
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnvironment returns the environment map.
|
||||||
|
func getEnvironment(opts []Options) map[string]string {
|
||||||
|
return opts[0].Environment
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses a struct containing `env` tags and loads its values from
|
||||||
|
// environment variables.
|
||||||
|
func Parse(v interface{}, opts ...Options) error {
|
||||||
|
return ParseWithFuncs(v, map[reflect.Type]ParserFunc{}, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
|
||||||
|
// in custom parsers.
|
||||||
|
func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...Options) error {
|
||||||
|
opts = configure(opts)
|
||||||
|
|
||||||
|
ptrRef := reflect.ValueOf(v)
|
||||||
|
if ptrRef.Kind() != reflect.Ptr {
|
||||||
|
return ErrNotAStructPtr
|
||||||
|
}
|
||||||
|
ref := ptrRef.Elem()
|
||||||
|
if ref.Kind() != reflect.Struct {
|
||||||
|
return ErrNotAStructPtr
|
||||||
|
}
|
||||||
|
parsers := defaultTypeParsers
|
||||||
|
for k, v := range funcMap {
|
||||||
|
parsers[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return doParse(ref, parsers, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error {
|
||||||
|
refType := ref.Type()
|
||||||
|
|
||||||
|
for i := 0; i < refType.NumField(); i++ {
|
||||||
|
refField := ref.Field(i)
|
||||||
|
if !refField.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if reflect.Ptr == refField.Kind() && !refField.IsNil() {
|
||||||
|
err := ParseWithFuncs(refField.Interface(), funcMap, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" {
|
||||||
|
err := Parse(refField.Addr().Interface(), opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
refTypeField := refType.Field(i)
|
||||||
|
value, err := get(refTypeField, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
|
if reflect.Struct == refField.Kind() {
|
||||||
|
subOpts := make([]Options, len(opts))
|
||||||
|
copy(subOpts, opts)
|
||||||
|
if prefix := refType.Field(i).Tag.Get("envPrefix"); prefix != "" {
|
||||||
|
subOpts[0].Prefix += prefix
|
||||||
|
}
|
||||||
|
if err := doParse(refField, funcMap, subOpts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := set(refField, refTypeField, value, funcMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(field reflect.StructField, opts []Options) (val string, err error) {
|
||||||
|
var exists bool
|
||||||
|
var isDefault bool
|
||||||
|
var loadFile bool
|
||||||
|
var unset bool
|
||||||
|
var notEmpty bool
|
||||||
|
|
||||||
|
required := opts[0].RequiredIfNoDef
|
||||||
|
prefix := opts[0].Prefix
|
||||||
|
key, tags := parseKeyForOption(field.Tag.Get(getTagName(opts)))
|
||||||
|
key = prefix + key
|
||||||
|
for _, tag := range tags {
|
||||||
|
switch tag {
|
||||||
|
case "":
|
||||||
|
continue
|
||||||
|
case "file":
|
||||||
|
loadFile = true
|
||||||
|
case "required":
|
||||||
|
required = true
|
||||||
|
case "unset":
|
||||||
|
unset = true
|
||||||
|
case "notEmpty":
|
||||||
|
notEmpty = true
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("env: tag option %q not supported", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expand := strings.EqualFold(field.Tag.Get("envExpand"), "true")
|
||||||
|
defaultValue, defExists := field.Tag.Lookup("envDefault")
|
||||||
|
val, exists, isDefault = getOr(key, defaultValue, defExists, getEnvironment(opts))
|
||||||
|
|
||||||
|
if expand {
|
||||||
|
val = os.ExpandEnv(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if unset {
|
||||||
|
defer os.Unsetenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if required && !exists && len(key) > 0 {
|
||||||
|
return "", fmt.Errorf(`env: required environment variable %q is not set`, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if notEmpty && val == "" {
|
||||||
|
return "", fmt.Errorf("env: environment variable %q should not be empty", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loadFile && val != "" {
|
||||||
|
filename := val
|
||||||
|
val, err = getFromFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(`env: could not load content of file "%s" from variable %s: %v`, filename, key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if onSetFn := getOnSetFn(opts); onSetFn != nil {
|
||||||
|
onSetFn(key, val, isDefault)
|
||||||
|
}
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// split the env tag's key into the expected key and desired option, if any.
|
||||||
|
func parseKeyForOption(key string) (string, []string) {
|
||||||
|
opts := strings.Split(key, ",")
|
||||||
|
return opts[0], opts[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFromFile(filename string) (value string, err error) {
|
||||||
|
b, err := os.ReadFile(filename)
|
||||||
|
return string(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOr(key, defaultValue string, defExists bool, envs map[string]string) (string, bool, bool) {
|
||||||
|
value, exists := envs[key]
|
||||||
|
switch {
|
||||||
|
case (!exists || key == "") && defExists:
|
||||||
|
return defaultValue, true, true
|
||||||
|
case !exists:
|
||||||
|
return "", false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, true, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(field reflect.Value, sf reflect.StructField, value string, funcMap map[reflect.Type]ParserFunc) error {
|
||||||
|
if tm := asTextUnmarshaler(field); tm != nil {
|
||||||
|
if err := tm.UnmarshalText([]byte(value)); err != nil {
|
||||||
|
return newParseError(sf, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
typee := sf.Type
|
||||||
|
fieldee := field
|
||||||
|
if typee.Kind() == reflect.Ptr {
|
||||||
|
typee = typee.Elem()
|
||||||
|
fieldee = field.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
parserFunc, ok := funcMap[typee]
|
||||||
|
if ok {
|
||||||
|
val, err := parserFunc(value)
|
||||||
|
if err != nil {
|
||||||
|
return newParseError(sf, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldee.Set(reflect.ValueOf(val))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
|
||||||
|
if ok {
|
||||||
|
val, err := parserFunc(value)
|
||||||
|
if err != nil {
|
||||||
|
return newParseError(sf, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldee.Set(reflect.ValueOf(val).Convert(typee))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.Kind() == reflect.Slice {
|
||||||
|
return handleSlice(field, value, sf, funcMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNoParserError(sf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSlice(field reflect.Value, value string, sf reflect.StructField, funcMap map[reflect.Type]ParserFunc) error {
|
||||||
|
separator := sf.Tag.Get("envSeparator")
|
||||||
|
if separator == "" {
|
||||||
|
separator = ","
|
||||||
|
}
|
||||||
|
parts := strings.Split(value, separator)
|
||||||
|
|
||||||
|
typee := sf.Type.Elem()
|
||||||
|
if typee.Kind() == reflect.Ptr {
|
||||||
|
typee = typee.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := reflect.New(typee).Interface().(encoding.TextUnmarshaler); ok {
|
||||||
|
return parseTextUnmarshalers(field, parts, sf)
|
||||||
|
}
|
||||||
|
|
||||||
|
parserFunc, ok := funcMap[typee]
|
||||||
|
if !ok {
|
||||||
|
parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
|
||||||
|
if !ok {
|
||||||
|
return newNoParserError(sf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := reflect.MakeSlice(sf.Type, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
r, err := parserFunc(part)
|
||||||
|
if err != nil {
|
||||||
|
return newParseError(sf, err)
|
||||||
|
}
|
||||||
|
v := reflect.ValueOf(r).Convert(typee)
|
||||||
|
if sf.Type.Elem().Kind() == reflect.Ptr {
|
||||||
|
v = reflect.New(typee)
|
||||||
|
v.Elem().Set(reflect.ValueOf(r).Convert(typee))
|
||||||
|
}
|
||||||
|
result = reflect.Append(result, v)
|
||||||
|
}
|
||||||
|
field.Set(result)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler {
|
||||||
|
if reflect.Ptr == field.Kind() {
|
||||||
|
if field.IsNil() {
|
||||||
|
field.Set(reflect.New(field.Type().Elem()))
|
||||||
|
}
|
||||||
|
} else if field.CanAddr() {
|
||||||
|
field = field.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
tm, ok := field.Interface().(encoding.TextUnmarshaler)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tm
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.StructField) error {
|
||||||
|
s := len(data)
|
||||||
|
elemType := field.Type().Elem()
|
||||||
|
slice := reflect.MakeSlice(reflect.SliceOf(elemType), s, s)
|
||||||
|
for i, v := range data {
|
||||||
|
sv := slice.Index(i)
|
||||||
|
kind := sv.Kind()
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
sv = reflect.New(elemType.Elem())
|
||||||
|
} else {
|
||||||
|
sv = sv.Addr()
|
||||||
|
}
|
||||||
|
tm := sv.Interface().(encoding.TextUnmarshaler)
|
||||||
|
if err := tm.UnmarshalText([]byte(v)); err != nil {
|
||||||
|
return newParseError(sf, err)
|
||||||
|
}
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
slice.Index(i).Set(sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field.Set(slice)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newParseError(sf reflect.StructField, err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return parseError{
|
||||||
|
sf: sf,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseError struct {
|
||||||
|
sf reflect.StructField
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e parseError) Error() string {
|
||||||
|
return fmt.Sprintf(`env: parse error on field "%s" of type "%s": %v`, e.sf.Name, e.sf.Type, e.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNoParserError(sf reflect.StructField) error {
|
||||||
|
return fmt.Errorf(`env: no parser found for field "%s" of type "%s"`, sf.Name, sf.Type)
|
||||||
|
}
|
1477
vendor/github.com/caarlos0/env/v6/env_test.go
generated
vendored
Normal file
1477
vendor/github.com/caarlos0/env/v6/env_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
vendor/github.com/caarlos0/env/v6/env_unix.go
generated
vendored
Normal file
15
vendor/github.com/caarlos0/env/v6/env_unix.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
|
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package env
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func toMap(env []string) map[string]string {
|
||||||
|
r := map[string]string{}
|
||||||
|
for _, e := range env {
|
||||||
|
p := strings.SplitN(e, "=", 2)
|
||||||
|
r[p[0]] = p[1]
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
25
vendor/github.com/caarlos0/env/v6/env_windows.go
generated
vendored
Normal file
25
vendor/github.com/caarlos0/env/v6/env_windows.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func toMap(env []string) map[string]string {
|
||||||
|
r := map[string]string{}
|
||||||
|
for _, e := range env {
|
||||||
|
p := strings.SplitN(e, "=", 2)
|
||||||
|
|
||||||
|
// On Windows, environment variables can start with '='. If so, Split at next character.
|
||||||
|
// See env_windows.go in the Go source: https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L58
|
||||||
|
prefixEqualSign := false
|
||||||
|
if len(e) > 0 && e[0] == '=' {
|
||||||
|
e = e[1:]
|
||||||
|
prefixEqualSign = true
|
||||||
|
}
|
||||||
|
p = strings.SplitN(e, "=", 2)
|
||||||
|
if prefixEqualSign {
|
||||||
|
p[0] = "=" + p[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
r[p[0]] = p[1]
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
20
vendor/github.com/caarlos0/env/v6/env_windows_test.go
generated
vendored
Normal file
20
vendor/github.com/caarlos0/env/v6/env_windows_test.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
// On Windows, environment variables can start with '='. This test verifies this behavior without relying on a Windows environment.
|
||||||
|
// See env_windows.go in the Go source: https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L58
|
||||||
|
func TestToMapWindows(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
envVars := []string{"=::=::\\", "=C:=C:\\test", "VAR=REGULARVAR"}
|
||||||
|
result := toMap(envVars)
|
||||||
|
is.Equal(map[string]string{
|
||||||
|
"=::": "::\\",
|
||||||
|
"=C:": "C:\\test",
|
||||||
|
"VAR": "REGULARVAR",
|
||||||
|
}, result)
|
||||||
|
}
|
5
vendor/github.com/caarlos0/env/v6/go.mod
generated
vendored
Normal file
5
vendor/github.com/caarlos0/env/v6/go.mod
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module github.com/caarlos0/env/v6
|
||||||
|
|
||||||
|
require github.com/matryer/is v1.4.0
|
||||||
|
|
||||||
|
go 1.17
|
2
vendor/github.com/caarlos0/env/v6/go.sum
generated
vendored
Normal file
2
vendor/github.com/caarlos0/env/v6/go.sum
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||||
|
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
Loading…
Reference in New Issue
Block a user