Compare commits

..
57 Commits
Author SHA1 Message Date
Ville SkyttäandMárk Sági-Kazár f62f86a84b refactor: make use of strings.Cut 2023-10-06 14:22:22 +02:00
Étienne BERSACandMárk Sági-Kazár 94632fa21e chore: Use pip3 explicitly to install yamllint
Distribution stends to skip unversionned pip for compatibility issues.
Also, yamllint requires Python 3.8+.

Signed-off-by: Étienne BERSAC <etienne.bersac@dalibo.com>
2023-10-06 14:12:00 +02:00
Étienne BERSACandMárk Sági-Kazár 3f6cadcbeb chore: Fix copy-paste error for yamllint target
Signed-off-by: Étienne BERSAC <etienne.bersac@dalibo.com>
2023-10-06 14:12:00 +02:00
yhliyrandMárk Sági-Kazár 287507c0b5 docs: add set subset KV example 2023-10-06 14:09:32 +02:00
Mark Sagi-KazarandMárk Sági-Kazár f1cb2262bb chore(deps): update crypt
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-10-05 22:52:58 +02:00
Oleksandr RedkoandMárk Sági-Kazár c292b55050 test: refactor asserts 2023-10-05 00:58:18 +02:00
Oleksandr RedkoandMárk Sági-Kazár 3d006fe361 refactor: replace interface{} with any 2023-10-01 17:04:26 +02:00
dependabot[bot]andMárk Sági-Kazár 8a6dc5d43c build(deps): bump github/codeql-action from 2.21.8 to 2.21.9
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.8 to 2.21.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/6a28655e3dcb49cb0840ea372fd6d17733edd8a4...ddccb873888234080b77e9bc2d4764d5ccaaccf9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-01 15:39:02 +02:00
Oleksandr RedkoandMárk Sági-Kazár 96c5c0083f chore: remove deprecated build tags 2023-09-25 13:19:04 +02:00
dependabot[bot]andMárk Sági-Kazár 44911d2cac build(deps): bump github/codeql-action from 2.21.7 to 2.21.8
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.7 to 2.21.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/04daf014b50eaf774287bf3f0f1869d4b4c4b913...6a28655e3dcb49cb0840ea372fd6d17733edd8a4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 12:34:26 +02:00
dependabot[bot]andMárk Sági-Kazár a6f8e227bb build(deps): bump github.com/spf13/afero from 1.9.5 to 1.10.0
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.9.5 to 1.10.0.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.9.5...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/afero
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 12:34:09 +02:00
dependabot[bot]andMárk Sági-Kazár a9437f2500 build(deps): bump actions/checkout from 4.0.0 to 4.1.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/3df4ab11eba7bda6032a0b82a6bb43b11571feac...8ade135a41bc03ea155e62e844d188df1ea18608)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 12:33:39 +02:00
Mark Sagi-KazarandMárk Sági-Kazár cae55fefc9 fix: result order by upgrading locafero
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-16 14:01:33 +02:00
Mark Sagi-KazarandMárk Sági-Kazár f2e0e48e6b fix: compile error
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-16 14:01:33 +02:00
Mark Sagi-KazarandMárk Sági-Kazár db5aafac4d feat: enable finder tag builds
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-16 14:01:33 +02:00
Mark Sagi-KazarandMárk Sági-Kazár abbfd91119 feat: replace io fs finder with afero finder
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-16 14:01:33 +02:00
Mark Sagi-KazarandMárk Sági-Kazár cfa8fd9b4f feat: use new finder library
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-16 14:01:33 +02:00
Oleksandr RedkoandMárk Sági-Kazár 6fdfebc843 Fix tests on Windows 2023-09-16 13:48:38 +02:00
Oleksandr RedkoandMárk Sági-Kazár 4aeec5882c Remove usages of deprecated io/ioutil; simplify tests 2023-09-16 13:48:38 +02:00
dependabot[bot]andMárk Sági-Kazár f683416bfb build(deps): bump github/codeql-action from 2.21.6 to 2.21.7
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.6 to 2.21.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/701f152f28d4350ad289a5e31435e9ab6169a7ca...04daf014b50eaf774287bf3f0f1869d4b4c4b913)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-16 13:47:30 +02:00
dependabot[bot]andMárk Sági-Kazár 336e9f0a93 build(deps): bump github/codeql-action from 2.21.5 to 2.21.6
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.5 to 2.21.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/00e563ead9f72a8461b24876bee2d0c2e8bd2ee8...701f152f28d4350ad289a5e31435e9ab6169a7ca)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-13 23:05:26 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 58b177a8c0 fix: lint violations
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-13 16:24:59 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 2672f3e4d3 refactor: drop unused jww code
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-13 16:24:59 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 0c5594ae7a feat: deprecate logger interface
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-13 16:24:59 +02:00
Mark Sagi-KazarandMárk Sági-Kazár d16deb4b93 feat: add slog support
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-13 16:24:59 +02:00
Mark Sagi-KazarandMárk Sági-Kazár d4c2f2ef40 feat!: bump minimum Go version to 1.19
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-12 00:17:53 +02:00
dependabot[bot]andMárk Sági-Kazár c78218cb99 build(deps): bump actions/dependency-review-action from 3.0.8 to 3.1.0
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3.0.8 to 3.1.0.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/f6fff72a3217f580d5afd49a46826795305b63c7...6c5ccdad469c9f8a2996bfecaec55a631a347034)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-09 07:55:48 +02:00
John HooksandMárk Sági-Kazár 1e811d1f02 Add NATS support 2023-09-08 15:32:50 +02:00
Mark Sagi-KazarandMárk Sági-Kazár d5c5c83bfc ci: add Go 1.21 to the test matrix
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-08 15:30:13 +02:00
Mark Sagi-KazarandMárk Sági-Kazár b425370376 chore(deps): update crypt
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-09-08 15:29:58 +02:00
dependabot[bot]andMárk Sági-Kazár 27914f862f build(deps): bump cachix/install-nix-action from 22 to 23
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 22 to 23.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/6ed004b9ccb68dbc28e7c85bee15fa93dbd214ac...6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-08 14:23:57 +02:00
dependabot[bot]andMárk Sági-Kazár 3718c2ec5a build(deps): bump github.com/subosito/gotenv from 1.4.2 to 1.6.0
Bumps [github.com/subosito/gotenv](https://github.com/subosito/gotenv) from 1.4.2 to 1.6.0.
- [Changelog](https://github.com/subosito/gotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/subosito/gotenv/compare/v1.4.2...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/subosito/gotenv
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-08 14:23:33 +02:00
Oleksandr RedkoandMárk Sági-Kazár 13dbfcafff test: remove testutil.Setenv as not needed from Go 1.17 2023-09-07 13:33:21 +02:00
Oleksandr RedkoandMárk Sági-Kazár a0696bbc97 docs: fix typos in comments 2023-09-07 13:32:51 +02:00
dependabot[bot]andMárk Sági-Kazár c63105849f build(deps): bump actions/setup-go from 4.0.1 to 4.1.0
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/fac708d6674e30b6ba41289acaab6d4b75aa0753...93397bea11091df50f3d7e59dc26a7711a8bcfbe)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 12:53:14 +02:00
dependabot[bot]andMárk Sági-Kazár 2bd8c8e48d build(deps): bump golangci/golangci-lint-action from 3.6.0 to 3.7.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/639cd343e1d3b897ff35927a75193d57cfcba299...3a919529898de77ec3da873e3063ca4b10e7f5cc)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 12:27:58 +02:00
dependabot[bot]andMárk Sági-Kazár 0195f11e50 build(deps): bump actions/dependency-review-action from 3.0.6 to 3.0.8
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3.0.6 to 3.0.8.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/1360a344ccb0ab6e9475edef90ad2f46bf8003b1...f6fff72a3217f580d5afd49a46826795305b63c7)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 12:27:42 +02:00
dependabot[bot]andMárk Sági-Kazár 64661d4342 build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.9 to 2.1.0
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.9 to 2.1.0.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.0.9...v2.1.0)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 12:27:28 +02:00
dependabot[bot]andMárk Sági-Kazár 59cd02f67b build(deps): bump github/codeql-action from 2.21.2 to 2.21.5
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.2 to 2.21.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/0ba4244466797eb048eb91a6cd43d5c03ca8bd05...00e563ead9f72a8461b24876bee2d0c2e8bd2ee8)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 12:27:14 +02:00
dependabot[bot]andMárk Sági-Kazár 7b2db61200 build(deps): bump actions/checkout from 3.5.3 to 4.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 4.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/c85c95e3d7251135ab7dc9ce3241c5835cc595a9...3df4ab11eba7bda6032a0b82a6bb43b11571feac)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 12:27:00 +02:00
Andres TaylorandMárk Sági-Kazár 24cfadcf0f Add Vitess to list of projects using Viper
Signed-off-by: Andres Taylor <andres@planetscale.com>
2023-09-07 12:26:29 +02:00
StepSecurity BotandMárk Sági-Kazár b77f4c1928 [StepSecurity] ci: Harden GitHub Actions
Signed-off-by: StepSecurity Bot <bot@stepsecurity.io>
2023-08-04 11:38:34 +02:00
guangwuandGitHub cb9b2bffc2 fix: code optimization (#1557)
* fix: code optimization

* fix: golangci-lint
2023-07-27 20:56:32 +02:00
Mark Sagi-KazarandMárk Sági-Kazár c5102bdba0 chore: upgrade crypt
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-07-21 16:13:01 +02:00
dependabot[bot]andMárk Sági-Kazár 706ccb68a4 build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.8 to 2.0.9
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.8 to 2.0.9.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.0.8...v2.0.9)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-15 12:35:25 +02:00
dependabot[bot]andMárk Sági-Kazár 097e0d888f build(deps): bump cachix/install-nix-action from 21 to 22
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 21 to 22.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/4b933aa7ebcc94a6174cf1364864e957b4910265...6ed004b9ccb68dbc28e7c85bee15fa93dbd214ac)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-29 06:50:12 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 961c39613b ci: add misc release note label
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 06:26:02 +02:00
Mark Sagi-KazarandMárk Sági-Kazár bb60a768c5 chore: improve dev env
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 06:26:02 +02:00
Mark Sagi-KazarandMárk Sági-Kazár bec1fce61d ci: update golangci-lint
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 06:26:02 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 76acbbdbb2 feat: drop support for Go 1.17
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 05:58:30 +02:00
Baruch OdemandMárk Sági-Kazár 731a91be43 feat: add func GetEnvPrefix 2023-06-29 05:37:45 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 31af6d09c2 ci: rename yml to yaml
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 05:37:25 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 74c53b7580 chore: update pr checks
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 05:37:25 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 351365dbe1 chore: add local development environment using nix
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-06-29 05:37:25 +02:00
dependabot[bot]andMárk Sági-Kazár a362607d5f build(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-28 18:59:50 +02:00
dependabot[bot]andMárk Sági-Kazár 5f51413ed2 build(deps): bump mheap/github-action-required-labels from 4 to 5
Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 4 to 5.
- [Release notes](https://github.com/mheap/github-action-required-labels/releases)
- [Commits](https://github.com/mheap/github-action-required-labels/compare/v4...v5)

---
updated-dependencies:
- dependency-name: mheap/github-action-required-labels
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-28 18:59:38 +02:00
goldenegggandMárk Sági-Kazár 0520e750b5 swap the order of actions/checkout@v3 and actions/setup-go@v4 2023-06-04 02:35:38 +02:00
56 changed files with 1105 additions and 2008 deletions
+3
View File
@@ -13,3 +13,6 @@ indent_style = tab
[{Makefile,*.mk}]
indent_style = tab
[*.nix]
indent_size = 2
+4
View File
@@ -0,0 +1,4 @@
if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8="
fi
use flake . --impure
+14 -2
View File
@@ -11,8 +11,20 @@ jobs:
steps:
- name: Check minimum labels
uses: mheap/github-action-required-labels@v4
uses: mheap/github-action-required-labels@4e9ef4ce8c697cf55716ecbf7f13a3d9e0b6ac6a # v5.1.0
with:
mode: minimum
count: 1
labels: "release-note/ignore, kind/feature, release-note/new-feature, kind/enhancement, release-note/enhancement, kind/bug, release-note/bug-fix, release-note/breaking-change, release-note/deprecation, area/dependencies, release-note/dependency-update"
labels: |
release-note/ignore
kind/feature
release-note/new-feature
kind/enhancement
release-note/enhancement
kind/bug
release-note/bug-fix
release-note/breaking-change
release-note/deprecation
area/dependencies
release-note/dependency-update
release-note/misc
+57 -27
View File
@@ -2,14 +2,14 @@ name: CI
on:
push:
branches:
- master
branches: [master]
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
@@ -20,13 +20,13 @@ jobs:
goarch: ppc64
steps:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: '1.21'
- name: Build
run: go build .
@@ -37,49 +37,79 @@ jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
# Fail fast is disabled because there are Go version specific features and tests
# that should be able to fail independently.
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.17', '1.18', '1.19', '1.20']
env:
GOFLAGS: -mod=readonly
go: ['1.19', '1.20', '1.21']
tags: ['', 'finder']
steps:
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v3
- name: Test
run: go test -race -v ./...
run: go test -race -v -tags '${{ matrix.tags }}' ./...
if: runner.os != 'Windows'
- name: Test (without race detector)
run: go test -v ./...
run: go test -v -tags '${{ matrix.tags }}' ./...
if: runner.os == 'Windows'
lint:
name: Lint
runs-on: ubuntu-latest
env:
GOFLAGS: -mod=readonly
steps:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: '1.21'
- name: Lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
with:
version: v1.52.2
version: v1.54.2
dev:
name: Developer environment
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set up Nix
uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Check
run: nix flake check --impure
- name: Dev shell
run: nix develop --impure
dependency-review:
name: Dependency review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Dependency Review
uses: actions/dependency-review-action@6c5ccdad469c9f8a2996bfecaec55a631a347034 # v3.1.0
+4 -4
View File
@@ -39,11 +39,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -68,5 +68,5 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
+3
View File
@@ -1,4 +1,7 @@
/.devenv/
/.direnv/
/.idea/
/.pre-commit-config.yaml
/bin/
/build/
/var/
+2
View File
@@ -0,0 +1,2 @@
# TODO: FIXME
/.github/
+6
View File
@@ -0,0 +1,6 @@
ignore-from-file: [.gitignore, .yamlignore]
extends: default
rules:
line-length: disable
+29 -18
View File
@@ -16,7 +16,7 @@ endif
# Dependency versions
GOTESTSUM_VERSION = 1.9.0
GOLANGCI_VERSION = 1.52.2
GOLANGCI_VERSION = 1.53.3
# Add the ability to override some variables
# Use with care
@@ -29,11 +29,6 @@ clear: ## Clear the working area and the project
.PHONY: check
check: test lint ## Run tests and linters
bin/gotestsum: bin/gotestsum-${GOTESTSUM_VERSION}
@ln -sf gotestsum-${GOTESTSUM_VERSION} bin/gotestsum
bin/gotestsum-${GOTESTSUM_VERSION}:
@mkdir -p bin
curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum-${GOTESTSUM_VERSION} && chmod +x ./bin/gotestsum-${GOTESTSUM_VERSION}
TEST_PKGS ?= ./...
.PHONY: test
@@ -44,20 +39,36 @@ test: bin/gotestsum ## Run tests
@mkdir -p ${BUILD_DIR}
bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...)
bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
bin/golangci-lint-${GOLANGCI_VERSION}:
@mkdir -p bin
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
@mv bin/golangci-lint "$@"
.PHONY: lint
lint: bin/golangci-lint ## Run linter
bin/golangci-lint run
lint: lint-go lint-yaml
lint: ## Run linters
.PHONY: fix
fix: bin/golangci-lint ## Fix lint violations
bin/golangci-lint run --fix
.PHONY: lint-go
lint-go:
golangci-lint run $(if ${CI},--out-format github-actions,)
.PHONY: lint-yaml
lint-yaml:
yamllint $(if ${CI},-f github,) --no-warnings .
.PHONY: fmt
fmt: ## Format code
golangci-lint run --fix
deps: bin/golangci-lint bin/gotestsum yamllint
deps: ## Install dependencies
bin/gotestsum:
@mkdir -p bin
curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum && chmod +x ./bin/gotestsum
bin/golangci-lint:
@mkdir -p bin
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- v${GOLANGCI_VERSION}
.PHONY: yamllint
yamllint:
pip3 install --user yamllint
# Add custom targets here
-include custom.mk
+50 -11
View File
@@ -11,7 +11,7 @@
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.19-61CFDD.svg?style=flat-square)
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
**Go configuration with fangs!**
@@ -30,6 +30,7 @@ Many Go projects are built using Viper including:
* [Meshery](https://github.com/meshery/meshery)
* [Bearer](https://github.com/bearer/bearer)
* [Coder](https://github.com/coder/coder)
* [Vitess](https://vitess.io/)
## Install
@@ -140,7 +141,7 @@ if err := viper.ReadInConfig(); err != nil {
// Config file found and successfully parsed
```
*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc`
*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmatically. For those configuration files that lie in the home of the user without any extension like `.bashrc`
### Writing Config Files
@@ -221,6 +222,7 @@ These could be from a command line flag, or from your own application logic.
```go
viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)
viper.Set("host.port", 5899) // set subset
```
### Registering and Using Aliases
@@ -487,6 +489,15 @@ err := viper.ReadRemoteConfig()
Of course, you're allowed to use `SecureRemoteProvider` also
#### NATS
```go
viper.AddRemoteProvider("nats", "nats://127.0.0.1:4222", "myapp.config")
viper.SetConfigType("json")
err := viper.ReadRemoteConfig()
```
### Remote Key/Value Store Example - Encrypted
```go
@@ -534,19 +545,19 @@ go func(){
In Viper, there are a few ways to get a value depending on the values type.
The following functions and methods exist:
* `Get(key string) : interface{}`
* `Get(key string) : any`
* `GetBool(key string) : bool`
* `GetFloat64(key string) : float64`
* `GetInt(key string) : int`
* `GetIntSlice(key string) : []int`
* `GetString(key string) : string`
* `GetStringMap(key string) : map[string]interface{}`
* `GetStringMap(key string) : map[string]any`
* `GetStringMapString(key string) : map[string]string`
* `GetStringSlice(key string) : []string`
* `GetTime(key string) : time.Time`
* `GetDuration(key string) : time.Duration`
* `IsSet(key string) : bool`
* `AllSettings() : map[string]interface{}`
* `AllSettings() : map[string]any`
One important thing to recognize is that each Get function will return a zero
value if its not found. To check if a given key exists, the `IsSet()` method
@@ -709,8 +720,8 @@ etc.
There are two methods to do this:
* `Unmarshal(rawVal interface{}) : error`
* `UnmarshalKey(key string, rawVal interface{}) : error`
* `Unmarshal(rawVal any) : error`
* `UnmarshalKey(key string, rawVal any) : error`
Example:
@@ -735,9 +746,9 @@ you have to change the delimiter:
```go
v := viper.NewWithOptions(viper.KeyDelimiter("::"))
v.SetDefault("chart::values", map[string]interface{}{
"ingress": map[string]interface{}{
"annotations": map[string]interface{}{
v.SetDefault("chart::values", map[string]any{
"ingress": map[string]any{
"annotations": map[string]any{
"traefik.frontend.rule.type": "PathPrefix",
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
},
@@ -746,7 +757,7 @@ v.SetDefault("chart::values", map[string]interface{}{
type config struct {
Chart struct{
Values map[string]interface{}
Values map[string]any
}
}
@@ -882,3 +893,31 @@ No, you will need to synchronize access to the viper yourself (for example by us
## Troubleshooting
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
## Development
**For an optimal developer experience, it is recommended to install [Nix](https://nixos.org/download.html) and [direnv](https://direnv.net/docs/installation.html).**
_Alternatively, install [Go](https://go.dev/dl/) on your computer then run `make deps` to install the rest of the dependencies._
Run the test suite:
```shell
make test
```
Run linters:
```shell
make lint # pass -j option to run them in parallel
```
Some linter violations can automatically be fixed:
```shell
make fmt
```
## License
The project is licensed under the [MIT License](LICENSE).
-11
View File
@@ -1,11 +0,0 @@
//go:build viper_logger
// +build viper_logger
package viper
// WithLogger sets a custom logger.
func WithLogger(l Logger) Option {
return optionFunc(func(v *Viper) {
v.logger = l
})
}
+1 -1
View File
@@ -30,7 +30,7 @@ func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
})
}
// pflagValue is a wrapper aroung *pflag.flag
// pflagValue is a wrapper around *pflag.flag
// that implements FlagValue
type pflagValue struct {
flag *pflag.Flag
+1 -1
View File
@@ -39,7 +39,7 @@ func TestBindFlagValueSet(t *testing.T) {
})
for name, expected := range mutatedTestValues {
assert.Equal(t, Get(name), expected)
assert.Equal(t, expected, Get(name))
}
}
Generated
+255
View File
@@ -0,0 +1,255 @@
{
"nodes": {
"devenv": {
"inputs": {
"flake-compat": "flake-compat",
"nix": "nix",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1687972261,
"narHash": "sha256-+mxvZfwMVoaZYETmuQWqTi/7T9UKoAE+WpdSQkOVJ2g=",
"owner": "cachix",
"repo": "devenv",
"rev": "e85df562088573305e55906eaa964341f8cb0d9f",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1687762428,
"narHash": "sha256-DIf7mi45PKo+s8dOYF+UlXHzE0Wl/+k3tXUyAoAnoGE=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "37dd7bb15791c86d55c5121740a1887ab55ee836",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1676545802,
"narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
"owner": "domenkozar",
"repo": "nix",
"rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "relaxed-flakes",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1678875422,
"narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1685564631,
"narHash": "sha256-8ywr3AkblY4++3lIVxmrWZFzac7+f32ZEhH/A8pNscI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4f53efe34b3a8877ac923b9350c874e3dcd5dc0a",
"type": "github"
},
"original": {
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1678872516,
"narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1687886075,
"narHash": "sha256-PeayJDDDy+uw1Ats4moZnRdL1OFuZm1Tj+KiHlD67+o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a565059a348422af5af9026b5174dc5c0dcefdae",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1686050334,
"narHash": "sha256-R0mczWjDzBpIvM3XXhO908X5e2CQqjyh/gFbwZk/7/Q=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "6881eb2ae5d8a3516e34714e7a90d9d95914c4dc",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_2"
}
}
},
"root": "root",
"version": 7
}
+56
View File
@@ -0,0 +1,56 @@
{
description = "Viper";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
perSystem = { config, self', inputs', pkgs, system, ... }: rec {
devenv.shells = {
default = {
languages = {
go.enable = true;
};
pre-commit.hooks = {
nixpkgs-fmt.enable = true;
yamllint.enable = true;
};
packages = with pkgs; [
gnumake
golangci-lint
yamllint
];
scripts = {
versions.exec = ''
go version
golangci-lint version
'';
};
enterShell = ''
versions
'';
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
containers = pkgs.lib.mkForce { };
};
ci = devenv.shells.default;
};
};
};
}
-65
View File
@@ -1,65 +0,0 @@
//go:build go1.16 && finder
// +build go1.16,finder
package viper
import (
"errors"
"io/fs"
"path"
)
type finder struct {
paths []string
fileNames []string
extensions []string
withoutExtension bool
}
func (f finder) Find(fsys fs.FS) (string, error) {
for _, searchPath := range f.paths {
for _, fileName := range f.fileNames {
for _, extension := range f.extensions {
filePath := path.Join(searchPath, fileName+"."+extension)
ok, err := fileExists(fsys, filePath)
if err != nil {
return "", err
}
if ok {
return filePath, nil
}
}
if f.withoutExtension {
filePath := path.Join(searchPath, fileName)
ok, err := fileExists(fsys, filePath)
if err != nil {
return "", err
}
if ok {
return filePath, nil
}
}
}
}
return "", nil
}
func fileExists(fsys fs.FS, filePath string) (bool, error) {
fileInfo, err := fs.Stat(fsys, filePath)
if err == nil {
return !fileInfo.IsDir(), nil
}
if errors.Is(err, fs.ErrNotExist) {
return false, nil
}
return false, err
}
-100
View File
@@ -1,100 +0,0 @@
//go:build go1.16 && finder
// +build go1.16,finder
package viper
import (
"io/fs"
"testing"
"testing/fstest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFinder(t *testing.T) {
t.Parallel()
fsys := fstest.MapFS{
"home/user/.config": &fstest.MapFile{},
"home/user/config.json": &fstest.MapFile{},
"home/user/config.yaml": &fstest.MapFile{},
"home/user/data.json": &fstest.MapFile{},
"etc/config/.config": &fstest.MapFile{},
"etc/config/a_random_file.txt": &fstest.MapFile{},
"etc/config/config.json": &fstest.MapFile{},
"etc/config/config.yaml": &fstest.MapFile{},
"etc/config/config.xml": &fstest.MapFile{},
}
testCases := []struct {
name string
fsys func() fs.FS
finder finder
result string
}{
{
name: "find file",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"etc/config"},
fileNames: []string{"config"},
extensions: []string{"json"},
},
result: "etc/config/config.json",
},
{
name: "file not found",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"var/config"},
fileNames: []string{"config"},
extensions: []string{"json"},
},
result: "",
},
{
name: "empty search params",
fsys: func() fs.FS { return fsys },
finder: finder{},
result: "",
},
{
name: "precedence",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"var/config", "home/user", "etc/config"},
fileNames: []string{"aconfig", "config"},
extensions: []string{"zml", "xml", "json"},
},
result: "home/user/config.json",
},
{
name: "without extension",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"var/config", "home/user", "etc/config"},
fileNames: []string{".config"},
extensions: []string{"zml", "xml", "json"},
withoutExtension: true,
},
result: "home/user/.config",
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
fsys := testCase.fsys()
result, err := testCase.finder.Find(fsys)
require.NoError(t, err)
assert.Equal(t, testCase.result, result)
})
}
}
+46 -35
View File
@@ -1,76 +1,87 @@
module github.com/spf13/viper
go 1.17
go 1.18
require (
github.com/fsnotify/fsnotify v1.6.0
github.com/hashicorp/hcl v1.0.0
github.com/magiconair/properties v1.8.7
github.com/mitchellh/mapstructure v1.5.0
github.com/pelletier/go-toml/v2 v2.0.8
github.com/sagikazarmark/crypt v0.10.0
github.com/spf13/afero v1.9.5
github.com/pelletier/go-toml/v2 v2.1.0
github.com/sagikazarmark/crypt v0.15.0
github.com/sagikazarmark/locafero v0.3.0
github.com/sagikazarmark/slog-shim v0.1.0
github.com/spf13/afero v1.10.0
github.com/spf13/cast v1.5.1
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.3
github.com/subosito/gotenv v1.4.2
github.com/stretchr/testify v1.8.4
github.com/subosito/gotenv v1.6.0
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
)
require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.19.0 // indirect
cloud.google.com/go v0.110.7 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/firestore v1.9.0 // indirect
cloud.google.com/go/longrunning v0.4.1 // indirect
github.com/armon/go-metrics v0.4.0 // indirect
cloud.google.com/go/firestore v1.13.0 // indirect
cloud.google.com/go/longrunning v0.5.1 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/color v1.14.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/s2a-go v0.1.3 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
github.com/hashicorp/consul/api v1.20.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/hashicorp/consul/api v1.25.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/minio/highwayhash v1.0.2 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nats-io/jwt/v2 v2.4.1 // indirect
github.com/nats-io/nats.go v1.30.2 // indirect
github.com/nats-io/nkeys v0.4.5 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/v2 v2.305.7 // indirect
go.etcd.io/etcd/client/v2 v2.305.9 // indirect
go.etcd.io/etcd/client/v3 v3.5.9 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.122.0 // indirect
google.golang.org/api v0.143.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.55.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
google.golang.org/grpc v1.58.2 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
+97 -1156
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -5,9 +5,9 @@ import (
)
// Decoder decodes the contents of b into v.
// It's primarily used for decoding contents of a file into a map[string]interface{}.
// It's primarily used for decoding contents of a file into a map[string]any.
type Decoder interface {
Decode(b []byte, v map[string]interface{}) error
Decode(b []byte, v map[string]any) error
}
const (
@@ -48,7 +48,7 @@ func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
}
// Decode calls the underlying Decoder based on the format.
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]any) error {
e.mu.RLock()
decoder, ok := e.decoders[format]
e.mu.RUnlock()
+5 -5
View File
@@ -6,10 +6,10 @@ import (
)
type decoder struct {
v map[string]interface{}
v map[string]any
}
func (d decoder) Decode(_ []byte, v map[string]interface{}) error {
func (d decoder) Decode(_ []byte, v map[string]any) error {
for key, value := range d.v {
v[key] = value
}
@@ -46,7 +46,7 @@ func TestDecoderRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewDecoderRegistry()
decoder := decoder{
v: map[string]interface{}{
v: map[string]any{
"key": "value",
},
}
@@ -56,7 +56,7 @@ func TestDecoderRegistry_Decode(t *testing.T) {
t.Fatal(err)
}
v := map[string]interface{}{}
v := map[string]any{}
err = registry.Decode("myformat", []byte("key: value"), v)
if err != nil {
@@ -71,7 +71,7 @@ func TestDecoderRegistry_Decode(t *testing.T) {
t.Run("DecoderNotFound", func(t *testing.T) {
registry := NewDecoderRegistry()
v := map[string]interface{}{}
v := map[string]any{}
err := registry.Decode("myformat", nil, v)
if err != ErrDecoderNotFound {
+3 -3
View File
@@ -15,8 +15,8 @@ const keyDelimiter = "_"
// (commonly called as dotenv format).
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
flattened := map[string]interface{}{}
func (Codec) Encode(v map[string]any) ([]byte, error) {
flattened := map[string]any{}
flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
@@ -40,7 +40,7 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (Codec) Decode(b []byte, v map[string]any) error {
var buf bytes.Buffer
_, err := buf.Write(b)
+3 -3
View File
@@ -15,7 +15,7 @@ const encoded = `KEY=value
`
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"KEY": "value",
}
@@ -36,7 +36,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
@@ -51,7 +51,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
+8 -8
View File
@@ -7,27 +7,27 @@ import (
)
// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in tha main package.
// Code is based on the function with the same name in the main package.
// TODO: move it to a common place
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
func flattenAndMergeMap(shadow map[string]any, m map[string]any, prefix string, delimiter string) map[string]any {
if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make(map[string]interface{})
shadow = make(map[string]any)
}
var m2 map[string]interface{}
var m2 map[string]any
if prefix != "" {
prefix += delimiter
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
switch val := val.(type) {
case map[string]any:
m2 = val
case map[any]any:
m2 = cast.ToStringMap(val)
default:
// immediate value
+3 -3
View File
@@ -5,9 +5,9 @@ import (
)
// Encoder encodes the contents of v into a byte representation.
// It's primarily used for encoding a map[string]interface{} into a file format.
// It's primarily used for encoding a map[string]any into a file format.
type Encoder interface {
Encode(v map[string]interface{}) ([]byte, error)
Encode(v map[string]any) ([]byte, error)
}
const (
@@ -47,7 +47,7 @@ func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
return nil
}
func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
func (e *EncoderRegistry) Encode(format string, v map[string]any) ([]byte, error) {
e.mu.RLock()
encoder, ok := e.encoders[format]
e.mu.RUnlock()
+3 -3
View File
@@ -8,7 +8,7 @@ type encoder struct {
b []byte
}
func (e encoder) Encode(_ map[string]interface{}) ([]byte, error) {
func (e encoder) Encode(_ map[string]any) ([]byte, error) {
return e.b, nil
}
@@ -49,7 +49,7 @@ func TestEncoderRegistry_Decode(t *testing.T) {
t.Fatal(err)
}
b, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
b, err := registry.Encode("myformat", map[string]any{"key": "value"})
if err != nil {
t.Fatal(err)
}
@@ -62,7 +62,7 @@ func TestEncoderRegistry_Decode(t *testing.T) {
t.Run("EncoderNotFound", func(t *testing.T) {
registry := NewEncoderRegistry()
_, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
_, err := registry.Encode("myformat", map[string]any{"key": "value"})
if err != ErrEncoderNotFound {
t.Fatalf("expected ErrEncoderNotFound, got: %v", err)
}
+2 -2
View File
@@ -12,7 +12,7 @@ import (
// TODO: add printer config to the codec?
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func (Codec) Encode(v map[string]any) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
@@ -35,6 +35,6 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (Codec) Decode(b []byte, v map[string]any) error {
return hcl.Unmarshal(b, &v)
}
+14 -14
View File
@@ -45,24 +45,24 @@ const encoded = `"key" = "value"
//
// in case of HCL it's slightly different from Viper's internal representation
// (eg. map is decoded into a list of maps)
var decoded = map[string]interface{}{
var decoded = map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
},
"map": []map[string]interface{}{
"map": []map[string]any{
{
"key": "value",
},
},
"nested_map": []map[string]interface{}{
"nested_map": []map[string]any{
{
"map": []map[string]interface{}{
"map": []map[string]any{
{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
@@ -74,20 +74,20 @@ var decoded = map[string]interface{}{
}
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
@@ -113,7 +113,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
@@ -128,7 +128,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
+3 -3
View File
@@ -19,11 +19,11 @@ type Codec struct {
LoadOptions LoadOptions
}
func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
func (c Codec) Encode(v map[string]any) ([]byte, error) {
cfg := ini.Empty()
ini.PrettyFormat = false
flattened := map[string]interface{}{}
flattened := map[string]any{}
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
@@ -62,7 +62,7 @@ func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (c Codec) Decode(b []byte, v map[string]interface{}) error {
func (c Codec) Decode(b []byte, v map[string]any) error {
cfg := ini.Empty(c.LoadOptions)
err := cfg.Append(b)
+10 -10
View File
@@ -26,19 +26,19 @@ key=value
//
// in case of INI it's slightly different from Viper's internal representation
// (eg. top level keys land in a section called default)
var decoded = map[string]interface{}{
"DEFAULT": map[string]interface{}{
var decoded = map[string]any{
"DEFAULT": map[string]any{
"key": "value",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
}
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"key": "value",
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
}
@@ -60,11 +60,11 @@ func TestCodec_Encode(t *testing.T) {
t.Run("Default", func(t *testing.T) {
codec := Codec{}
data := map[string]interface{}{
"default": map[string]interface{}{
data := map[string]any{
"default": map[string]any{
"key": "value",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
}
@@ -84,7 +84,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
@@ -99,7 +99,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
+12 -12
View File
@@ -15,22 +15,22 @@ import (
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
func deepSearch(m map[string]any, path []string) map[string]any {
for _, k := range path {
m2, ok := m[k]
if !ok {
// intermediate key does not exist
// => create it and continue from there
m3 := make(map[string]interface{})
m3 := make(map[string]any)
m[k] = m3
m = m3
continue
}
m3, ok := m2.(map[string]interface{})
m3, ok := m2.(map[string]any)
if !ok {
// intermediate key is a value
// => replace with a new map
m3 = make(map[string]interface{})
m3 = make(map[string]any)
m[k] = m3
}
// continue search from here
@@ -40,27 +40,27 @@ func deepSearch(m map[string]interface{}, path []string) map[string]interface{}
}
// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in tha main package.
// Code is based on the function with the same name in the main package.
// TODO: move it to a common place
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
func flattenAndMergeMap(shadow map[string]any, m map[string]any, prefix string, delimiter string) map[string]any {
if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make(map[string]interface{})
shadow = make(map[string]any)
}
var m2 map[string]interface{}
var m2 map[string]any
if prefix != "" {
prefix += delimiter
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
switch val := val.(type) {
case map[string]any:
m2 = val
case map[any]any:
m2 = cast.ToStringMap(val)
default:
// immediate value
+3 -3
View File
@@ -20,12 +20,12 @@ type Codec struct {
Properties *properties.Properties
}
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
func (c *Codec) Encode(v map[string]any) ([]byte, error) {
if c.Properties == nil {
c.Properties = properties.NewProperties()
}
flattened := map[string]interface{}{}
flattened := map[string]any{}
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
@@ -54,7 +54,7 @@ func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
func (c *Codec) Decode(b []byte, v map[string]any) error {
var err error
c.Properties, err = properties.Load(b, properties.UTF8)
if err != nil {
@@ -17,9 +17,9 @@ map.key = value
`
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"key": "value",
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
}
@@ -41,7 +41,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
@@ -58,7 +58,7 @@ func TestCodec_Decode(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
codec.Decode([]byte(``), v)
@@ -71,7 +71,7 @@ func TestCodec_Decode(t *testing.T) {
func TestCodec_DecodeEncode(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
+12 -12
View File
@@ -15,22 +15,22 @@ import (
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
func deepSearch(m map[string]any, path []string) map[string]any {
for _, k := range path {
m2, ok := m[k]
if !ok {
// intermediate key does not exist
// => create it and continue from there
m3 := make(map[string]interface{})
m3 := make(map[string]any)
m[k] = m3
m = m3
continue
}
m3, ok := m2.(map[string]interface{})
m3, ok := m2.(map[string]any)
if !ok {
// intermediate key is a value
// => replace with a new map
m3 = make(map[string]interface{})
m3 = make(map[string]any)
m[k] = m3
}
// continue search from here
@@ -40,27 +40,27 @@ func deepSearch(m map[string]interface{}, path []string) map[string]interface{}
}
// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in tha main package.
// Code is based on the function with the same name in the main package.
// TODO: move it to a common place
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
func flattenAndMergeMap(shadow map[string]any, m map[string]any, prefix string, delimiter string) map[string]any {
if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make(map[string]interface{})
shadow = make(map[string]any)
}
var m2 map[string]interface{}
var m2 map[string]any
if prefix != "" {
prefix += delimiter
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
switch val := val.(type) {
case map[string]any:
m2 = val
case map[any]any:
m2 = cast.ToStringMap(val)
default:
// immediate value
+2 -2
View File
@@ -7,11 +7,11 @@ import (
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func (Codec) Encode(v map[string]any) ([]byte, error) {
// TODO: expose prefix and indent in the Codec as setting?
return json.MarshalIndent(v, "", " ")
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (Codec) Decode(b []byte, v map[string]any) error {
return json.Unmarshal(b, &v)
}
+8 -8
View File
@@ -29,20 +29,20 @@ const encoded = `{
}`
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
@@ -68,7 +68,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(encoded), v)
if err != nil {
@@ -83,7 +83,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
+2 -2
View File
@@ -7,10 +7,10 @@ import (
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func (Codec) Encode(v map[string]any) ([]byte, error) {
return toml.Marshal(v)
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (Codec) Decode(b []byte, v map[string]any) error {
return toml.Unmarshal(b, &v)
}
+8 -8
View File
@@ -39,20 +39,20 @@ list = ['item1', 'item2', 'item3']
`
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
@@ -78,7 +78,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
@@ -93,7 +93,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
+2 -2
View File
@@ -5,10 +5,10 @@ import "gopkg.in/yaml.v3"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func (Codec) Encode(v map[string]any) ([]byte, error) {
return yaml.Marshal(v)
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (Codec) Decode(b []byte, v map[string]any) error {
return yaml.Unmarshal(b, &v)
}
+14 -14
View File
@@ -47,20 +47,20 @@ nested_map:
//
// in case of YAML it's slightly different from Viper's internal representation
// (eg. map is decoded into a map with interface key)
var decoded = map[string]interface{}{
var decoded = map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
@@ -70,20 +70,20 @@ var decoded = map[string]interface{}{
}
// Viper's internal representation
var data = map[string]interface{}{
var data = map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []interface{}{
"list": []any{
"item1",
"item2",
"item3",
@@ -109,7 +109,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(original), v)
if err != nil {
@@ -124,7 +124,7 @@ func TestCodec_Decode(t *testing.T) {
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
-40
View File
@@ -1,40 +0,0 @@
//go:build !go1.17
// +build !go1.17
package testutil
import (
"os"
"testing"
)
// Based on https://github.com/frankban/quicktest/blob/577841610793d24f99e31cc2c0ef3a541fefd7c7/patch.go#L34-L64
// Licensed under the MIT license
// Copyright (c) 2017 Canonical Ltd.
// Setenv sets an environment variable to a temporary value for the
// duration of the test.
//
// At the end of the test (see "Deferred execution" in the package docs), the
// environment variable is returned to its original value.
func Setenv(t *testing.T, name, val string) {
setenv(t, name, val, true)
}
// setenv sets or unsets an environment variable to a temporary value for the
// duration of the test
func setenv(t *testing.T, name, val string, valOK bool) {
oldVal, oldOK := os.LookupEnv(name)
if valOK {
os.Setenv(name, val)
} else {
os.Unsetenv(name)
}
t.Cleanup(func() {
if oldOK {
os.Setenv(name, oldVal)
} else {
os.Unsetenv(name)
}
})
}
-18
View File
@@ -1,18 +0,0 @@
//go:build go1.17
// +build go1.17
package testutil
import (
"testing"
)
// Setenv sets an environment variable to a temporary value for the
// duration of the test.
//
// This shim can be removed once support for Go <1.17 is dropped.
func Setenv(t *testing.T, name, val string) {
t.Helper()
t.Setenv(name, val)
}
+25 -34
View File
@@ -1,77 +1,68 @@
package viper
import (
"fmt"
"context"
jww "github.com/spf13/jwalterweatherman"
slog "github.com/sagikazarmark/slog-shim"
)
// Logger is a unified interface for various logging use cases and practices, including:
// - leveled logging
// - structured logging
//
// Deprecated: use `log/slog` instead.
type Logger interface {
// Trace logs a Trace event.
//
// Even more fine-grained information than Debug events.
// Loggers not supporting this level should fall back to Debug.
Trace(msg string, keyvals ...interface{})
Trace(msg string, keyvals ...any)
// Debug logs a Debug event.
//
// A verbose series of information events.
// They are useful when debugging the system.
Debug(msg string, keyvals ...interface{})
Debug(msg string, keyvals ...any)
// Info logs an Info event.
//
// General information about what's happening inside the system.
Info(msg string, keyvals ...interface{})
Info(msg string, keyvals ...any)
// Warn logs a Warn(ing) event.
//
// Non-critical events that should be looked at.
Warn(msg string, keyvals ...interface{})
Warn(msg string, keyvals ...any)
// Error logs an Error event.
//
// Critical events that require immediate attention.
// Loggers commonly provide Fatal and Panic levels above Error level,
// but exiting and panicing is out of scope for a logging library.
Error(msg string, keyvals ...interface{})
// but exiting and panicking is out of scope for a logging library.
Error(msg string, keyvals ...any)
}
type jwwLogger struct{}
func (jwwLogger) Trace(msg string, keyvals ...interface{}) {
jww.TRACE.Printf(jwwLogMessage(msg, keyvals...))
// WithLogger sets a custom logger.
func WithLogger(l *slog.Logger) Option {
return optionFunc(func(v *Viper) {
v.logger = l
})
}
func (jwwLogger) Debug(msg string, keyvals ...interface{}) {
jww.DEBUG.Printf(jwwLogMessage(msg, keyvals...))
type discardHandler struct{}
func (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool {
return false
}
func (jwwLogger) Info(msg string, keyvals ...interface{}) {
jww.INFO.Printf(jwwLogMessage(msg, keyvals...))
func (n *discardHandler) Handle(_ context.Context, _ slog.Record) error {
return nil
}
func (jwwLogger) Warn(msg string, keyvals ...interface{}) {
jww.WARN.Printf(jwwLogMessage(msg, keyvals...))
func (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler {
return n
}
func (jwwLogger) Error(msg string, keyvals ...interface{}) {
jww.ERROR.Printf(jwwLogMessage(msg, keyvals...))
}
func jwwLogMessage(msg string, keyvals ...interface{}) string {
out := msg
if len(keyvals) > 0 && len(keyvals)%2 == 1 {
keyvals = append(keyvals, nil)
}
for i := 0; i <= len(keyvals)-2; i += 2 {
out = fmt.Sprintf("%s %v=%v", out, keyvals[i], keyvals[i+1])
}
return out
func (n *discardHandler) WithGroup(_ string) slog.Handler {
return n
}
+18 -22
View File
@@ -1,7 +1,6 @@
package viper
import (
"fmt"
"strings"
"testing"
@@ -46,11 +45,11 @@ func TestNestedOverrides(t *testing.T) {
deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)
// Case 4: key:value overridden by a map
v = overrideDefault(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable
assert.Equal(10, v.Get("tom.age")) // new value should be there
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there
v = override(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10})
v = overrideDefault(assert, "tom.size", 4, "tom", map[string]any{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable
assert.Equal(10, v.Get("tom.age")) // new value should be there
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there
v = override(assert, "tom.size", 4, "tom", map[string]any{"age": 10})
assert.Nil(v.Get("tom.size"))
assert.Equal(10, v.Get("tom.age"))
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)
@@ -75,11 +74,11 @@ func TestNestedOverrides(t *testing.T) {
}
}
func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
func overrideDefault(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
}
func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
func override(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
}
@@ -94,7 +93,7 @@ func override(assert *assert.Assertions, firstPath string, firstValue interface{
//
// After each assignment, the value is checked, retrieved both by its full path
// and by its key sequence (successive maps).
func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
v := New()
firstKeys := strings.Split(firstPath, v.keyDelim)
if assert == nil ||
@@ -128,14 +127,14 @@ func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, fir
// deepCheckValue checks that all given keys correspond to a valid path in the
// configuration map of the given layer, and that the final value equals the one given
func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value interface{}) {
func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value any) {
if assert == nil || v == nil ||
len(keys) == 0 || len(keys[0]) == 0 {
return
}
// init
var val interface{}
var val any
var ms string
switch l {
case defaultLayer:
@@ -147,28 +146,25 @@ func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string,
}
// loop through map
var m map[string]interface{}
err := false
var m map[string]any
for _, k := range keys {
if val == nil {
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
assert.Failf("%s is not a map[string]any", ms)
return
}
// deep scan of the map to get the final value
switch val.(type) {
case map[interface{}]interface{}:
switch val := val.(type) {
case map[any]any:
m = cast.ToStringMap(val)
case map[string]interface{}:
m = val.(map[string]interface{})
case map[string]any:
m = val
default:
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
assert.Failf("%s is not a map[string]any", ms)
return
}
ms = ms + "[\"" + k + "\"]"
val = m[k]
}
if !err {
assert.Equal(value, val)
}
assert.Equal(value, val)
}
+4
View File
@@ -91,6 +91,8 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
cm, err = crypt.NewEtcdV3ConfigManager(endpoints, kr)
case "firestore":
cm, err = crypt.NewFirestoreConfigManager(endpoints, kr)
case "nats":
cm, err = crypt.NewNatsConfigManager(endpoints, kr)
default:
cm, err = crypt.NewConsulConfigManager(endpoints, kr)
}
@@ -102,6 +104,8 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
cm, err = crypt.NewStandardEtcdV3ConfigManager(endpoints)
case "firestore":
cm, err = crypt.NewStandardFirestoreConfigManager(endpoints)
case "nats":
cm, err = crypt.NewStandardNatsConfigManager(endpoints)
default:
cm, err = crypt.NewStandardConsulConfigManager(endpoints)
}
+23 -22
View File
@@ -18,6 +18,7 @@ import (
"strings"
"unicode"
slog "github.com/sagikazarmark/slog-shim"
"github.com/spf13/cast"
)
@@ -38,11 +39,11 @@ func (pe ConfigParseError) Unwrap() error {
// toCaseInsensitiveValue checks if the value is a map;
// if so, create a copy and lower-case the keys recursively.
func toCaseInsensitiveValue(value interface{}) interface{} {
func toCaseInsensitiveValue(value any) any {
switch v := value.(type) {
case map[interface{}]interface{}:
case map[any]any:
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
case map[string]interface{}:
case map[string]any:
value = copyAndInsensitiviseMap(v)
}
@@ -51,15 +52,15 @@ func toCaseInsensitiveValue(value interface{}) interface{} {
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
// any map it makes case insensitive.
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
nm := make(map[string]interface{})
func copyAndInsensitiviseMap(m map[string]any) map[string]any {
nm := make(map[string]any)
for key, val := range m {
lkey := strings.ToLower(key)
switch v := val.(type) {
case map[interface{}]interface{}:
case map[any]any:
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
case map[string]interface{}:
case map[string]any:
nm[lkey] = copyAndInsensitiviseMap(v)
default:
nm[lkey] = v
@@ -69,23 +70,23 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
return nm
}
func insensitiviseVal(val interface{}) interface{} {
switch val.(type) {
case map[interface{}]interface{}:
func insensitiviseVal(val any) any {
switch v := val.(type) {
case map[any]any:
// nested map: cast and recursively insensitivise
val = cast.ToStringMap(val)
insensitiviseMap(val.(map[string]interface{}))
case map[string]interface{}:
insensitiviseMap(val.(map[string]any))
case map[string]any:
// nested map: recursively insensitivise
insensitiviseMap(val.(map[string]interface{}))
case []interface{}:
insensitiviseMap(v)
case []any:
// nested array: recursively insensitivise
insensitiveArray(val.([]interface{}))
insensitiveArray(v)
}
return val
}
func insensitiviseMap(m map[string]interface{}) {
func insensitiviseMap(m map[string]any) {
for key, val := range m {
val = insensitiviseVal(val)
lower := strings.ToLower(key)
@@ -98,13 +99,13 @@ func insensitiviseMap(m map[string]interface{}) {
}
}
func insensitiveArray(a []interface{}) {
func insensitiveArray(a []any) {
for i, val := range a {
a[i] = insensitiviseVal(val)
}
}
func absPathify(logger Logger, inPath string) string {
func absPathify(logger *slog.Logger, inPath string) string {
logger.Info("trying to resolve absolute path", "path", inPath)
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
@@ -197,22 +198,22 @@ func parseSizeInBytes(sizeStr string) uint {
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
func deepSearch(m map[string]any, path []string) map[string]any {
for _, k := range path {
m2, ok := m[k]
if !ok {
// intermediate key does not exist
// => create it and continue from there
m3 := make(map[string]interface{})
m3 := make(map[string]any)
m[k] = m3
m = m3
continue
}
m3, ok := m2.(map[string]interface{})
m3, ok := m2.(map[string]any)
if !ok {
// intermediate key is a value
// => replace with a new map
m3 = make(map[string]interface{})
m3 = make(map[string]any)
m[k] = m3
}
// continue search from here
+9 -9
View File
@@ -16,21 +16,21 @@ import (
"reflect"
"testing"
"github.com/spf13/viper/internal/testutil"
slog "github.com/sagikazarmark/slog-shim"
)
func TestCopyAndInsensitiviseMap(t *testing.T) {
var (
given = map[string]interface{}{
given = map[string]any{
"Foo": 32,
"Bar": map[interface{}]interface{}{
"Bar": map[any]any{
"ABc": "A",
"cDE": "B",
},
}
expected = map[string]interface{}{
expected = map[string]any{
"foo": 32,
"bar": map[string]interface{}{
"bar": map[string]any{
"abc": "A",
"cde": "B",
},
@@ -51,7 +51,7 @@ func TestCopyAndInsensitiviseMap(t *testing.T) {
t.Fatal("Input map changed")
}
m := given["Bar"].(map[interface{}]interface{})
m := given["Bar"].(map[any]any)
if _, ok := m["ABc"]; !ok {
t.Fatal("Input map changed")
}
@@ -64,8 +64,8 @@ func TestAbsPathify(t *testing.T) {
homer := filepath.Join(home, "homer")
wd, _ := os.Getwd()
testutil.Setenv(t, "HOMER_ABSOLUTE_PATH", homer)
testutil.Setenv(t, "VAR_WITH_RELATIVE_PATH", "relative")
t.Setenv("HOMER_ABSOLUTE_PATH", homer)
t.Setenv("VAR_WITH_RELATIVE_PATH", "relative")
tests := []struct {
input string
@@ -87,7 +87,7 @@ func TestAbsPathify(t *testing.T) {
}
for _, test := range tests {
got := absPathify(jwwLogger{}, test.input)
got := absPathify(slog.Default(), test.input)
if got != test.output {
t.Errorf("Got %v\nexpected\n%q", got, test.output)
}
+124 -119
View File
@@ -35,6 +35,7 @@ import (
"github.com/fsnotify/fsnotify"
"github.com/mitchellh/mapstructure"
slog "github.com/sagikazarmark/slog-shim"
"github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/pflag"
@@ -206,10 +207,10 @@ type Viper struct {
allowEmptyEnv bool
parents []string
config map[string]interface{}
override map[string]interface{}
defaults map[string]interface{}
kvstore map[string]interface{}
config map[string]any
override map[string]any
defaults map[string]any
kvstore map[string]any
pflags map[string]FlagValue
env map[string][]string
aliases map[string]string
@@ -217,7 +218,7 @@ type Viper struct {
onConfigChange func(fsnotify.Event)
logger Logger
logger *slog.Logger
// TODO: should probably be protected with a mutex
encoderRegistry *encoding.EncoderRegistry
@@ -231,16 +232,16 @@ func New() *Viper {
v.configName = "config"
v.configPermissions = os.FileMode(0o644)
v.fs = afero.NewOsFs()
v.config = make(map[string]interface{})
v.config = make(map[string]any)
v.parents = []string{}
v.override = make(map[string]interface{})
v.defaults = make(map[string]interface{})
v.kvstore = make(map[string]interface{})
v.override = make(map[string]any)
v.defaults = make(map[string]any)
v.kvstore = make(map[string]any)
v.pflags = make(map[string]FlagValue)
v.env = make(map[string][]string)
v.aliases = make(map[string]string)
v.typeByDefValue = false
v.logger = jwwLogger{}
v.logger = slog.New(&discardHandler{})
v.resetEncoding()
@@ -301,7 +302,7 @@ func NewWithOptions(opts ...Option) *Viper {
func Reset() {
v = New()
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
}
// TODO: make this lazy initialization instead
@@ -420,7 +421,7 @@ type RemoteProvider interface {
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
// SupportedRemoteProviders are universally supported remote providers.
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
// OnConfigChange sets the event handler that is called when a config file changes.
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
@@ -523,6 +524,12 @@ func (v *Viper) SetEnvPrefix(in string) {
}
}
func GetEnvPrefix() string { return v.GetEnvPrefix() }
func (v *Viper) GetEnvPrefix() string {
return v.envPrefix
}
func (v *Viper) mergeWithEnvPrefix(in string) string {
if v.envPrefix != "" {
return strings.ToUpper(v.envPrefix + "_" + in)
@@ -578,8 +585,8 @@ func (v *Viper) AddConfigPath(in string) {
// AddRemoteProvider adds a remote configuration source.
// Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
@@ -609,7 +616,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
// AddSecureRemoteProvider adds a remote configuration source.
// Secure Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
// path is the path in the k/v store to retrieve configuration
@@ -653,7 +660,7 @@ func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
// searchMap recursively searches for a value for path in source map.
// Returns nil if not found.
// Note: This assumes that the path entries and map keys are lower cased.
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
func (v *Viper) searchMap(source map[string]any, path []string) any {
if len(path) == 0 {
return source
}
@@ -666,13 +673,13 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
}
// Nested case
switch next.(type) {
case map[interface{}]interface{}:
switch next := next.(type) {
case map[any]any:
return v.searchMap(cast.ToStringMap(next), path[1:])
case map[string]interface{}:
case map[string]any:
// Type assertion is safe here since it is only reached
// if the type of `next` is the same as the type being asserted
return v.searchMap(next.(map[string]interface{}), path[1:])
return v.searchMap(next, path[1:])
default:
// got a value but nested key expected, return "nil" for not found
return nil
@@ -692,7 +699,7 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
// in their keys).
//
// Note: This assumes that the path entries and map keys are lower cased.
func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []string) interface{} {
func (v *Viper) searchIndexableWithPathPrefixes(source any, path []string) any {
if len(path) == 0 {
return source
}
@@ -701,11 +708,11 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
for i := len(path); i > 0; i-- {
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
var val interface{}
var val any
switch sourceIndexable := source.(type) {
case []interface{}:
case []any:
val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path)
case map[string]interface{}:
case map[string]any:
val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path)
}
if val != nil {
@@ -722,11 +729,11 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
// This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
func (v *Viper) searchSliceWithPathPrefixes(
sourceSlice []interface{},
sourceSlice []any,
prefixKey string,
pathIndex int,
path []string,
) interface{} {
) any {
// if the prefixKey is not a number or it is out of bounds of the slice
index, err := strconv.Atoi(prefixKey)
if err != nil || len(sourceSlice) <= index {
@@ -741,9 +748,9 @@ func (v *Viper) searchSliceWithPathPrefixes(
}
switch n := next.(type) {
case map[interface{}]interface{}:
case map[any]any:
return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
case map[string]interface{}, []interface{}:
case map[string]any, []any:
return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
default:
// got a value but nested key expected, do nothing and look for next prefix
@@ -758,11 +765,11 @@ func (v *Viper) searchSliceWithPathPrefixes(
// This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
func (v *Viper) searchMapWithPathPrefixes(
sourceMap map[string]interface{},
sourceMap map[string]any,
prefixKey string,
pathIndex int,
path []string,
) interface{} {
) any {
next, ok := sourceMap[prefixKey]
if !ok {
return nil
@@ -775,9 +782,9 @@ func (v *Viper) searchMapWithPathPrefixes(
// Nested case
switch n := next.(type) {
case map[interface{}]interface{}:
case map[any]any:
return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
case map[string]interface{}, []interface{}:
case map[string]any, []any:
return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
default:
// got a value but nested key expected, do nothing and look for next prefix
@@ -792,8 +799,8 @@ func (v *Viper) searchMapWithPathPrefixes(
// e.g., if "foo.bar" has a value in the given map, it “shadows”
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
var parentVal interface{}
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]any) string {
var parentVal any
for i := 1; i < len(path); i++ {
parentVal = v.searchMap(m, path[0:i])
if parentVal == nil {
@@ -801,9 +808,9 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
return ""
}
switch parentVal.(type) {
case map[interface{}]interface{}:
case map[any]any:
continue
case map[string]interface{}:
case map[string]any:
continue
default:
// parentVal is a regular value which shadows "path"
@@ -818,9 +825,9 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
// e.g., if "foo.bar" has a value in the given map, it “shadows”
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
func (v *Viper) isPathShadowedInFlatMap(path []string, mi any) string {
// unify input map
var m map[string]interface{}
var m map[string]any
switch mi.(type) {
case map[string]string, map[string]FlagValue:
m = cast.ToStringMap(mi)
@@ -887,9 +894,9 @@ func GetViper() *Viper {
// override, flag, env, config file, key/value store, default
//
// Get returns an interface. For a specific value use one of the Get____ methods.
func Get(key string) interface{} { return v.Get(key) }
func Get(key string) any { return v.Get(key) }
func (v *Viper) Get(key string) interface{} {
func (v *Viper) Get(key string) any {
lcaseKey := strings.ToLower(key)
val := v.find(lcaseKey, true)
if val == nil {
@@ -1059,9 +1066,9 @@ func (v *Viper) GetStringSlice(key string) []string {
}
// GetStringMap returns the value associated with the key as a map of interfaces.
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
func GetStringMap(key string) map[string]any { return v.GetStringMap(key) }
func (v *Viper) GetStringMap(key string) map[string]interface{} {
func (v *Viper) GetStringMap(key string) map[string]any {
return cast.ToStringMap(v.Get(key))
}
@@ -1089,27 +1096,27 @@ func (v *Viper) GetSizeInBytes(key string) uint {
}
// UnmarshalKey takes a single key and unmarshals it into a Struct.
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
return v.UnmarshalKey(key, rawVal, opts...)
}
func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
}
// Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set.
func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
func Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
return v.Unmarshal(rawVal, opts...)
}
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
}
// defaultDecoderConfig returns default mapstructure.DecoderConfig with support
// of time.Duration values & string slices
func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
c := &mapstructure.DecoderConfig{
Metadata: nil,
Result: output,
@@ -1126,7 +1133,7 @@ func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *maps
}
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
func decode(input interface{}, config *mapstructure.DecoderConfig) error {
func decode(input any, config *mapstructure.DecoderConfig) error {
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
@@ -1136,11 +1143,11 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error {
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct.
func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
return v.UnmarshalExact(rawVal, opts...)
}
func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
config := defaultDecoderConfig(rawVal, opts...)
config.ErrorUnused = true
@@ -1237,9 +1244,9 @@ func (v *Viper) MustBindEnv(input ...string) {
// corresponds to a flag, the flag's default value is returned.
//
// Note: this assumes a lower-cased key given.
func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
func (v *Viper) find(lcaseKey string, flagDefault bool) any {
var (
val interface{}
val any
exists bool
path = strings.Split(lcaseKey, v.keyDelim)
nested = len(path) > 1
@@ -1398,46 +1405,46 @@ func readAsCSV(val string) ([]string, error) {
}
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79
// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap
func stringToStringConv(val string) interface{} {
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap
func stringToStringConv(val string) any {
val = strings.Trim(val, "[]")
// An empty string would cause an empty map
if len(val) == 0 {
return map[string]interface{}{}
return map[string]any{}
}
r := csv.NewReader(strings.NewReader(val))
ss, err := r.Read()
if err != nil {
return nil
}
out := make(map[string]interface{}, len(ss))
out := make(map[string]any, len(ss))
for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2)
if len(kv) != 2 {
k, vv, found := strings.Cut(pair, "=")
if !found {
return nil
}
out[kv[0]] = kv[1]
out[k] = vv
}
return out
}
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68
// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap
func stringToIntConv(val string) interface{} {
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap
func stringToIntConv(val string) any {
val = strings.Trim(val, "[]")
// An empty string would cause an empty map
if len(val) == 0 {
return map[string]interface{}{}
return map[string]any{}
}
ss := strings.Split(val, ",")
out := make(map[string]interface{}, len(ss))
out := make(map[string]any, len(ss))
for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2)
if len(kv) != 2 {
k, vv, found := strings.Cut(pair, "=")
if !found {
return nil
}
var err error
out[kv[0]], err = strconv.Atoi(kv[1])
out[k], err = strconv.Atoi(vv)
if err != nil {
return nil
}
@@ -1538,9 +1545,9 @@ func (v *Viper) InConfig(key string) bool {
// SetDefault sets the default value for this key.
// SetDefault is case-insensitive for a key.
// Default only used when no value is provided by the user via flag, config or ENV.
func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
func SetDefault(key string, value any) { v.SetDefault(key, value) }
func (v *Viper) SetDefault(key string, value interface{}) {
func (v *Viper) SetDefault(key string, value any) {
// If alias passed in, then set the proper default
key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)
@@ -1557,9 +1564,9 @@ func (v *Viper) SetDefault(key string, value interface{}) {
// Set is case-insensitive for a key.
// Will be used instead of values obtained via
// flags, config file, ENV, default, or key/value store.
func Set(key string, value interface{}) { v.Set(key, value) }
func Set(key string, value any) { v.Set(key, value) }
func (v *Viper) Set(key string, value interface{}) {
func (v *Viper) Set(key string, value any) {
// If alias passed in, then set the proper override
key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)
@@ -1593,7 +1600,7 @@ func (v *Viper) ReadInConfig() error {
return err
}
config := make(map[string]interface{})
config := make(map[string]any)
err = v.unmarshalReader(bytes.NewReader(file), config)
if err != nil {
@@ -1631,7 +1638,7 @@ func (v *Viper) MergeInConfig() error {
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
func (v *Viper) ReadConfig(in io.Reader) error {
v.config = make(map[string]interface{})
v.config = make(map[string]any)
return v.unmarshalReader(in, v.config)
}
@@ -1639,7 +1646,7 @@ func (v *Viper) ReadConfig(in io.Reader) error {
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
func (v *Viper) MergeConfig(in io.Reader) error {
cfg := make(map[string]interface{})
cfg := make(map[string]any)
if err := v.unmarshalReader(in, cfg); err != nil {
return err
}
@@ -1648,11 +1655,11 @@ func (v *Viper) MergeConfig(in io.Reader) error {
// MergeConfigMap merges the configuration from the map given with an existing config.
// Note that the map given may be modified.
func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
func MergeConfigMap(cfg map[string]any) error { return v.MergeConfigMap(cfg) }
func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
func (v *Viper) MergeConfigMap(cfg map[string]any) error {
if v.config == nil {
v.config = make(map[string]interface{})
v.config = make(map[string]any)
}
insensitiviseMap(cfg)
mergeMaps(cfg, v.config, nil)
@@ -1717,7 +1724,7 @@ func (v *Viper) writeConfig(filename string, force bool) error {
return UnsupportedConfigError(configType)
}
if v.config == nil {
v.config = make(map[string]interface{})
v.config = make(map[string]any)
}
flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
if !force {
@@ -1738,11 +1745,11 @@ func (v *Viper) writeConfig(filename string, force bool) error {
// Unmarshal a Reader into a map.
// Should probably be an unexported function.
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
func unmarshalReader(in io.Reader, c map[string]any) error {
return v.unmarshalReader(in, c)
}
func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
buf := new(bytes.Buffer)
buf.ReadFrom(in)
@@ -1776,7 +1783,7 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error {
return nil
}
func keyExists(k string, m map[string]interface{}) string {
func keyExists(k string, m map[string]any) string {
lk := strings.ToLower(k)
for mk := range m {
lmk := strings.ToLower(mk)
@@ -1788,33 +1795,33 @@ func keyExists(k string, m map[string]interface{}) string {
}
func castToMapStringInterface(
src map[interface{}]interface{},
) map[string]interface{} {
tgt := map[string]interface{}{}
src map[any]any,
) map[string]any {
tgt := map[string]any{}
for k, v := range src {
tgt[fmt.Sprintf("%v", k)] = v
}
return tgt
}
func castMapStringSliceToMapInterface(src map[string][]string) map[string]interface{} {
tgt := map[string]interface{}{}
func castMapStringSliceToMapInterface(src map[string][]string) map[string]any {
tgt := map[string]any{}
for k, v := range src {
tgt[k] = v
}
return tgt
}
func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
tgt := map[string]interface{}{}
func castMapStringToMapInterface(src map[string]string) map[string]any {
tgt := map[string]any{}
for k, v := range src {
tgt[k] = v
}
return tgt
}
func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} {
tgt := map[string]interface{}{}
func castMapFlagToMapInterface(src map[string]FlagValue) map[string]any {
tgt := map[string]any{}
for k, v := range src {
tgt[k] = v
}
@@ -1822,17 +1829,15 @@ func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{}
}
// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
// insistence on parsing nested structures as `map[interface{}]interface{}`
// insistence on parsing nested structures as `map[any]any`
// instead of using a `string` as the key for nest structures beyond one level
// deep. Both map types are supported as there is a go-yaml fork that uses
// `map[string]interface{}` instead.
func mergeMaps(
src, tgt map[string]interface{}, itgt map[interface{}]interface{},
) {
// `map[string]any` instead.
func mergeMaps(src, tgt map[string]any, itgt map[any]any) {
for sk, sv := range src {
tk := keyExists(sk, tgt)
if tk == "" {
v.logger.Trace("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv)
v.logger.Debug("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv)
tgt[sk] = sv
if itgt != nil {
itgt[sk] = sv
@@ -1842,7 +1847,7 @@ func mergeMaps(
tv, ok := tgt[tk]
if !ok {
v.logger.Trace("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv)
v.logger.Debug("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv)
tgt[sk] = sv
if itgt != nil {
itgt[sk] = sv
@@ -1853,7 +1858,7 @@ func mergeMaps(
svType := reflect.TypeOf(sv)
tvType := reflect.TypeOf(tv)
v.logger.Trace(
v.logger.Debug(
"processing",
"key", sk,
"st", svType,
@@ -1863,12 +1868,12 @@ func mergeMaps(
)
switch ttv := tv.(type) {
case map[interface{}]interface{}:
v.logger.Trace("merging maps (must convert)")
tsv, ok := sv.(map[interface{}]interface{})
case map[any]any:
v.logger.Debug("merging maps (must convert)")
tsv, ok := sv.(map[any]any)
if !ok {
v.logger.Error(
"Could not cast sv to map[interface{}]interface{}",
"Could not cast sv to map[any]any",
"key", sk,
"st", svType,
"tt", tvType,
@@ -1881,12 +1886,12 @@ func mergeMaps(
ssv := castToMapStringInterface(tsv)
stv := castToMapStringInterface(ttv)
mergeMaps(ssv, stv, ttv)
case map[string]interface{}:
v.logger.Trace("merging maps")
tsv, ok := sv.(map[string]interface{})
case map[string]any:
v.logger.Debug("merging maps")
tsv, ok := sv.(map[string]any)
if !ok {
v.logger.Error(
"Could not cast sv to map[string]interface{}",
"Could not cast sv to map[string]any",
"key", sk,
"st", svType,
"tt", tvType,
@@ -1897,7 +1902,7 @@ func mergeMaps(
}
mergeMaps(tsv, ttv, nil)
default:
v.logger.Trace("setting value")
v.logger.Debug("setting value")
tgt[tk] = sv
if itgt != nil {
itgt[tk] = sv
@@ -1948,7 +1953,7 @@ func (v *Viper) getKeyValueConfig() error {
return RemoteConfigError("No Files Found")
}
func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {
reader, err := RemoteConfig.Get(provider)
if err != nil {
return nil, err
@@ -1997,7 +2002,7 @@ func (v *Viper) watchKeyValueConfig() error {
return RemoteConfigError("No Files Found")
}
func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) {
reader, err := RemoteConfig.Watch(provider)
if err != nil {
return nil, err
@@ -2036,7 +2041,7 @@ func (v *Viper) AllKeys() []string {
// it is skipped.
//
// The resulting set of paths is merged to the given shadow set at the same time.
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]any, prefix string) map[string]bool {
if shadow != nil && prefix != "" && shadow[prefix] {
// prefix is shadowed => nothing more to flatten
return shadow
@@ -2045,16 +2050,16 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
shadow = make(map[string]bool)
}
var m2 map[string]interface{}
var m2 map[string]any
if prefix != "" {
prefix += v.keyDelim
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
switch val := val.(type) {
case map[string]any:
m2 = val
case map[any]any:
m2 = cast.ToStringMap(val)
default:
// immediate value
@@ -2069,7 +2074,7 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
// mergeFlatMap merges the given maps, excluding values of the second map
// shadowed by values from the first map.
func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]any) map[string]bool {
// scan keys
outer:
for k := range m {
@@ -2089,11 +2094,11 @@ outer:
return shadow
}
// AllSettings merges all settings and returns them as a map[string]interface{}.
func AllSettings() map[string]interface{} { return v.AllSettings() }
// AllSettings merges all settings and returns them as a map[string]any.
func AllSettings() map[string]any { return v.AllSettings() }
func (v *Viper) AllSettings() map[string]interface{} {
m := map[string]interface{}{}
func (v *Viper) AllSettings() map[string]any {
m := map[string]any{}
// start from the list of keys, and construct the map one value at a time
for _, k := range v.AllKeys() {
value := v.Get(k)
+1 -2
View File
@@ -1,5 +1,4 @@
//go:build !go1.16 || !finder
// +build !go1.16 !finder
//go:build !finder
package viper
+17 -11
View File
@@ -1,32 +1,38 @@
//go:build go1.16 && finder
// +build go1.16,finder
//go:build finder
package viper
import (
"fmt"
"github.com/spf13/afero"
"github.com/sagikazarmark/locafero"
)
// Search all configPaths for any config file.
// Returns the first path that exists (and is a config file).
func (v *Viper) findConfigFile() (string, error) {
finder := finder{
paths: v.configPaths,
fileNames: []string{v.configName},
extensions: SupportedExts,
withoutExtension: v.configType != "",
var names []string
if v.configType != "" {
names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)
} else {
names = locafero.NameWithExtensions(v.configName, SupportedExts...)
}
file, err := finder.Find(afero.NewIOFS(v.fs))
finder := locafero.Finder{
Paths: v.configPaths,
Names: names,
Type: locafero.FileTypeFile,
}
results, err := finder.Find(v.fs)
if err != nil {
return "", err
}
if file == "" {
if len(results) == 0 {
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
}
return file, nil
return results[0], nil
}
+129 -171
View File
@@ -10,14 +10,12 @@ import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
"sync"
"testing"
@@ -237,7 +235,7 @@ func initIni() {
}
// make directories for testing
func initDirs(t *testing.T) (string, string, func()) {
func initDirs(t *testing.T) (string, string) {
var (
testDirs = []string{`a a`, `b`, `C_`}
config = `improbable`
@@ -247,38 +245,21 @@ func initDirs(t *testing.T) (string, string, func()) {
testDirs = append(testDirs, `d\d`)
}
root, err := ioutil.TempDir("", "")
require.NoError(t, err, "Failed to create temporary directory")
cleanup := true
defer func() {
if cleanup {
os.Chdir("..")
os.RemoveAll(root)
}
}()
assert.Nil(t, err)
err = os.Chdir(root)
require.Nil(t, err)
root := t.TempDir()
for _, dir := range testDirs {
err = os.Mkdir(dir, 0o750)
assert.Nil(t, err)
innerDir := filepath.Join(root, dir)
err := os.Mkdir(innerDir, 0o750)
require.NoError(t, err)
err = ioutil.WriteFile(
path.Join(dir, config+".toml"),
[]byte("key = \"value is "+dir+"\"\n"),
err = os.WriteFile(
filepath.Join(innerDir, config+".toml"),
[]byte(`key = "value is `+dir+`"`+"\n"),
0o640)
assert.Nil(t, err)
require.NoError(t, err)
}
cleanup = false
return root, config, func() {
os.Chdir("..")
os.RemoveAll(root)
}
return root, config
}
// stubs for PFlag Values
@@ -514,8 +495,8 @@ func TestUnmarshaling(t *testing.T) {
assert.False(t, InConfig("state"))
assert.False(t, InConfig("clothing.hat"))
assert.Equal(t, "steve", Get("name"))
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
assert.Equal(t, []any{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
assert.Equal(t, map[string]any{"jacket": "leather", "trousers": "denim", "pants": map[string]any{"size": "large"}}, Get("clothing"))
assert.Equal(t, 35, Get("age"))
}
@@ -620,10 +601,10 @@ func TestEnv(t *testing.T) {
BindEnv("id")
BindEnv("f", "FOOD", "OLD_FOOD")
testutil.Setenv(t, "ID", "13")
testutil.Setenv(t, "FOOD", "apple")
testutil.Setenv(t, "OLD_FOOD", "banana")
testutil.Setenv(t, "NAME", "crunk")
t.Setenv("ID", "13")
t.Setenv("FOOD", "apple")
t.Setenv("OLD_FOOD", "banana")
t.Setenv("NAME", "crunk")
assert.Equal(t, "13", Get("id"))
assert.Equal(t, "apple", Get("f"))
@@ -639,7 +620,7 @@ func TestMultipleEnv(t *testing.T) {
BindEnv("f", "FOOD", "OLD_FOOD")
testutil.Setenv(t, "OLD_FOOD", "banana")
t.Setenv("OLD_FOOD", "banana")
assert.Equal(t, "banana", Get("f"))
}
@@ -650,7 +631,7 @@ func TestEmptyEnv(t *testing.T) {
BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set environment variable
testutil.Setenv(t, "TYPE", "")
t.Setenv("TYPE", "")
assert.Equal(t, "donut", Get("type"))
assert.Equal(t, "Cake", Get("name"))
@@ -664,7 +645,7 @@ func TestEmptyEnv_Allowed(t *testing.T) {
BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set environment variable
testutil.Setenv(t, "TYPE", "")
t.Setenv("TYPE", "")
assert.Equal(t, "", Get("type"))
assert.Equal(t, "Cake", Get("name"))
@@ -677,9 +658,9 @@ func TestEnvPrefix(t *testing.T) {
BindEnv("id")
BindEnv("f", "FOOD") // not using prefix
testutil.Setenv(t, "FOO_ID", "13")
testutil.Setenv(t, "FOOD", "apple")
testutil.Setenv(t, "FOO_NAME", "crunk")
t.Setenv("FOO_ID", "13")
t.Setenv("FOOD", "apple")
t.Setenv("FOO_NAME", "crunk")
assert.Equal(t, "13", Get("id"))
assert.Equal(t, "apple", Get("f"))
@@ -695,7 +676,7 @@ func TestAutoEnv(t *testing.T) {
AutomaticEnv()
testutil.Setenv(t, "FOO_BAR", "13")
t.Setenv("FOO_BAR", "13")
assert.Equal(t, "13", Get("foo_bar"))
}
@@ -706,7 +687,7 @@ func TestAutoEnvWithPrefix(t *testing.T) {
AutomaticEnv()
SetEnvPrefix("Baz")
testutil.Setenv(t, "BAZ_BAR", "13")
t.Setenv("BAZ_BAR", "13")
assert.Equal(t, "13", Get("bar"))
}
@@ -716,7 +697,7 @@ func TestSetEnvKeyReplacer(t *testing.T) {
AutomaticEnv()
testutil.Setenv(t, "REFRESH_INTERVAL", "30s")
t.Setenv("REFRESH_INTERVAL", "30s")
replacer := strings.NewReplacer("-", "_")
SetEnvKeyReplacer(replacer)
@@ -729,7 +710,7 @@ func TestEnvKeyReplacer(t *testing.T) {
v.AutomaticEnv()
testutil.Setenv(t, "REFRESH_INTERVAL", "30s")
t.Setenv("REFRESH_INTERVAL", "30s")
assert.Equal(t, "30s", v.Get("refresh-interval"))
}
@@ -741,21 +722,21 @@ func TestEnvSubConfig(t *testing.T) {
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
testutil.Setenv(t, "CLOTHING_PANTS_SIZE", "small")
t.Setenv("CLOTHING_PANTS_SIZE", "small")
subv := v.Sub("clothing").Sub("pants")
assert.Equal(t, "small", subv.Get("size"))
// again with EnvPrefix
v.SetEnvPrefix("foo") // will be uppercased automatically
subWithPrefix := v.Sub("clothing").Sub("pants")
testutil.Setenv(t, "FOO_CLOTHING_PANTS_SIZE", "large")
t.Setenv("FOO_CLOTHING_PANTS_SIZE", "large")
assert.Equal(t, "large", subWithPrefix.Get("size"))
}
func TestAllKeys(t *testing.T) {
initConfigs()
ks := sort.StringSlice{
ks := []string{
"title",
"author.bio",
"author.e-mail",
@@ -792,14 +773,14 @@ func TestAllKeys(t *testing.T) {
"name_dotenv",
}
dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
all := map[string]interface{}{
"owner": map[string]interface{}{
all := map[string]any{
"owner": map[string]any{
"organization": "MongoDB",
"bio": "MongoDB Chief Developer Advocate & Hacker at Large",
"dob": dob,
},
"title": "TOML Example",
"author": map[string]interface{}{
"author": map[string]any{
"e-mail": "fake@localhost",
"github": "https://github.com/Unknown",
"name": "Unknown",
@@ -807,28 +788,28 @@ func TestAllKeys(t *testing.T) {
},
"ppu": 0.55,
"eyes": "brown",
"clothing": map[string]interface{}{
"clothing": map[string]any{
"trousers": "denim",
"jacket": "leather",
"pants": map[string]interface{}{"size": "large"},
"pants": map[string]any{"size": "large"},
},
"default": map[string]interface{}{
"default": map[string]any{
"import_path": "gopkg.in/ini.v1",
"name": "ini",
"version": "v1",
},
"id": "0001",
"batters": map[string]interface{}{
"batter": []interface{}{
map[string]interface{}{"type": "Regular"},
map[string]interface{}{"type": "Chocolate"},
map[string]interface{}{"type": "Blueberry"},
map[string]interface{}{"type": "Devil's Food"},
"batters": map[string]any{
"batter": []any{
map[string]any{"type": "Regular"},
map[string]any{"type": "Chocolate"},
map[string]any{"type": "Blueberry"},
map[string]any{"type": "Devil's Food"},
},
},
"hacker": true,
"beard": true,
"hobbies": []interface{}{
"hobbies": []any{
"skateboarding",
"snowboarding",
"go",
@@ -840,13 +821,13 @@ func TestAllKeys(t *testing.T) {
"p_id": "0001",
"p_ppu": "0.55",
"p_name": "Cake",
"p_batters": map[string]interface{}{
"batter": map[string]interface{}{"type": "Regular"},
"p_batters": map[string]any{
"batter": map[string]any{"type": "Regular"},
},
"p_type": "donut",
"foos": []map[string]interface{}{
"foos": []map[string]any{
{
"foo": []map[string]interface{}{
"foo": []map[string]any{
{"key": 1},
{"key": 2},
{"key": 3},
@@ -859,11 +840,7 @@ func TestAllKeys(t *testing.T) {
"name_dotenv": "Cake",
}
allkeys := sort.StringSlice(AllKeys())
allkeys.Sort()
ks.Sort()
assert.Equal(t, ks, allkeys)
assert.ElementsMatch(t, ks, AllKeys())
assert.Equal(t, all, AllSettings())
}
@@ -875,14 +852,10 @@ func TestAllKeysWithEnv(t *testing.T) {
v.BindEnv("foo.bar")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
testutil.Setenv(t, "ID", "13")
testutil.Setenv(t, "FOO_BAR", "baz")
t.Setenv("ID", "13")
t.Setenv("FOO_BAR", "baz")
expectedKeys := sort.StringSlice{"id", "foo.bar"}
expectedKeys.Sort()
keys := sort.StringSlice(v.AllKeys())
keys.Sort()
assert.Equal(t, expectedKeys, keys)
assert.ElementsMatch(t, []string{"id", "foo.bar"}, v.AllKeys())
}
func TestAliasesOfAliases(t *testing.T) {
@@ -955,7 +928,7 @@ func TestUnmarshalWithDecoderOptions(t *testing.T) {
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
// Custom Decode Hook Function
func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
func(rf reflect.Kind, rt reflect.Kind, data any) (any, error) {
if rf != reflect.String || rt != reflect.Map {
return data, nil
}
@@ -1308,7 +1281,7 @@ func TestBoundCaseSensitivity(t *testing.T) {
BindEnv("eYEs", "TURTLE_EYES")
testutil.Setenv(t, "TURTLE_EYES", "blue")
t.Setenv("TURTLE_EYES", "blue")
assert.Equal(t, "blue", Get("eyes"))
@@ -1346,38 +1319,38 @@ func TestFindsNestedKeys(t *testing.T) {
initConfigs()
dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
Set("super", map[string]interface{}{
"deep": map[string]interface{}{
Set("super", map[string]any{
"deep": map[string]any{
"nested": "value",
},
})
expected := map[string]interface{}{
"super": map[string]interface{}{
"deep": map[string]interface{}{
expected := map[string]any{
"super": map[string]any{
"deep": map[string]any{
"nested": "value",
},
},
"super.deep": map[string]interface{}{
"super.deep": map[string]any{
"nested": "value",
},
"super.deep.nested": "value",
"owner.organization": "MongoDB",
"batters.batter": []interface{}{
map[string]interface{}{
"batters.batter": []any{
map[string]any{
"type": "Regular",
},
map[string]interface{}{
map[string]any{
"type": "Chocolate",
},
map[string]interface{}{
map[string]any{
"type": "Blueberry",
},
map[string]interface{}{
map[string]any{
"type": "Devil's Food",
},
},
"hobbies": []interface{}{
"hobbies": []any{
"skateboarding", "snowboarding", "go",
},
"TITLE_DOTENV": "DotEnv Example",
@@ -1385,25 +1358,25 @@ func TestFindsNestedKeys(t *testing.T) {
"NAME_DOTENV": "Cake",
"title": "TOML Example",
"newkey": "remote",
"batters": map[string]interface{}{
"batter": []interface{}{
map[string]interface{}{
"batters": map[string]any{
"batter": []any{
map[string]any{
"type": "Regular",
},
map[string]interface{}{
map[string]any{
"type": "Chocolate",
},
map[string]interface{}{
map[string]any{
"type": "Blueberry",
},
map[string]interface{}{
map[string]any{
"type": "Devil's Food",
},
},
},
"eyes": "brown",
"age": 35,
"owner": map[string]interface{}{
"owner": map[string]any{
"organization": "MongoDB",
"bio": "MongoDB Chief Developer Advocate & Hacker at Large",
"dob": dob,
@@ -1414,10 +1387,10 @@ func TestFindsNestedKeys(t *testing.T) {
"name": "Cake",
"hacker": true,
"ppu": 0.55,
"clothing": map[string]interface{}{
"clothing": map[string]any{
"jacket": "leather",
"trousers": "denim",
"pants": map[string]interface{}{
"pants": map[string]any{
"size": "large",
},
},
@@ -1426,9 +1399,9 @@ func TestFindsNestedKeys(t *testing.T) {
"clothing.trousers": "denim",
"owner.dob": dob,
"beard": true,
"foos": []map[string]interface{}{
"foos": []map[string]any{
{
"foo": []map[string]interface{}{
"foo": []map[string]any{
{
"key": 1,
},
@@ -1462,8 +1435,8 @@ func TestReadBufConfig(t *testing.T) {
assert.False(t, v.InConfig("state"))
assert.False(t, v.InConfig("clothing.hat"))
assert.Equal(t, "steve", v.Get("name"))
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
assert.Equal(t, []any{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
assert.Equal(t, map[string]any{"jacket": "leather", "trousers": "denim", "pants": map[string]any{"size": "large"}}, v.Get("clothing"))
assert.Equal(t, 35, v.Get("age"))
}
@@ -1492,8 +1465,8 @@ func TestIsSet(t *testing.T) {
v.BindEnv("clothing.hat")
v.BindEnv("clothing.hats")
testutil.Setenv(t, "FOO", "bar")
testutil.Setenv(t, "CLOTHING_HAT", "bowler")
t.Setenv("FOO", "bar")
t.Setenv("CLOTHING_HAT", "bowler")
assert.True(t, v.IsSet("eyes")) // in the config file
assert.True(t, v.IsSet("foo")) // in the environment
@@ -1515,30 +1488,28 @@ func TestIsSet(t *testing.T) {
}
func TestDirsSearch(t *testing.T) {
root, config, cleanup := initDirs(t)
defer cleanup()
root, config := initDirs(t)
v := New()
v.SetConfigName(config)
v.SetDefault(`key`, `default`)
entries, err := ioutil.ReadDir(root)
assert.Nil(t, err)
entries, err := os.ReadDir(root)
require.NoError(t, err)
for _, e := range entries {
if e.IsDir() {
v.AddConfigPath(e.Name())
v.AddConfigPath(filepath.Join(root, e.Name()))
}
}
err = v.ReadInConfig()
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
}
func TestWrongDirsSearchNotFound(t *testing.T) {
_, config, cleanup := initDirs(t)
defer cleanup()
_, config := initDirs(t)
v := New()
v.SetConfigName(config)
@@ -1548,7 +1519,7 @@ func TestWrongDirsSearchNotFound(t *testing.T) {
v.AddConfigPath(`thispathaintthere`)
err := v.ReadInConfig()
assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
assert.IsType(t, err, ConfigFileNotFoundError{"", ""})
// Even though config did not load and the error might have
// been ignored by the client, the default still loads
@@ -1556,8 +1527,7 @@ func TestWrongDirsSearchNotFound(t *testing.T) {
}
func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
_, config, cleanup := initDirs(t)
defer cleanup()
_, config := initDirs(t)
v := New()
v.SetConfigName(config)
@@ -1604,12 +1574,10 @@ func TestSub(t *testing.T) {
assert.Equal(t, (*Viper)(nil), subv)
subv = v.Sub("clothing")
assert.Equal(t, subv.parents[0], "clothing")
assert.Equal(t, []string{"clothing"}, subv.parents)
subv = v.Sub("clothing").Sub("pants")
assert.Equal(t, len(subv.parents), 2)
assert.Equal(t, subv.parents[0], "clothing")
assert.Equal(t, subv.parents[1], "pants")
assert.Equal(t, []string{"clothing", "pants"}, subv.parents)
}
var hclWriteExpected = []byte(`"foos" = {
@@ -2201,11 +2169,11 @@ func TestMergeConfigMap(t *testing.T) {
assert(37890)
update := map[string]interface{}{
"Hello": map[string]interface{}{
update := map[string]any{
"Hello": map[string]any{
"Pop": 1234,
},
"World": map[interface{}]interface{}{
"World": map[any]any{
"Rock": 345,
},
}
@@ -2276,19 +2244,19 @@ clothing:
assert.Nil(t, Get("clothing.jacket.price"))
assert.Equal(t, polyester, GetString("clothing.shirt"))
clothingSettings := AllSettings()["clothing"].(map[string]interface{})
clothingSettings := AllSettings()["clothing"].(map[string]any)
assert.Equal(t, "leather", clothingSettings["jacket"])
assert.Equal(t, polyester, clothingSettings["shirt"])
}
func TestDotParameter(t *testing.T) {
initJSON()
// shoud take precedence over batters defined in jsonExample
// should take precedence over batters defined in jsonExample
r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
unmarshalReader(r, v.config)
actual := Get("batters.batter")
expected := []interface{}{map[string]interface{}{"type": "Small"}}
expected := []any{map[string]any{"type": "Small"}}
assert.Equal(t, expected, actual)
}
@@ -2339,17 +2307,17 @@ R = 6
func TestCaseInsensitiveSet(t *testing.T) {
Reset()
m1 := map[string]interface{}{
m1 := map[string]any{
"Foo": 32,
"Bar": map[interface{}]interface{}{
"Bar": map[any]any{
"ABc": "A",
"cDE": "B",
},
}
m2 := map[string]interface{}{
m2 := map[string]any{
"Foo": 52,
"Bar": map[interface{}]interface{}{
"Bar": map[any]any{
"bCd": "A",
"eFG": "B",
},
@@ -2420,7 +2388,7 @@ func TestParseNested(t *testing.T) {
t.Fatalf("unable to decode into struct, %v", err)
}
assert.Equal(t, 1, len(items))
assert.Len(t, items, 1)
assert.Equal(t, 100*time.Millisecond, items[0].Delay)
assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
}
@@ -2438,36 +2406,28 @@ func doTestCaseInsensitive(t *testing.T, typ, config string) {
assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
}
func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
watchDir, err := ioutil.TempDir("", "")
require.Nil(t, err)
func newViperWithConfigFile(t *testing.T) (*Viper, string) {
watchDir := t.TempDir()
configFile := path.Join(watchDir, "config.yaml")
err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0o640)
require.Nil(t, err)
cleanup := func() {
os.RemoveAll(watchDir)
}
err := os.WriteFile(configFile, []byte("foo: bar\n"), 0o640)
require.NoError(t, err)
v := New()
v.SetConfigFile(configFile)
err = v.ReadInConfig()
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, "bar", v.Get("foo"))
return v, configFile, cleanup
return v, configFile
}
func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
watchDir, err := ioutil.TempDir("", "")
require.Nil(t, err)
func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string) {
watchDir := t.TempDir()
dataDir1 := path.Join(watchDir, "data1")
err = os.Mkdir(dataDir1, 0o777)
require.Nil(t, err)
err := os.Mkdir(dataDir1, 0o777)
require.NoError(t, err)
realConfigFile := path.Join(dataDir1, "config.yaml")
t.Logf("Real config file location: %s\n", realConfigFile)
err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
require.Nil(t, err)
cleanup := func() {
os.RemoveAll(watchDir)
}
err = os.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
require.NoError(t, err)
// now, symlink the tm `data1` dir to `data` in the baseDir
os.Symlink(dataDir1, path.Join(watchDir, "data"))
// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
@@ -2478,9 +2438,9 @@ func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func
v := New()
v.SetConfigFile(configFile)
err = v.ReadInConfig()
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, "bar", v.Get("foo"))
return v, watchDir, configFile, cleanup
return v, watchDir, configFile
}
func TestWatchFile(t *testing.T) {
@@ -2491,8 +2451,7 @@ func TestWatchFile(t *testing.T) {
t.Run("file content changed", func(t *testing.T) {
// given a `config.yaml` file being watched
v, configFile, cleanup := newViperWithConfigFile(t)
defer cleanup()
v, configFile := newViperWithConfigFile(t)
_, err := os.Stat(configFile)
require.NoError(t, err)
t.Logf("test config file: %s\n", configFile)
@@ -2507,10 +2466,10 @@ func TestWatchFile(t *testing.T) {
})
v.WatchConfig()
// when overwriting the file and waiting for the custom change notification handler to be triggered
err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0o640)
err = os.WriteFile(configFile, []byte("foo: baz\n"), 0o640)
wg.Wait()
// then the config value should have changed
require.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "baz", v.Get("foo"))
})
@@ -2519,8 +2478,7 @@ func TestWatchFile(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
}
v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
// defer cleanup()
v, watchDir, _ := newViperWithSymlinkedConfigFile(t)
wg := sync.WaitGroup{}
v.WatchConfig()
v.OnConfigChange(func(in fsnotify.Event) {
@@ -2531,16 +2489,16 @@ func TestWatchFile(t *testing.T) {
// when link to another `config.yaml` file
dataDir2 := path.Join(watchDir, "data2")
err := os.Mkdir(dataDir2, 0o777)
require.Nil(t, err)
require.NoError(t, err)
configFile2 := path.Join(dataDir2, "config.yaml")
err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
require.Nil(t, err)
err = os.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
require.NoError(t, err)
// change the symlink using the `ln -sfn` command
err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
require.Nil(t, err)
require.NoError(t, err)
wg.Wait()
// then
require.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "baz", v.Get("foo"))
})
}
@@ -2590,13 +2548,13 @@ func TestKeyDelimiter(t *testing.T) {
err := v.unmarshalReader(r, v.config)
require.NoError(t, err)
values := map[string]interface{}{
"image": map[string]interface{}{
values := map[string]any{
"image": map[string]any{
"repository": "someImage",
"tag": "1.0.0",
},
"ingress": map[string]interface{}{
"annotations": map[string]interface{}{
"ingress": map[string]any{
"annotations": map[string]any{
"traefik.frontend.rule.type": "PathPrefix",
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
},
@@ -2610,13 +2568,13 @@ func TestKeyDelimiter(t *testing.T) {
type config struct {
Charts struct {
Values map[string]interface{}
Values map[string]any
}
}
expected := config{
Charts: struct {
Values map[string]interface{}
Values map[string]any
}{
Values: values,
},
-1
View File
@@ -1,5 +1,4 @@
//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows
// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows
package viper
-1
View File
@@ -1,5 +1,4 @@
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
package viper