Compare commits

...
Author SHA1 Message Date
Mark Sagi-Kazar 80f981797a fix: add back weak string slice hook
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-12-19 22:35:12 +01:00
Mark Sagi-Kazar 6ce751eafe chore: update mapstructure
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-12-19 22:15:13 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 473a3dfc7f fix!: hide struct binding behind a feature flag
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-12-18 19:36:22 +01:00
dependabot[bot]andMárk Sági-Kazár 0b0a1104ba build(deps): bump github.com/pelletier/go-toml/v2 from 2.1.0 to 2.1.1
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.1.0 to 2.1.1.
- [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.1.0...v2.1.1)

---
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-12-15 14:09:38 +01:00
dependabot[bot]andMárk Sági-Kazár fcda1149d6 build(deps): bump github/codeql-action from 2.22.9 to 3.22.11
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.9 to 3.22.11.
- [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/c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2...b374143c1149a9115d881581d29b8390bbcbb59c)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-15 13:41:45 +01:00
dependabot[bot]andMárk Sági-Kazár 9154b900c3 build(deps): bump actions/setup-go from 4.1.0 to 5.0.0
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4.1.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/93397bea11091df50f3d7e59dc26a7711a8bcfbe...0c52d547c9bc32b1aa3301fd7a9cb496313a4491)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 14:48:22 +01:00
dependabot[bot]andMárk Sági-Kazár 08e4a00949 build(deps): bump github/codeql-action from 2.22.8 to 2.22.9
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.8 to 2.22.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/407ffafae6a767df3e0230c3df91b6443ae8df75...c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2)

---
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-12-08 14:48:15 +01:00
Filip KrakowskiandMárk Sági-Kazár fb6eb1e8e9 fix: merge missing struct keys inside UnmarshalExact 2023-12-08 14:44:05 +01:00
Mark Sagi-KazarandMárk Sági-Kazár f5fcb4a104 chore: update crypt
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-12-06 21:48:41 +07:00
f7363633d1 fix isPathShadowedInFlatMap type cast bug (#1585)
* fix isPathShadowedInFlatMap type cast bug

* Add "IsPathShadowedInFlatMap" method unit test

* fix: typo

* add an unit test for flag shadow

---------

Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
2023-12-06 14:41:36 +01:00
Oleksandr RedkoandMárk Sági-Kazár 36a38682ba Review changes 2023-12-06 19:07:50 +07:00
Oleksandr RedkoandMárk Sági-Kazár f0c4ccd6cd fix: gocritic lint issues 2023-12-06 19:07:50 +07:00
Oleksandr RedkoandMárk Sági-Kazár 3a23b80b11 ci: enable test shuffle; fix tests 2023-12-06 19:03:14 +07:00
73dfb94c57 feat: make Unmarshal work with AutomaticEnv
Co-authored-by: Filip Krakowski <krakowski@hhu.de>
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-12-06 18:44:04 +07:00
Mark Sagi-KazarandMárk Sági-Kazár 6ea31ae4ca refactor: move all settings code to a getter
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-12-06 18:44:04 +07:00
Oleksandr RedkoandMárk Sági-Kazár c4dcd31f68 fix: godot lint issues 2023-11-30 21:20:53 +07:00
Ville SkyttäandMárk Sági-Kazár 4c9b2a26ae Note Get* behavior on parse failure
Even though it's kind of evident as these functions don't return an
error, it can still come as a surprise.
2023-11-30 15:21:42 +07:00
Mark Sagi-KazarandMárk Sági-Kazár a4a551fd2a chore: update dependencies
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-11-30 00:37:44 +07:00
Mark Sagi-KazarandMárk Sági-Kazár 464cdab72a refactor: drop fsonitfy wrapper
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-11-30 00:24:02 +07:00
dependabot[bot]andMárk Sági-Kazár 6db0ab274d build(deps): bump github.com/fsnotify/fsnotify from 1.6.0 to 1.7.0
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.6.0...v1.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 23:52:50 +07:00
maxim-konovalovandMárk Sági-Kazár 1c764910bb add info about multiple hosts for remote config 2023-11-29 23:47:27 +07:00
dependabot[bot]andMárk Sági-Kazár 7a9716be0b build(deps): bump github.com/spf13/cast from 1.5.1 to 1.6.0
Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/spf13/cast/releases)
- [Commits](https://github.com/spf13/cast/compare/v1.5.1...v1.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 23:38:31 +07:00
dependabot[bot]andMárk Sági-Kazár cd3d41c170 build(deps): bump github.com/nats-io/nkeys from 0.4.5 to 0.4.6
Bumps [github.com/nats-io/nkeys](https://github.com/nats-io/nkeys) from 0.4.5 to 0.4.6.
- [Release notes](https://github.com/nats-io/nkeys/releases)
- [Changelog](https://github.com/nats-io/nkeys/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nkeys/compare/v0.4.5...v0.4.6)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nkeys
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 21:43:32 +07:00
dependabot[bot]andMárk Sági-Kazár ece813c99a build(deps): bump cachix/install-nix-action from 23 to 24
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 23 to 24.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011...7ac1ec25491415c381d9b62f0657c7a028df52a7)

---
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-11-29 21:43:00 +07:00
dependabot[bot]andMárk Sági-Kazár a831a3736f build(deps): bump actions/dependency-review-action from 3.1.0 to 3.1.4
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3.1.0 to 3.1.4.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/6c5ccdad469c9f8a2996bfecaec55a631a347034...01bc87099ba56df1e897b6874784491ea6309bc4)

---
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-11-29 21:42:34 +07:00
dependabot[bot]andMárk Sági-Kazár 291a41f167 build(deps): bump github.com/spf13/afero from 1.10.0 to 1.11.0
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.10.0...v1.11.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-11-29 21:42:03 +07:00
dependabot[bot]andMárk Sági-Kazár 1a4fa6c906 build(deps): bump github/codeql-action from 2.22.3 to 2.22.8
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.3 to 2.22.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/0116bc2df50751f9724a2e35ef1f24d22f90e4e1...407ffafae6a767df3e0230c3df91b6443ae8df75)

---
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-11-29 21:34:01 +07:00
dependabot[bot]andMárk Sági-Kazár be8a2e81aa build(deps): bump actions/github-script from 6.4.1 to 7.0.1
Bumps [actions/github-script](https://github.com/actions/github-script) from 6.4.1 to 7.0.1.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/d7906e4ad0b1822421a7e6a35d5ca353c962f410...60a0d83039c74a4aee543508d2ffcb1c3799cdea)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 21:33:31 +07:00
dependabot[bot]andMárk Sági-Kazár e9f2018b22 build(deps): bump actions/checkout from 4.1.0 to 4.1.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
- [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/8ade135a41bc03ea155e62e844d188df1ea18608...b4ffde65f46336ab88eb53be808477a3936bae11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 21:32:32 +07:00
Oleksandr RedkoandMárk Sági-Kazár b5daec6e7b test: Replace ifs with asserts to simplify tests 2023-10-14 02:12:35 +02:00
dependabot[bot]andMárk Sági-Kazár b6610e7702 build(deps): bump golang.org/x/net from 0.15.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-14 02:11:19 +02:00
dependabot[bot]andMárk Sági-Kazár 4b59dd6386 build(deps): bump github/codeql-action from 2.21.9 to 2.22.3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.9 to 2.22.3.
- [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/ddccb873888234080b77e9bc2d4764d5ccaaccf9...0116bc2df50751f9724a2e35ef1f24d22f90e4e1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-14 02:10:56 +02:00
Oleksandr RedkoandMárk Sági-Kazár dd9a341aec Rename 2023-10-06 15:06:37 +02:00
Oleksandr RedkoandMárk Sági-Kazár 1d75c512ef chore: rename files according enabled build tags 2023-10-06 15:06:37 +02:00
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
andigandGitHub 21a7fd828e Allow ConfigParseError to unwrap (#1433)
* Allow ConfigParseError to unwrap

* wip

* Avoid pointer type
2023-05-30 12:07:27 +02:00
Márk Sági-Kazár 53cdb5253a chore: improve log messages 2023-05-29 15:43:38 +02:00
Quentin BurgessandMárk Sági-Kazár 24ece16a9a drop std' logger in favor of jww one 2023-05-29 15:43:38 +02:00
Quentin BurgessandMárk Sági-Kazár 98b1b9fd42 add:viper: allow to disable internal log messages 2023-05-29 15:43:38 +02:00
VOrishirneandMárk Sági-Kazár 2ee16310d0 extend viper's pflag binding to stringToInt pflag 2023-05-29 15:34:52 +02:00
Ben PotterandMárk Sági-Kazár 86520578dc docs: Coder uses Viper 2023-05-29 15:32:18 +02:00
dependabot[bot]andMárk Sági-Kazár b850f3448f build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.7 to 2.0.8
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.7 to 2.0.8.
- [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.7...v2.0.8)

---
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-05-29 15:16:39 +02:00
Cédric FabianskiandMárk Sági-Kazár 23b123aba9 doc: add bearer/bearer as projects using Viper 2023-05-29 15:16:28 +02:00
DeleplaceandMárk Sági-Kazár a0fed9f570 Update main image to better handle dark background 2023-05-29 14:52:14 +02:00
dependabot[bot]andMárk Sági-Kazár 143e3bc9d9 build(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3)

---
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-05-29 14:50:48 +02:00
dependabot[bot]andMárk Sági-Kazár 9bfa0ccd19 build(deps): bump github.com/spf13/cast from 1.5.0 to 1.5.1
Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/spf13/cast/releases)
- [Commits](https://github.com/spf13/cast/compare/v1.5.0...v1.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 14:50:32 +02:00
dependabot[bot]andMárk Sági-Kazár 9aa0336962 build(deps): bump github.com/sagikazarmark/crypt from 0.9.0 to 0.10.0
Bumps [github.com/sagikazarmark/crypt](https://github.com/sagikazarmark/crypt) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/sagikazarmark/crypt/compare/v0.9.0...v0.10.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-27 00:13:40 +02:00
Azanul HaqueandMárk Sági-Kazár f74916adfa ADD Meshery to projects 2023-04-19 10:29:14 +02:00
dependabot[bot] adc3a873f0 build(deps): bump github.com/spf13/afero from 1.9.3 to 1.9.5
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.9.3 to 1.9.5.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.9.3...v1.9.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-07 19:39:06 +00:00
Mark Sagi-KazarandMárk Sági-Kazár 995db99714 test: fix test to actually assert something
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-04-06 11:12:29 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 8d1fb59230 chore: update linter
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-04-06 11:12:29 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 59fe4dbc92 ci: add Go 1.20 to build matrix
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-04-06 11:12:29 +02:00
dependabot[bot] e2cc9031b5 build(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
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-04-06 08:52:37 +00:00
dependabot[bot] ff77ba015e build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.6 to 2.0.7
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.6 to 2.0.7.
- [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.6...v2.0.7)

---
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-04-06 08:52:08 +00:00
dependabot[bot] 6393b67401 build(deps): bump golang.org/x/net from 0.4.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.4.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.4.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-06 08:51:38 +00:00
dependabot[bot] 28997ba8e9 build(deps): bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-06 08:51:00 +00:00
dependabot[bot] 1663811850 build(deps): bump mheap/github-action-required-labels from 3 to 4
Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 3 to 4.
- [Release notes](https://github.com/mheap/github-action-required-labels/releases)
- [Commits](https://github.com/mheap/github-action-required-labels/compare/v3...v4)

---
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-04-06 08:50:42 +00:00
Márk Sági-Kazár 34d6318c0b chore: fix typo 2023-03-31 13:08:16 +02:00
Yoofi QuansahandMárk Sági-Kazár c6ba8f4702 fix: fix typo 2023-03-31 13:08:16 +02:00
TaylorOnoandMárk Sági-Kazár 3970ad177e Preserve envPrefix in Sub 2023-02-25 22:17:36 +01:00
TaylorOnoandMárk Sági-Kazár 3f4449054d Sub inerits from automaticEnvApplied and envKeyReplacer
parents are tracked so sub(x).sub(y).GetString(z) looks up x.y.z
2023-02-25 22:17:36 +01:00
c898f59d33 fix: time.Duration slice type conversion (#1498)
* fix DurationSlice

* Fix typo

---------

Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
2023-02-05 18:30:05 +01:00
5182412574 Add DocBlock to WatchConfig (#1467)
* Add DocBlock to WatchConfig

Added a small description of the WatchConfig().
So that you can understand in the ide why you could use it.

* Update viper.go

* update viper.go

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-01-19 16:38:18 +01:00
AstoneandMárk Sági-Kazár 0755477020 feat: add multiple endpoints support for remote 2023-01-19 16:34:22 +01:00
dependabot[bot] a7ad8cfcc4 build(deps): bump github.com/sagikazarmark/crypt from 0.8.0 to 0.9.0
Bumps [github.com/sagikazarmark/crypt](https://github.com/sagikazarmark/crypt) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/sagikazarmark/crypt/releases)
- [Commits](https://github.com/sagikazarmark/crypt/compare/v0.8.0...v0.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-19 15:27:42 +00:00
dependabot[bot] 63630c2fcd build(deps): bump github.com/subosito/gotenv from 1.4.1 to 1.4.2
Bumps [github.com/subosito/gotenv](https://github.com/subosito/gotenv) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/subosito/gotenv/releases)
- [Changelog](https://github.com/subosito/gotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/subosito/gotenv/compare/v1.4.1...v1.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-19 15:20:14 +00:00
Mark Sagi-KazarandMárk Sági-Kazár 996ab10214 ci: update golangci-lint
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-01-19 16:18:37 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 982460e8e1 feat!: drop Go 1.16 support
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-01-19 16:18:37 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 2f5cb62e1f ci: cleanup tags
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-01-19 16:02:48 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 77fc1c3084 feat!: drop toml v1 support
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-01-19 16:02:48 +01:00
Mark Sagi-KazarandMárk Sági-Kazár e42b933dbe feat!: drop yaml v2 support
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2023-01-19 16:02:48 +01:00
dependabot[bot] 9e46b7652f build(deps): bump mheap/github-action-required-labels from 2 to 3
Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 2 to 3.
- [Release notes](https://github.com/mheap/github-action-required-labels/releases)
- [Commits](https://github.com/mheap/github-action-required-labels/compare/v2...v3)

---
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-01-18 23:27:40 +00:00
Adam WilliamsandMárk Sági-Kazár ce73936440 Provide a link to explain what a 12-factor app is 2023-01-08 23:39:46 +01:00
Ahra ChoandMárk Sági-Kazár 9da88d94fa Update badge route 2022-12-26 21:01:12 +01:00
dependabot[bot] ab1367a4d9 build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.5 to 2.0.6
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.5 to 2.0.6.
- [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.5...v2.0.6)

---
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>
2022-12-14 12:41:13 +00:00
dependabot[bot] 41d75b2770 build(deps): bump github.com/magiconair/properties from 1.8.6 to 1.8.7
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.6 to 1.8.7.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Changelog](https://github.com/magiconair/properties/blob/main/CHANGELOG.md)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.6...v1.8.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-08 19:12:13 +00:00
dependabot[bot] 38b5494903 build(deps): bump github.com/spf13/afero from 1.9.2 to 1.9.3
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.9.2 to 1.9.3.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.9.2...v1.9.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-08 19:11:54 +00:00
Mark Sagi-KazarandMárk Sági-Kazár b89e554a96 chore: update crypt
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-06 14:19:33 +01:00
Mark Sagi-KazarandMárk Sági-Kazár db9f89ac41 chore: disable watch on appengine
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-06 03:22:10 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 4b8d14881e refactor: use new Has fsnotify method for event matching
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-04 05:39:26 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 2e99a57324 refactor: rename watch file to unsupported
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-04 03:17:49 +01:00
Mark Sagi-KazarandMárk Sági-Kazár dcb7f30f39 feat: fix compilation for all platforms unsupported by fsnotify
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-04 03:17:49 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 2e04739b68 ci: drop dedicated wasm build
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-04 03:17:49 +01:00
Mark Sagi-KazarandMárk Sági-Kazár b2234f214f ci: add build for aix
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-04 03:17:49 +01:00
Mark Sagi-KazarandMárk Sági-Kazár 52009d3493 feat: disable watcher on aix
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-11-04 03:17:49 +01:00
dependabot[bot]andMárk Sági-Kazár b274f639e0 build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.5.4 to 1.6.0.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.5.4...v1.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-03 17:20:38 +01:00
dependabot[bot]andMárk Sági-Kazár 7c62cfdbac build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
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>
2022-11-03 17:19:37 +01:00
dependabot[bot] f1d2c470bf build(deps): bump github.com/sagikazarmark/crypt from 0.6.0 to 0.7.0
Bumps [github.com/sagikazarmark/crypt](https://github.com/sagikazarmark/crypt) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/sagikazarmark/crypt/releases)
- [Commits](https://github.com/sagikazarmark/crypt/compare/v0.6.0...v0.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-27 21:07:24 +00:00
dependabot[bot]andMárk Sági-Kazár 419fd86e49 build(deps): bump github.com/spf13/afero from 1.8.2 to 1.9.2
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.8.2 to 1.9.2.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.8.2...v1.9.2)

---
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>
2022-09-06 14:46:54 +02:00
Mark Sagi-KazarandMárk Sági-Kazár 91aa484d1d chore: drop support for Go 1.15
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2022-09-06 14:28:48 +02:00
70 changed files with 1996 additions and 2508 deletions
+3
View File
@@ -13,3 +13,6 @@ indent_style = tab
[{Makefile,*.mk}] [{Makefile,*.mk}]
indent_style = tab 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
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 38 KiB

+14 -2
View File
@@ -11,8 +11,20 @@ jobs:
steps: steps:
- name: Check minimum labels - name: Check minimum labels
uses: mheap/github-action-required-labels@v2 uses: mheap/github-action-required-labels@4e9ef4ce8c697cf55716ecbf7f13a3d9e0b6ac6a # v5.1.0
with: with:
mode: minimum mode: minimum
count: 1 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
+78 -22
View File
@@ -2,58 +2,114 @@ name: CI
on: on:
push: push:
branches: branches: [master]
- master
pull_request: pull_request:
jobs: jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- goos: js
goarch: wasm
- goos: aix
goarch: ppc64
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version: "1.21"
- name: Build
run: go build .
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
test: test:
name: Test name: Test
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
# Fail fast is disabled because there are Go version specific features and tests # Fail fast is disabled because there are Go version specific features and tests
# that should be able to fail independently. # that should be able to fail independently.
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.15', '1.16', '1.17', '1.18', '1.19'] go: ["1.19", "1.20", "1.21"]
tags: ['', 'viper_yaml2', 'viper_toml1'] tags: ["", "finder", "viper_bind_struct"]
env:
GOFLAGS: -mod=readonly
steps: steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v3 uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v3
- name: Test - name: Test
run: go test -race -tags '${{ matrix.tags }}' -v ./... run: go test -race -v -tags '${{ matrix.tags }}' -shuffle=on ./...
if: runner.os != 'Windows' if: runner.os != 'Windows'
- name: Test (without race detector) - name: Test (without race detector)
run: go test -tags '${{ matrix.tags }}' -v ./... run: go test -v -tags '${{ matrix.tags }}' -shuffle=on ./...
if: runner.os == 'Windows' if: runner.os == 'Windows'
lint: lint:
name: Lint name: Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
GOFLAGS: -mod=readonly
steps: steps:
- name: Set up Go - name: Checkout repository
uses: actions/setup-go@v3 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
go-version: 1.19
- name: Checkout code - name: Set up Go
uses: actions/checkout@v3 uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version: "1.21"
- name: Lint - name: Lint
uses: golangci/golangci-lint-action@v3 uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
with: with:
version: v1.49 version: v1.54.2
dev:
name: Developer environment
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Nix
uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24
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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Dependency Review
uses: actions/dependency-review-action@01bc87099ba56df1e897b6874784491ea6309bc4 # v3.1.4
+4 -4
View File
@@ -39,11 +39,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # 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). # 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) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11
# ️ Command-line programs to run using the OS shell. # ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -68,5 +68,5 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
comment: comment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v6 - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
script: | script: |
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
comment: comment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v6 - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
script: | script: |
-26
View File
@@ -1,26 +0,0 @@
name: WASM
on:
push:
branches:
- master
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
env:
GOFLAGS: -mod=readonly
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.17'
- name: Checkout code
uses: actions/checkout@v3
- name: Ensure Viper compiles for WASM
run: GOOS=js GOARCH=wasm go build .
+3
View File
@@ -1,4 +1,7 @@
/.devenv/
/.direnv/
/.idea/ /.idea/
/.pre-commit-config.yaml
/bin/ /bin/
/build/ /build/
/var/ /var/
+17 -5
View File
@@ -7,6 +7,16 @@ linters-settings:
- standard - standard
- default - default
- prefix(github.com/spf13/viper) - prefix(github.com/spf13/viper)
gocritic:
# Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
enabled-tags:
- diagnostic
- experimental
- opinionated
- style
disabled-checks:
- importShadow
- unnamedResult
golint: golint:
min-confidence: 0 min-confidence: 0
goimports: goimports:
@@ -16,13 +26,14 @@ linters:
disable-all: true disable-all: true
enable: enable:
- bodyclose - bodyclose
- deadcode
- dogsled - dogsled
- dupl - dupl
- durationcheck - durationcheck
- exhaustive - exhaustive
- exportloopref - exportloopref
- gci - gci
- gocritic
- godot
- gofmt - gofmt
- gofumpt - gofumpt
- goimports - goimports
@@ -43,14 +54,12 @@ linters:
- rowserrcheck - rowserrcheck
- sqlclosecheck - sqlclosecheck
- staticcheck - staticcheck
- structcheck
- stylecheck - stylecheck
- tparallel - tparallel
- typecheck - typecheck
- unconvert - unconvert
- unparam - unparam
- unused - unused
- varcheck
- wastedassign - wastedassign
- whitespace - whitespace
@@ -65,9 +74,7 @@ linters:
# - gochecknoinits # - gochecknoinits
# - gocognit # - gocognit
# - goconst # - goconst
# - gocritic
# - gocyclo # - gocyclo
# - godot
# - gosec # - gosec
# - gosimple # - gosimple
# - ifshort # - ifshort
@@ -83,6 +90,11 @@ linters:
# - goheader # - goheader
# - gomodguard # - gomodguard
# deprecated
# - deadcode
# - structcheck
# - varcheck
# don't enable: # don't enable:
# - asciicheck # - asciicheck
# - funlen # - funlen
+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
+30 -19
View File
@@ -15,8 +15,8 @@ TEST_FORMAT = short-verbose
endif endif
# Dependency versions # Dependency versions
GOTESTSUM_VERSION = 1.8.0 GOTESTSUM_VERSION = 1.9.0
GOLANGCI_VERSION = 1.49.0 GOLANGCI_VERSION = 1.53.3
# Add the ability to override some variables # Add the ability to override some variables
# Use with care # Use with care
@@ -29,11 +29,6 @@ clear: ## Clear the working area and the project
.PHONY: check .PHONY: check
check: test lint ## Run tests and linters 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 ?= ./... TEST_PKGS ?= ./...
.PHONY: test .PHONY: test
@@ -44,20 +39,36 @@ test: bin/gotestsum ## Run tests
@mkdir -p ${BUILD_DIR} @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/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 .PHONY: lint
lint: bin/golangci-lint ## Run linter lint: lint-go lint-yaml
bin/golangci-lint run lint: ## Run linters
.PHONY: fix .PHONY: lint-go
fix: bin/golangci-lint ## Fix lint violations lint-go:
bin/golangci-lint run --fix 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 # Add custom targets here
-include custom.mk -include custom.mk
+62 -15
View File
@@ -8,10 +8,10 @@
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) [![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) [![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 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.15-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) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
**Go configuration with fangs!** **Go configuration with fangs!**
@@ -27,6 +27,10 @@ Many Go projects are built using Viper including:
* [doctl](https://github.com/digitalocean/doctl) * [doctl](https://github.com/digitalocean/doctl)
* [Clairctl](https://github.com/jgsqware/clairctl) * [Clairctl](https://github.com/jgsqware/clairctl)
* [Mercure](https://mercure.rocks) * [Mercure](https://mercure.rocks)
* [Meshery](https://github.com/meshery/meshery)
* [Bearer](https://github.com/bearer/bearer)
* [Coder](https://github.com/coder/coder)
* [Vitess](https://vitess.io/)
## Install ## Install
@@ -40,8 +44,8 @@ go get github.com/spf13/viper
## What is Viper? ## What is Viper?
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors).
to work within an application, and can handle all types of configuration needs It is designed to work within an application, and can handle all types of configuration needs
and formats. It supports: and formats. It supports:
* setting defaults * setting defaults
@@ -137,7 +141,7 @@ if err := viper.ReadInConfig(); err != nil {
// Config file found and successfully parsed // 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 ### Writing Config Files
@@ -218,6 +222,7 @@ These could be from a command line flag, or from your own application logic.
```go ```go
viper.Set("Verbose", true) viper.Set("Verbose", true)
viper.Set("LogFile", LogFile) viper.Set("LogFile", LogFile)
viper.Set("host.port", 5899) // set subset
``` ```
### Registering and Using Aliases ### Registering and Using Aliases
@@ -413,6 +418,8 @@ in a Key/Value store such as etcd or Consul. These values take precedence over
default values, but are overridden by configuration values retrieved from disk, default values, but are overridden by configuration values retrieved from disk,
flags, or environment variables. flags, or environment variables.
Viper supports multiple hosts. To use, pass a list of endpoints separated by `;`. For example `http://127.0.0.1:4001;http://127.0.0.1:4002`.
Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve
configuration from the K/V store, which means that you can store your configuration from the K/V store, which means that you can store your
configuration values encrypted and have them automatically decrypted if you have configuration values encrypted and have them automatically decrypted if you have
@@ -484,6 +491,15 @@ err := viper.ReadRemoteConfig()
Of course, you're allowed to use `SecureRemoteProvider` also 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 ### Remote Key/Value Store Example - Encrypted
```go ```go
@@ -531,24 +547,27 @@ go func(){
In Viper, there are a few ways to get a value depending on the values type. In Viper, there are a few ways to get a value depending on the values type.
The following functions and methods exist: The following functions and methods exist:
* `Get(key string) : interface{}` * `Get(key string) : any`
* `GetBool(key string) : bool` * `GetBool(key string) : bool`
* `GetFloat64(key string) : float64` * `GetFloat64(key string) : float64`
* `GetInt(key string) : int` * `GetInt(key string) : int`
* `GetIntSlice(key string) : []int` * `GetIntSlice(key string) : []int`
* `GetString(key string) : string` * `GetString(key string) : string`
* `GetStringMap(key string) : map[string]interface{}` * `GetStringMap(key string) : map[string]any`
* `GetStringMapString(key string) : map[string]string` * `GetStringMapString(key string) : map[string]string`
* `GetStringSlice(key string) : []string` * `GetStringSlice(key string) : []string`
* `GetTime(key string) : time.Time` * `GetTime(key string) : time.Time`
* `GetDuration(key string) : time.Duration` * `GetDuration(key string) : time.Duration`
* `IsSet(key string) : bool` * `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 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 value if its not found. To check if a given key exists, the `IsSet()` method
has been provided. has been provided.
The zero value will also be returned if the value is set, but fails to parse
as the requested type.
Example: Example:
```go ```go
viper.GetString("logfile") // case-insensitive Setting & Getting viper.GetString("logfile") // case-insensitive Setting & Getting
@@ -706,8 +725,8 @@ etc.
There are two methods to do this: There are two methods to do this:
* `Unmarshal(rawVal interface{}) : error` * `Unmarshal(rawVal any) : error`
* `UnmarshalKey(key string, rawVal interface{}) : error` * `UnmarshalKey(key string, rawVal any) : error`
Example: Example:
@@ -732,9 +751,9 @@ you have to change the delimiter:
```go ```go
v := viper.NewWithOptions(viper.KeyDelimiter("::")) v := viper.NewWithOptions(viper.KeyDelimiter("::"))
v.SetDefault("chart::values", map[string]interface{}{ v.SetDefault("chart::values", map[string]any{
"ingress": map[string]interface{}{ "ingress": map[string]any{
"annotations": map[string]interface{}{ "annotations": map[string]any{
"traefik.frontend.rule.type": "PathPrefix", "traefik.frontend.rule.type": "PathPrefix",
"traefik.ingress.kubernetes.io/ssl-redirect": "true", "traefik.ingress.kubernetes.io/ssl-redirect": "true",
}, },
@@ -743,7 +762,7 @@ v.SetDefault("chart::values", map[string]interface{}{
type config struct { type config struct {
Chart struct{ Chart struct{
Values map[string]interface{} Values map[string]any
} }
} }
@@ -783,7 +802,7 @@ if err != nil {
} }
``` ```
Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. Viper uses [github.com/go-viper/mapstructure](https://github.com/go-viper/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default.
### Decoding custom formats ### Decoding custom formats
@@ -879,3 +898,31 @@ No, you will need to synchronize access to the viper yourself (for example by us
## Troubleshooting ## Troubleshooting
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md). 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
})
}
+2 -3
View File
@@ -1,5 +1,4 @@
//go:build !go1.16 || !finder //go:build !finder
// +build !go1.16 !finder
package viper package viper
@@ -44,7 +43,7 @@ func (v *Viper) searchInPath(in string) (filename string) {
return "" return ""
} }
// Check if file Exists // exists checks if file exists.
func exists(fs afero.Fs, path string) (bool, error) { func exists(fs afero.Fs, path string) (bool, error) {
stat, err := fs.Stat(path) stat, err := fs.Stat(path)
if err == nil { if err == nil {
+38
View File
@@ -0,0 +1,38 @@
//go:build finder
package viper
import (
"fmt"
"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) {
var names []string
if v.configType != "" {
names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)
} else {
names = locafero.NameWithExtensions(v.configName, SupportedExts...)
}
finder := locafero.Finder{
Paths: v.configPaths,
Names: names,
Type: locafero.FileTypeFile,
}
results, err := finder.Find(v.fs)
if err != nil {
return "", err
}
if len(results) == 0 {
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
}
return results[0], nil
}
+2 -2
View File
@@ -30,8 +30,8 @@ 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 // that implements FlagValue.
type pflagValue struct { type pflagValue struct {
flag *pflag.Flag flag *pflag.Flag
} }
+4 -4
View File
@@ -5,9 +5,11 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestBindFlagValueSet(t *testing.T) { func TestBindFlagValueSet(t *testing.T) {
Reset()
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
testValues := map[string]*string{ testValues := map[string]*string{
@@ -29,9 +31,7 @@ func TestBindFlagValueSet(t *testing.T) {
flagValueSet := pflagValueSet{flagSet} flagValueSet := pflagValueSet{flagSet}
err := BindFlagValues(flagValueSet) err := BindFlagValues(flagValueSet)
if err != nil { require.NoError(t, err, "error binding flag set")
t.Fatalf("error binding flag set, %v", err)
}
flagSet.VisitAll(func(flag *pflag.Flag) { flagSet.VisitAll(func(flag *pflag.Flag) {
flag.Value.Set(mutatedTestValues[flag.Name]) flag.Value.Set(mutatedTestValues[flag.Name])
@@ -39,7 +39,7 @@ func TestBindFlagValueSet(t *testing.T) {
}) })
for name, expected := range mutatedTestValues { 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)
})
}
}
+59 -46
View File
@@ -1,72 +1,85 @@
module github.com/spf13/viper module github.com/spf13/viper
go 1.17 go 1.18
require ( require (
github.com/fsnotify/fsnotify v1.5.4 github.com/fsnotify/fsnotify v1.7.0
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1
github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl v1.0.0
github.com/magiconair/properties v1.8.6 github.com/magiconair/properties v1.8.7
github.com/mitchellh/mapstructure v1.5.0 github.com/pelletier/go-toml/v2 v2.1.1
github.com/pelletier/go-toml v1.9.5 github.com/sagikazarmark/crypt v0.17.0
github.com/pelletier/go-toml/v2 v2.0.5 github.com/sagikazarmark/locafero v0.4.0
github.com/sagikazarmark/crypt v0.6.0 github.com/sagikazarmark/slog-shim v0.1.0
github.com/spf13/afero v1.8.2 github.com/spf13/afero v1.11.0
github.com/spf13/cast v1.5.0 github.com/spf13/cast v1.6.0
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.4
github.com/subosito/gotenv v1.4.1 github.com/subosito/gotenv v1.6.0
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
cloud.google.com/go v0.100.2 // indirect cloud.google.com/go v0.110.10 // indirect
cloud.google.com/go/compute v1.6.1 // indirect cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/firestore v1.6.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/armon/go-metrics v0.3.10 // indirect cloud.google.com/go/firestore v1.14.0 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/color v1.13.0 // indirect github.com/fatih/color v1.14.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.8 // indirect github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/gax-go/v2 v2.4.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/hashicorp/consul/api v1.12.0 // 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-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-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.9.7 // indirect github.com/hashicorp/serf v0.10.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nats-io/nats.go v1.31.0 // indirect
github.com/nats-io/nkeys v0.4.6 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pkg/errors v0.9.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
go.etcd.io/etcd/api/v3 v3.5.4 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect go.etcd.io/etcd/api/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/v2 v2.305.4 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/v3 v3.5.4 // indirect go.etcd.io/etcd/client/v2 v2.305.10 // indirect
go.opencensus.io v0.23.0 // indirect go.etcd.io/etcd/client/v3 v3.5.10 // indirect
go.uber.org/atomic v1.7.0 // indirect go.opencensus.io v0.24.0 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/zap v1.17.0 // indirect go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect go.uber.org/zap v1.21.0 // indirect
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect golang.org/x/crypto v0.16.0 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/net v0.19.0 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect golang.org/x/sync v0.5.0 // indirect
google.golang.org/api v0.81.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.153.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.46.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/protobuf v1.28.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
) )
+140 -624
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. // 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 { type Decoder interface {
Decode(b []byte, v map[string]interface{}) error Decode(b []byte, v map[string]any) error
} }
const ( const (
@@ -48,7 +48,7 @@ func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
} }
// Decode calls the underlying Decoder based on the format. // 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() e.mu.RLock()
decoder, ok := e.decoders[format] decoder, ok := e.decoders[format]
e.mu.RUnlock() e.mu.RUnlock()
+15 -27
View File
@@ -1,15 +1,17 @@
package encoding package encoding
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
type decoder struct { 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 { for key, value := range d.v {
v[key] = value v[key] = value
} }
@@ -22,23 +24,17 @@ func TestDecoderRegistry_RegisterDecoder(t *testing.T) {
registry := NewDecoderRegistry() registry := NewDecoderRegistry()
err := registry.RegisterDecoder("myformat", decoder{}) err := registry.RegisterDecoder("myformat", decoder{})
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
}) })
t.Run("AlreadyRegistered", func(t *testing.T) { t.Run("AlreadyRegistered", func(t *testing.T) {
registry := NewDecoderRegistry() registry := NewDecoderRegistry()
err := registry.RegisterDecoder("myformat", decoder{}) err := registry.RegisterDecoder("myformat", decoder{})
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
err = registry.RegisterDecoder("myformat", decoder{}) err = registry.RegisterDecoder("myformat", decoder{})
if err != ErrDecoderFormatAlreadyRegistered { assert.ErrorIs(t, err, ErrDecoderFormatAlreadyRegistered)
t.Fatalf("expected ErrDecoderFormatAlreadyRegistered, got: %v", err)
}
}) })
} }
@@ -46,36 +42,28 @@ func TestDecoderRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
registry := NewDecoderRegistry() registry := NewDecoderRegistry()
decoder := decoder{ decoder := decoder{
v: map[string]interface{}{ v: map[string]any{
"key": "value", "key": "value",
}, },
} }
err := registry.RegisterDecoder("myformat", decoder) err := registry.RegisterDecoder("myformat", decoder)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
v := map[string]interface{}{} v := map[string]any{}
err = registry.Decode("myformat", []byte("key: value"), v) err = registry.Decode("myformat", []byte("key: value"), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(decoder.v, v) { assert.Equal(t, decoder.v, v)
t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v)
}
}) })
t.Run("DecoderNotFound", func(t *testing.T) { t.Run("DecoderNotFound", func(t *testing.T) {
registry := NewDecoderRegistry() registry := NewDecoderRegistry()
v := map[string]interface{}{} v := map[string]any{}
err := registry.Decode("myformat", nil, v) err := registry.Decode("myformat", nil, v)
if err != ErrDecoderNotFound { assert.ErrorIs(t, err, ErrDecoderNotFound)
t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
}
}) })
} }
+3 -3
View File
@@ -15,8 +15,8 @@ const keyDelimiter = "_"
// (commonly called as dotenv format). // (commonly called as dotenv format).
type Codec struct{} type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) { func (Codec) Encode(v map[string]any) ([]byte, error) {
flattened := map[string]interface{}{} flattened := map[string]any{}
flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter) flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
@@ -40,7 +40,7 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil 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 var buf bytes.Buffer
_, err := buf.Write(b) _, err := buf.Write(b)
+14 -22
View File
@@ -1,21 +1,23 @@
package dotenv package dotenv
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// original form of the data // original form of the data.
const original = `# key-value pair const original = `# key-value pair
KEY=value KEY=value
` `
// encoded form of the data // encoded form of the data.
const encoded = `KEY=value const encoded = `KEY=value
` `
// Viper's internal representation // data is Viper's internal representation.
var data = map[string]interface{}{ var data = map[string]any{
"KEY": "value", "KEY": "value",
} }
@@ -23,40 +25,30 @@ func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
} }
func TestCodec_Decode(t *testing.T) { func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) { assert.Equal(t, data, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v) err := codec.Decode([]byte(`invalid data`), v)
if err == nil { require.Error(t, err)
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err) t.Logf("decoding failed as expected: %s", err)
}) })
+9 -9
View File
@@ -7,27 +7,27 @@ import (
) )
// flattenAndMergeMap recursively flattens the given map into a new map // 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 // 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, m map[string]any, prefix, delimiter string) map[string]any {
if shadow != nil && prefix != "" && shadow[prefix] != nil { if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten // prefix is shadowed => nothing more to flatten
return shadow return shadow
} }
if shadow == nil { 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 != "" { if prefix != "" {
prefix += delimiter prefix += delimiter
} }
for k, val := range m { for k, val := range m {
fullKey := prefix + k fullKey := prefix + k
switch val.(type) { switch val := val.(type) {
case map[string]interface{}: case map[string]any:
m2 = val.(map[string]interface{}) m2 = val
case map[interface{}]interface{}: case map[any]any:
m2 = cast.ToStringMap(val) m2 = cast.ToStringMap(val)
default: default:
// immediate value // immediate value
+3 -3
View File
@@ -5,9 +5,9 @@ import (
) )
// Encoder encodes the contents of v into a byte representation. // 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 { type Encoder interface {
Encode(v map[string]interface{}) ([]byte, error) Encode(v map[string]any) ([]byte, error)
} }
const ( const (
@@ -47,7 +47,7 @@ func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
return nil 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() e.mu.RLock()
encoder, ok := e.encoders[format] encoder, ok := e.encoders[format]
e.mu.RUnlock() e.mu.RUnlock()
+13 -24
View File
@@ -2,13 +2,16 @@ package encoding
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
type encoder struct { type encoder struct {
b []byte b []byte
} }
func (e encoder) Encode(_ map[string]interface{}) ([]byte, error) { func (e encoder) Encode(_ map[string]any) ([]byte, error) {
return e.b, nil return e.b, nil
} }
@@ -17,23 +20,17 @@ func TestEncoderRegistry_RegisterEncoder(t *testing.T) {
registry := NewEncoderRegistry() registry := NewEncoderRegistry()
err := registry.RegisterEncoder("myformat", encoder{}) err := registry.RegisterEncoder("myformat", encoder{})
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
}) })
t.Run("AlreadyRegistered", func(t *testing.T) { t.Run("AlreadyRegistered", func(t *testing.T) {
registry := NewEncoderRegistry() registry := NewEncoderRegistry()
err := registry.RegisterEncoder("myformat", encoder{}) err := registry.RegisterEncoder("myformat", encoder{})
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
err = registry.RegisterEncoder("myformat", encoder{}) err = registry.RegisterEncoder("myformat", encoder{})
if err != ErrEncoderFormatAlreadyRegistered { assert.ErrorIs(t, err, ErrEncoderFormatAlreadyRegistered)
t.Fatalf("expected ErrEncoderFormatAlreadyRegistered, got: %v", err)
}
}) })
} }
@@ -45,26 +42,18 @@ func TestEncoderRegistry_Decode(t *testing.T) {
} }
err := registry.RegisterEncoder("myformat", encoder) err := registry.RegisterEncoder("myformat", encoder)
if err != nil { require.NoError(t, err)
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 { require.NoError(t, err)
t.Fatal(err)
}
if string(b) != "key: value" { assert.Equal(t, "key: value", string(b))
t.Fatalf("expected 'key: value', got: %#v", string(b))
}
}) })
t.Run("EncoderNotFound", func(t *testing.T) { t.Run("EncoderNotFound", func(t *testing.T) {
registry := NewEncoderRegistry() registry := NewEncoderRegistry()
_, err := registry.Encode("myformat", map[string]interface{}{"key": "value"}) _, err := registry.Encode("myformat", map[string]any{"key": "value"})
if err != ErrEncoderNotFound { assert.ErrorIs(t, 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? // TODO: add printer config to the codec?
type Codec struct{} 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) b, err := json.Marshal(v)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -35,6 +35,6 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil 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) return hcl.Unmarshal(b, &v)
} }
+28 -36
View File
@@ -1,11 +1,13 @@
package hcl package hcl
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// original form of the data // original form of the data.
const original = `# key-value pair const original = `# key-value pair
"key" = "value" "key" = "value"
@@ -26,7 +28,7 @@ nested map
"list" = ["item1", "item2", "item3"] "list" = ["item1", "item2", "item3"]
}` }`
// encoded form of the data // encoded form of the data.
const encoded = `"key" = "value" const encoded = `"key" = "value"
"list" = ["item1", "item2", "item3"] "list" = ["item1", "item2", "item3"]
@@ -41,28 +43,28 @@ const encoded = `"key" = "value"
"list" = ["item1", "item2", "item3"] "list" = ["item1", "item2", "item3"]
}` }`
// decoded form of the data // decoded form of the data.
// //
// in case of HCL it's slightly different from Viper's internal representation // In case of HCL it's slightly different from Viper's internal representation
// (eg. map is decoded into a list of maps) // (e.g. map is decoded into a list of maps).
var decoded = map[string]interface{}{ var decoded = map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
}, },
"map": []map[string]interface{}{ "map": []map[string]any{
{ {
"key": "value", "key": "value",
}, },
}, },
"nested_map": []map[string]interface{}{ "nested_map": []map[string]any{
{ {
"map": []map[string]interface{}{ "map": []map[string]any{
{ {
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
@@ -73,21 +75,21 @@ var decoded = map[string]interface{}{
}, },
} }
// Viper's internal representation // data is Viper's internal representation.
var data = map[string]interface{}{ var data = map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
}, },
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
"nested_map": map[string]interface{}{ "nested_map": map[string]any{
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
@@ -100,40 +102,30 @@ func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
} }
func TestCodec_Decode(t *testing.T) { func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(decoded, v) { assert.Equal(t, decoded, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v) err := codec.Decode([]byte(`invalid data`), v)
if err == nil { require.Error(t, err)
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err) t.Logf("decoding failed as expected: %s", err)
}) })
+3 -3
View File
@@ -19,11 +19,11 @@ type Codec struct {
LoadOptions LoadOptions 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() cfg := ini.Empty()
ini.PrettyFormat = false ini.PrettyFormat = false
flattened := map[string]interface{}{} flattened := map[string]any{}
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter()) flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
@@ -62,7 +62,7 @@ func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil 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) cfg := ini.Empty(c.LoadOptions)
err := cfg.Append(b) err := cfg.Append(b)
+26 -38
View File
@@ -1,11 +1,13 @@
package ini package ini
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// original form of the data // original form of the data.
const original = `; key-value pair const original = `; key-value pair
key=value ; key-value pair key=value ; key-value pair
@@ -15,30 +17,30 @@ key=%(key)s
` `
// encoded form of the data // encoded form of the data.
const encoded = `key=value const encoded = `key=value
[map] [map]
key=value key=value
` `
// decoded form of the data // decoded form of the data.
// //
// in case of INI it's slightly different from Viper's internal representation // In case of INI it's slightly different from Viper's internal representation
// (eg. top level keys land in a section called default) // (e.g. top level keys land in a section called default).
var decoded = map[string]interface{}{ var decoded = map[string]any{
"DEFAULT": map[string]interface{}{ "DEFAULT": map[string]any{
"key": "value", "key": "value",
}, },
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
} }
// Viper's internal representation // data is Viper's internal representation.
var data = map[string]interface{}{ var data = map[string]any{
"key": "value", "key": "value",
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
} }
@@ -48,35 +50,27 @@ func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}) })
t.Run("Default", func(t *testing.T) { t.Run("Default", func(t *testing.T) {
codec := Codec{} codec := Codec{}
data := map[string]interface{}{ data := map[string]any{
"default": map[string]interface{}{ "default": map[string]any{
"key": "value", "key": "value",
}, },
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
} }
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}) })
} }
@@ -84,27 +78,21 @@ func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(decoded, v) { assert.Equal(t, decoded, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v) err := codec.Decode([]byte(`invalid data`), v)
if err == nil { require.Error(t, err)
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err) t.Logf("decoding failed as expected: %s", err)
}) })
+13 -13
View File
@@ -15,22 +15,22 @@ import (
// In case intermediate keys do not exist, or map to a non-map value, // 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: // a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified! // 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 { for _, k := range path {
m2, ok := m[k] m2, ok := m[k]
if !ok { if !ok {
// intermediate key does not exist // intermediate key does not exist
// => create it and continue from there // => create it and continue from there
m3 := make(map[string]interface{}) m3 := make(map[string]any)
m[k] = m3 m[k] = m3
m = m3 m = m3
continue continue
} }
m3, ok := m2.(map[string]interface{}) m3, ok := m2.(map[string]any)
if !ok { if !ok {
// intermediate key is a value // intermediate key is a value
// => replace with a new map // => replace with a new map
m3 = make(map[string]interface{}) m3 = make(map[string]any)
m[k] = m3 m[k] = m3
} }
// continue search from here // 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 // 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 // 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, m map[string]any, prefix, delimiter string) map[string]any {
if shadow != nil && prefix != "" && shadow[prefix] != nil { if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten // prefix is shadowed => nothing more to flatten
return shadow return shadow
} }
if shadow == nil { 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 != "" { if prefix != "" {
prefix += delimiter prefix += delimiter
} }
for k, val := range m { for k, val := range m {
fullKey := prefix + k fullKey := prefix + k
switch val.(type) { switch val := val.(type) {
case map[string]interface{}: case map[string]any:
m2 = val.(map[string]interface{}) m2 = val
case map[interface{}]interface{}: case map[any]any:
m2 = cast.ToStringMap(val) m2 = cast.ToStringMap(val)
default: default:
// immediate value // immediate value
+3 -3
View File
@@ -20,12 +20,12 @@ type Codec struct {
Properties *properties.Properties 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 { if c.Properties == nil {
c.Properties = properties.NewProperties() c.Properties = properties.NewProperties()
} }
flattened := map[string]interface{}{} flattened := map[string]any{}
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter()) flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
@@ -54,7 +54,7 @@ func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil 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 var err error
c.Properties, err = properties.Load(b, properties.UTF8) c.Properties, err = properties.Load(b, properties.UTF8)
if err != nil { if err != nil {
+19 -33
View File
@@ -1,25 +1,27 @@
package javaproperties package javaproperties
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// original form of the data // original form of the data.
const original = `#key-value pair const original = `#key-value pair
key = value key = value
map.key = value map.key = value
` `
// encoded form of the data // encoded form of the data.
const encoded = `key = value const encoded = `key = value
map.key = value map.key = value
` `
// Viper's internal representation // data is Viper's internal representation.
var data = map[string]interface{}{ var data = map[string]any{
"key": "value", "key": "value",
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
} }
@@ -28,29 +30,21 @@ func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
} }
func TestCodec_Decode(t *testing.T) { func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) { assert.Equal(t, data, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
@@ -58,32 +52,24 @@ func TestCodec_Decode(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
codec.Decode([]byte(``), v) codec.Decode([]byte(``), v)
if len(v) > 0 { assert.Empty(t, v)
t.Fatalf("expected map to be empty when data is invalid\nactual: %#v", v)
}
}) })
} }
func TestCodec_DecodeEncode(t *testing.T) { func TestCodec_DecodeEncode(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if original != string(b) { assert.Equal(t, original, string(b))
t.Fatalf("encoded value does not match the original\nactual: %#v\nexpected: %#v", string(b), original)
}
} }
+13 -13
View File
@@ -15,22 +15,22 @@ import (
// In case intermediate keys do not exist, or map to a non-map value, // 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: // a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified! // 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 { for _, k := range path {
m2, ok := m[k] m2, ok := m[k]
if !ok { if !ok {
// intermediate key does not exist // intermediate key does not exist
// => create it and continue from there // => create it and continue from there
m3 := make(map[string]interface{}) m3 := make(map[string]any)
m[k] = m3 m[k] = m3
m = m3 m = m3
continue continue
} }
m3, ok := m2.(map[string]interface{}) m3, ok := m2.(map[string]any)
if !ok { if !ok {
// intermediate key is a value // intermediate key is a value
// => replace with a new map // => replace with a new map
m3 = make(map[string]interface{}) m3 = make(map[string]any)
m[k] = m3 m[k] = m3
} }
// continue search from here // 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 // 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 // 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, m map[string]any, prefix, delimiter string) map[string]any {
if shadow != nil && prefix != "" && shadow[prefix] != nil { if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten // prefix is shadowed => nothing more to flatten
return shadow return shadow
} }
if shadow == nil { 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 != "" { if prefix != "" {
prefix += delimiter prefix += delimiter
} }
for k, val := range m { for k, val := range m {
fullKey := prefix + k fullKey := prefix + k
switch val.(type) { switch val := val.(type) {
case map[string]interface{}: case map[string]any:
m2 = val.(map[string]interface{}) m2 = val
case map[interface{}]interface{}: case map[any]any:
m2 = cast.ToStringMap(val) m2 = cast.ToStringMap(val)
default: default:
// immediate value // immediate value
+2 -2
View File
@@ -7,11 +7,11 @@ import (
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding. // Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
type Codec struct{} 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? // TODO: expose prefix and indent in the Codec as setting?
return json.MarshalIndent(v, "", " ") 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) return json.Unmarshal(b, &v)
} }
+18 -26
View File
@@ -1,11 +1,13 @@
package json package json
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// encoded form of the data // encoded form of the data.
const encoded = `{ const encoded = `{
"key": "value", "key": "value",
"list": [ "list": [
@@ -28,21 +30,21 @@ const encoded = `{
} }
}` }`
// Viper's internal representation // data is Viper's internal representation.
var data = map[string]interface{}{ var data = map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
}, },
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
"nested_map": map[string]interface{}{ "nested_map": map[string]any{
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
@@ -55,40 +57,30 @@ func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
} }
func TestCodec_Decode(t *testing.T) { func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(encoded), v) err := codec.Decode([]byte(encoded), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) { assert.Equal(t, data, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v) err := codec.Decode([]byte(`invalid data`), v)
if err == nil { require.Error(t, err)
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err) t.Logf("decoding failed as expected: %s", err)
}) })
+5 -28
View File
@@ -1,39 +1,16 @@
//go:build viper_toml1
// +build viper_toml1
package toml package toml
import ( import (
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml/v2"
) )
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding. // Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
type Codec struct{} type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) { func (Codec) Encode(v map[string]any) ([]byte, error) {
t, err := toml.TreeFromMap(v) return toml.Marshal(v)
if err != nil {
return nil, err
}
s, err := t.ToTomlString()
if err != nil {
return nil, err
}
return []byte(s), nil
} }
func (Codec) Decode(b []byte, v map[string]interface{}) error { func (Codec) Decode(b []byte, v map[string]any) error {
tree, err := toml.LoadBytes(b) return toml.Unmarshal(b, &v)
if err != nil {
return err
}
tmap := tree.ToMap()
for key, value := range tmap {
v[key] = value
}
return nil
} }
-19
View File
@@ -1,19 +0,0 @@
//go:build !viper_toml1
// +build !viper_toml1
package toml
import (
"github.com/pelletier/go-toml/v2"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return toml.Marshal(v)
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return toml.Unmarshal(b, &v)
}
-108
View File
@@ -1,108 +0,0 @@
//go:build !viper_toml1
// +build !viper_toml1
package toml
import (
"reflect"
"testing"
)
// original form of the data
const original = `# key-value pair
key = "value"
list = ["item1", "item2", "item3"]
[map]
key = "value"
# nested
# map
[nested_map]
[nested_map.map]
key = "value"
list = [
"item1",
"item2",
"item3",
]
`
// encoded form of the data
const encoded = `key = 'value'
list = ['item1', 'item2', 'item3']
[map]
key = 'value'
[nested_map]
[nested_map.map]
key = 'value'
list = ['item1', 'item2', 'item3']
`
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}
+25 -37
View File
@@ -1,14 +1,13 @@
//go:build viper_toml1
// +build viper_toml1
package toml package toml
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// original form of the data // original form of the data.
const original = `# key-value pair const original = `# key-value pair
key = "value" key = "value"
list = ["item1", "item2", "item3"] list = ["item1", "item2", "item3"]
@@ -28,35 +27,34 @@ list = [
] ]
` `
// encoded form of the data // encoded form of the data.
const encoded = `key = "value" const encoded = `key = 'value'
list = ["item1", "item2", "item3"] list = ['item1', 'item2', 'item3']
[map] [map]
key = "value" key = 'value'
[nested_map] [nested_map]
[nested_map.map]
[nested_map.map] key = 'value'
key = "value" list = ['item1', 'item2', 'item3']
list = ["item1", "item2", "item3"]
` `
// Viper's internal representation // data is Viper's internal representation.
var data = map[string]interface{}{ var data = map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
}, },
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
}, },
"nested_map": map[string]interface{}{ "nested_map": map[string]any{
"map": map[string]interface{}{ "map": map[string]any{
"key": "value", "key": "value",
"list": []interface{}{ "list": []any{
"item1", "item1",
"item2", "item2",
"item3", "item3",
@@ -69,40 +67,30 @@ func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
} }
func TestCodec_Decode(t *testing.T) { func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) { assert.Equal(t, data, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v) err := codec.Decode([]byte(`invalid data`), v)
if err == nil { require.Error(t, err)
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err) t.Logf("decoding failed as expected: %s", err)
}) })
+3 -3
View File
@@ -1,14 +1,14 @@
package yaml package yaml
// import "gopkg.in/yaml.v2" import "gopkg.in/yaml.v3"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding. // Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
type Codec struct{} 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) 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) return yaml.Unmarshal(b, &v)
} }
+97 -18
View File
@@ -1,48 +1,127 @@
package yaml package yaml
import ( import (
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// original form of the data.
const original = `# key-value pair
key: value
list:
- item1
- item2
- item3
map:
key: value
# nested
# map
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// encoded form of the data.
const encoded = `key: value
list:
- item1
- item2
- item3
map:
key: value
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// decoded form of the data.
//
// In case of YAML it's slightly different from Viper's internal representation
// (e.g. map is decoded into a map with interface key).
var decoded = map[string]any{
"key": "value",
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []any{
"item1",
"item2",
"item3",
},
},
},
}
// data is Viper's internal representation.
var data = map[string]any{
"key": "value",
"list": []any{
"item1",
"item2",
"item3",
},
"map": map[string]any{
"key": "value",
},
"nested_map": map[string]any{
"map": map[string]any{
"key": "value",
"list": []any{
"item1",
"item2",
"item3",
},
},
},
}
func TestCodec_Encode(t *testing.T) { func TestCodec_Encode(t *testing.T) {
codec := Codec{} codec := Codec{}
b, err := codec.Encode(data) b, err := codec.Encode(data)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if encoded != string(b) { assert.Equal(t, encoded, string(b))
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
} }
func TestCodec_Decode(t *testing.T) { func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(original), v) err := codec.Decode([]byte(original), v)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
if !reflect.DeepEqual(decoded, v) { assert.Equal(t, decoded, v)
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
}
}) })
t.Run("InvalidData", func(t *testing.T) { t.Run("InvalidData", func(t *testing.T) {
codec := Codec{} codec := Codec{}
v := map[string]interface{}{} v := map[string]any{}
err := codec.Decode([]byte(`invalid data`), v) err := codec.Decode([]byte(`invalid data`), v)
if err == nil { require.Error(t, err)
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err) t.Logf("decoding failed as expected: %s", err)
}) })
-14
View File
@@ -1,14 +0,0 @@
//go:build viper_yaml2
// +build viper_yaml2
package yaml
import yamlv2 "gopkg.in/yaml.v2"
var yaml = struct {
Marshal func(in interface{}) (out []byte, err error)
Unmarshal func(in []byte, out interface{}) (err error)
}{
Marshal: yamlv2.Marshal,
Unmarshal: yamlv2.Unmarshal,
}
-91
View File
@@ -1,91 +0,0 @@
//go:build viper_yaml2
// +build viper_yaml2
package yaml
// original form of the data
const original = `# key-value pair
key: value
list:
- item1
- item2
- item3
map:
key: value
# nested
# map
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// encoded form of the data
const encoded = `key: value
list:
- item1
- item2
- item3
map:
key: value
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// decoded form of the data
//
// 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{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[interface{}]interface{}{
"key": "value",
},
"nested_map": map[interface{}]interface{}{
"map": map[interface{}]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
-14
View File
@@ -1,14 +0,0 @@
//go:build !viper_yaml2
// +build !viper_yaml2
package yaml
import yamlv3 "gopkg.in/yaml.v3"
var yaml = struct {
Marshal func(in interface{}) (out []byte, err error)
Unmarshal func(in []byte, out interface{}) (err error)
}{
Marshal: yamlv3.Marshal,
Unmarshal: yamlv3.Unmarshal,
}
-91
View File
@@ -1,91 +0,0 @@
//go:build !viper_yaml2
// +build !viper_yaml2
package yaml
// original form of the data
const original = `# key-value pair
key: value
list:
- item1
- item2
- item3
map:
key: value
# nested
# map
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// encoded form of the data
const encoded = `key: value
list:
- item1
- item2
- item3
map:
key: value
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// decoded form of the data
//
// 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{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
+5
View File
@@ -0,0 +1,5 @@
//go:build viper_bind_struct
package features
const BindStruct = true
+5
View File
@@ -0,0 +1,5 @@
//go:build !viper_bind_struct
package features
const BindStruct = false
-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 package viper
import ( 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: // Logger is a unified interface for various logging use cases and practices, including:
// - leveled logging // - leveled logging
// - structured logging // - structured logging
//
// Deprecated: use `log/slog` instead.
type Logger interface { type Logger interface {
// Trace logs a Trace event. // Trace logs a Trace event.
// //
// Even more fine-grained information than Debug events. // Even more fine-grained information than Debug events.
// Loggers not supporting this level should fall back to Debug. // 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. // Debug logs a Debug event.
// //
// A verbose series of information events. // A verbose series of information events.
// They are useful when debugging the system. // They are useful when debugging the system.
Debug(msg string, keyvals ...interface{}) Debug(msg string, keyvals ...any)
// Info logs an Info event. // Info logs an Info event.
// //
// General information about what's happening inside the system. // 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. // Warn logs a Warn(ing) event.
// //
// Non-critical events that should be looked at. // Non-critical events that should be looked at.
Warn(msg string, keyvals ...interface{}) Warn(msg string, keyvals ...any)
// Error logs an Error event. // Error logs an Error event.
// //
// Critical events that require immediate attention. // Critical events that require immediate attention.
// Loggers commonly provide Fatal and Panic levels above Error level, // Loggers commonly provide Fatal and Panic levels above Error level,
// but exiting and panicing is out of scope for a logging library. // but exiting and panicking is out of scope for a logging library.
Error(msg string, keyvals ...interface{}) Error(msg string, keyvals ...any)
} }
type jwwLogger struct{} // WithLogger sets a custom logger.
func WithLogger(l *slog.Logger) Option {
func (jwwLogger) Trace(msg string, keyvals ...interface{}) { return optionFunc(func(v *Viper) {
jww.TRACE.Printf(jwwLogMessage(msg, keyvals...)) v.logger = l
})
} }
func (jwwLogger) Debug(msg string, keyvals ...interface{}) { type discardHandler struct{}
jww.DEBUG.Printf(jwwLogMessage(msg, keyvals...))
func (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool {
return false
} }
func (jwwLogger) Info(msg string, keyvals ...interface{}) { func (n *discardHandler) Handle(_ context.Context, _ slog.Record) error {
jww.INFO.Printf(jwwLogMessage(msg, keyvals...)) return nil
} }
func (jwwLogger) Warn(msg string, keyvals ...interface{}) { func (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler {
jww.WARN.Printf(jwwLogMessage(msg, keyvals...)) return n
} }
func (jwwLogger) Error(msg string, keyvals ...interface{}) { func (n *discardHandler) WithGroup(_ string) slog.Handler {
jww.ERROR.Printf(jwwLogMessage(msg, keyvals...)) return n
}
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
} }
+22 -26
View File
@@ -1,7 +1,6 @@
package viper package viper
import ( import (
"fmt"
"strings" "strings"
"testing" "testing"
@@ -46,11 +45,11 @@ func TestNestedOverrides(t *testing.T) {
deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4) deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)
// Case 4: key:value overridden by a map // 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} 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(4, v.Get("tom.size")) // "tom.size" should still be reachable
assert.Equal(10, v.Get("tom.age")) // new value should be there 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 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 = override(assert, "tom.size", 4, "tom", map[string]any{"age": 10})
assert.Nil(v.Get("tom.size")) assert.Nil(v.Get("tom.size"))
assert.Equal(10, v.Get("tom.age")) assert.Equal(10, v.Get("tom.age"))
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) 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) 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) return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
} }
@@ -94,11 +93,11 @@ func override(assert *assert.Assertions, firstPath string, firstValue interface{
// //
// After each assignment, the value is checked, retrieved both by its full path // After each assignment, the value is checked, retrieved both by its full path
// and by its key sequence (successive maps). // 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() v := New()
firstKeys := strings.Split(firstPath, v.keyDelim) firstKeys := strings.Split(firstPath, v.keyDelim)
if assert == nil || if assert == nil ||
len(firstKeys) == 0 || len(firstKeys[0]) == 0 { len(firstKeys) == 0 || firstKeys[0] == "" {
return v return v
} }
@@ -116,7 +115,7 @@ func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, fir
// Override and check new value // Override and check new value
secondKeys := strings.Split(secondPath, v.keyDelim) secondKeys := strings.Split(secondPath, v.keyDelim)
if len(secondKeys) == 0 || len(secondKeys[0]) == 0 { if len(secondKeys) == 0 || secondKeys[0] == "" {
return v return v
} }
v.Set(secondPath, secondValue) v.Set(secondPath, secondValue)
@@ -127,15 +126,15 @@ func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, fir
} }
// deepCheckValue checks that all given keys correspond to a valid path in the // 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 // 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 || if assert == nil || v == nil ||
len(keys) == 0 || len(keys[0]) == 0 { len(keys) == 0 || keys[0] == "" {
return return
} }
// init // init
var val interface{} var val any
var ms string var ms string
switch l { switch l {
case defaultLayer: case defaultLayer:
@@ -147,28 +146,25 @@ func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string,
} }
// loop through map // loop through map
var m map[string]interface{} var m map[string]any
err := false
for _, k := range keys { for _, k := range keys {
if val == nil { 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 return
} }
// deep scan of the map to get the final value // deep scan of the map to get the final value
switch val.(type) { switch val := val.(type) {
case map[interface{}]interface{}: case map[any]any:
m = cast.ToStringMap(val) m = cast.ToStringMap(val)
case map[string]interface{}: case map[string]any:
m = val.(map[string]interface{}) m = val
default: default:
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms)) assert.Failf("%s is not a map[string]any", ms)
return return
} }
ms = ms + "[\"" + k + "\"]" ms = ms + "[\"" + k + "\"]"
val = m[k] val = m[k]
} }
if !err { assert.Equal(value, val)
assert.Equal(value, val)
}
} }
+14 -8
View File
@@ -10,6 +10,7 @@ import (
"bytes" "bytes"
"io" "io"
"os" "os"
"strings"
crypt "github.com/sagikazarmark/crypt/config" crypt "github.com/sagikazarmark/crypt/config"
@@ -75,6 +76,7 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
var cm crypt.ConfigManager var cm crypt.ConfigManager
var err error var err error
endpoints := strings.Split(rp.Endpoint(), ";")
if rp.SecretKeyring() != "" { if rp.SecretKeyring() != "" {
var kr *os.File var kr *os.File
kr, err = os.Open(rp.SecretKeyring()) kr, err = os.Open(rp.SecretKeyring())
@@ -84,24 +86,28 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
defer kr.Close() defer kr.Close()
switch rp.Provider() { switch rp.Provider() {
case "etcd": case "etcd":
cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr) cm, err = crypt.NewEtcdConfigManager(endpoints, kr)
case "etcd3": case "etcd3":
cm, err = crypt.NewEtcdV3ConfigManager([]string{rp.Endpoint()}, kr) cm, err = crypt.NewEtcdV3ConfigManager(endpoints, kr)
case "firestore": case "firestore":
cm, err = crypt.NewFirestoreConfigManager([]string{rp.Endpoint()}, kr) cm, err = crypt.NewFirestoreConfigManager(endpoints, kr)
case "nats":
cm, err = crypt.NewNatsConfigManager(endpoints, kr)
default: default:
cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr) cm, err = crypt.NewConsulConfigManager(endpoints, kr)
} }
} else { } else {
switch rp.Provider() { switch rp.Provider() {
case "etcd": case "etcd":
cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()}) cm, err = crypt.NewStandardEtcdConfigManager(endpoints)
case "etcd3": case "etcd3":
cm, err = crypt.NewStandardEtcdV3ConfigManager([]string{rp.Endpoint()}) cm, err = crypt.NewStandardEtcdV3ConfigManager(endpoints)
case "firestore": case "firestore":
cm, err = crypt.NewStandardFirestoreConfigManager([]string{rp.Endpoint()}) cm, err = crypt.NewStandardFirestoreConfigManager(endpoints)
case "nats":
cm, err = crypt.NewStandardNatsConfigManager(endpoints)
default: default:
cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()}) cm, err = crypt.NewStandardConsulConfigManager(endpoints)
} }
} }
if err != nil { if err != nil {
+29 -23
View File
@@ -18,6 +18,7 @@ import (
"strings" "strings"
"unicode" "unicode"
slog "github.com/sagikazarmark/slog-shim"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -31,13 +32,18 @@ func (pe ConfigParseError) Error() string {
return fmt.Sprintf("While parsing config: %s", pe.err.Error()) return fmt.Sprintf("While parsing config: %s", pe.err.Error())
} }
// Unwrap returns the wrapped error.
func (pe ConfigParseError) Unwrap() error {
return pe.err
}
// toCaseInsensitiveValue checks if the value is a map; // toCaseInsensitiveValue checks if the value is a map;
// if so, create a copy and lower-case the keys recursively. // 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) { switch v := value.(type) {
case map[interface{}]interface{}: case map[any]any:
value = copyAndInsensitiviseMap(cast.ToStringMap(v)) value = copyAndInsensitiviseMap(cast.ToStringMap(v))
case map[string]interface{}: case map[string]any:
value = copyAndInsensitiviseMap(v) value = copyAndInsensitiviseMap(v)
} }
@@ -46,15 +52,15 @@ func toCaseInsensitiveValue(value interface{}) interface{} {
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of // copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
// any map it makes case insensitive. // any map it makes case insensitive.
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} { func copyAndInsensitiviseMap(m map[string]any) map[string]any {
nm := make(map[string]interface{}) nm := make(map[string]any)
for key, val := range m { for key, val := range m {
lkey := strings.ToLower(key) lkey := strings.ToLower(key)
switch v := val.(type) { switch v := val.(type) {
case map[interface{}]interface{}: case map[any]any:
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v)) nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
case map[string]interface{}: case map[string]any:
nm[lkey] = copyAndInsensitiviseMap(v) nm[lkey] = copyAndInsensitiviseMap(v)
default: default:
nm[lkey] = v nm[lkey] = v
@@ -64,23 +70,23 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
return nm return nm
} }
func insensitiviseVal(val interface{}) interface{} { func insensitiviseVal(val any) any {
switch val.(type) { switch v := val.(type) {
case map[interface{}]interface{}: case map[any]any:
// nested map: cast and recursively insensitivise // nested map: cast and recursively insensitivise
val = cast.ToStringMap(val) val = cast.ToStringMap(val)
insensitiviseMap(val.(map[string]interface{})) insensitiviseMap(val.(map[string]any))
case map[string]interface{}: case map[string]any:
// nested map: recursively insensitivise // nested map: recursively insensitivise
insensitiviseMap(val.(map[string]interface{})) insensitiviseMap(v)
case []interface{}: case []any:
// nested array: recursively insensitivise // nested array: recursively insensitivise
insensitiveArray(val.([]interface{})) insensitiveArray(v)
} }
return val return val
} }
func insensitiviseMap(m map[string]interface{}) { func insensitiviseMap(m map[string]any) {
for key, val := range m { for key, val := range m {
val = insensitiviseVal(val) val = insensitiviseVal(val)
lower := strings.ToLower(key) lower := strings.ToLower(key)
@@ -93,13 +99,13 @@ func insensitiviseMap(m map[string]interface{}) {
} }
} }
func insensitiveArray(a []interface{}) { func insensitiveArray(a []any) {
for i, val := range a { for i, val := range a {
a[i] = insensitiviseVal(val) 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) logger.Info("trying to resolve absolute path", "path", inPath)
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) { if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
@@ -150,7 +156,7 @@ func safeMul(a, b uint) uint {
return c return c
} }
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes // parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.
func parseSizeInBytes(sizeStr string) uint { func parseSizeInBytes(sizeStr string) uint {
sizeStr = strings.TrimSpace(sizeStr) sizeStr = strings.TrimSpace(sizeStr)
lastChar := len(sizeStr) - 1 lastChar := len(sizeStr) - 1
@@ -192,22 +198,22 @@ func parseSizeInBytes(sizeStr string) uint {
// In case intermediate keys do not exist, or map to a non-map value, // 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: // a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified! // 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 { for _, k := range path {
m2, ok := m[k] m2, ok := m[k]
if !ok { if !ok {
// intermediate key does not exist // intermediate key does not exist
// => create it and continue from there // => create it and continue from there
m3 := make(map[string]interface{}) m3 := make(map[string]any)
m[k] = m3 m[k] = m3
m = m3 m = m3
continue continue
} }
m3, ok := m2.(map[string]interface{}) m3, ok := m2.(map[string]any)
if !ok { if !ok {
// intermediate key is a value // intermediate key is a value
// => replace with a new map // => replace with a new map
m3 = make(map[string]interface{}) m3 = make(map[string]any)
m[k] = m3 m[k] = m3
} }
// continue search from here // continue search from here
+18 -27
View File
@@ -13,24 +13,24 @@ package viper
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"testing" "testing"
"github.com/spf13/viper/internal/testutil" slog "github.com/sagikazarmark/slog-shim"
"github.com/stretchr/testify/assert"
) )
func TestCopyAndInsensitiviseMap(t *testing.T) { func TestCopyAndInsensitiviseMap(t *testing.T) {
var ( var (
given = map[string]interface{}{ given = map[string]any{
"Foo": 32, "Foo": 32,
"Bar": map[interface{}]interface{}{ "Bar": map[any]any{
"ABc": "A", "ABc": "A",
"cDE": "B", "cDE": "B",
}, },
} }
expected = map[string]interface{}{ expected = map[string]any{
"foo": 32, "foo": 32,
"bar": map[string]interface{}{ "bar": map[string]any{
"abc": "A", "abc": "A",
"cde": "B", "cde": "B",
}, },
@@ -39,22 +39,15 @@ func TestCopyAndInsensitiviseMap(t *testing.T) {
got := copyAndInsensitiviseMap(given) got := copyAndInsensitiviseMap(given)
if !reflect.DeepEqual(got, expected) { assert.Equal(t, expected, got)
t.Fatalf("Got %q\nexpected\n%q", got, expected) _, ok := given["foo"]
} assert.False(t, ok)
_, ok = given["bar"]
assert.False(t, ok)
if _, ok := given["foo"]; ok { m := given["Bar"].(map[any]any)
t.Fatal("Input map changed") _, ok = m["ABc"]
} assert.True(t, ok)
if _, ok := given["bar"]; ok {
t.Fatal("Input map changed")
}
m := given["Bar"].(map[interface{}]interface{})
if _, ok := m["ABc"]; !ok {
t.Fatal("Input map changed")
}
} }
func TestAbsPathify(t *testing.T) { func TestAbsPathify(t *testing.T) {
@@ -64,8 +57,8 @@ func TestAbsPathify(t *testing.T) {
homer := filepath.Join(home, "homer") homer := filepath.Join(home, "homer")
wd, _ := os.Getwd() wd, _ := os.Getwd()
testutil.Setenv(t, "HOMER_ABSOLUTE_PATH", homer) t.Setenv("HOMER_ABSOLUTE_PATH", homer)
testutil.Setenv(t, "VAR_WITH_RELATIVE_PATH", "relative") t.Setenv("VAR_WITH_RELATIVE_PATH", "relative")
tests := []struct { tests := []struct {
input string input string
@@ -87,9 +80,7 @@ func TestAbsPathify(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
got := absPathify(jwwLogger{}, test.input) got := absPathify(slog.Default(), test.input)
if got != test.output { assert.Equal(t, test.output, got)
t.Errorf("Got %v\nexpected\n%q", got, test.output)
}
} }
} }
+275 -145
View File
@@ -25,7 +25,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@@ -35,7 +34,8 @@ import (
"time" "time"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/mitchellh/mapstructure" "github.com/go-viper/mapstructure/v2"
slog "github.com/sagikazarmark/slog-shim"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cast" "github.com/spf13/cast"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@@ -48,6 +48,7 @@ import (
"github.com/spf13/viper/internal/encoding/json" "github.com/spf13/viper/internal/encoding/json"
"github.com/spf13/viper/internal/encoding/toml" "github.com/spf13/viper/internal/encoding/toml"
"github.com/spf13/viper/internal/encoding/yaml" "github.com/spf13/viper/internal/encoding/yaml"
"github.com/spf13/viper/internal/features"
) )
// ConfigMarshalError happens when failing to marshal the configuration. // ConfigMarshalError happens when failing to marshal the configuration.
@@ -77,7 +78,7 @@ type remoteConfigFactory interface {
WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
} }
// RemoteConfig is optional, see the remote package // RemoteConfig is optional, see the remote package.
var RemoteConfig remoteConfigFactory var RemoteConfig remoteConfigFactory
// UnsupportedConfigError denotes encountering an unsupported // UnsupportedConfigError denotes encountering an unsupported
@@ -102,7 +103,7 @@ func (str UnsupportedRemoteProviderError) Error() string {
// pull the configuration from the remote provider. // pull the configuration from the remote provider.
type RemoteConfigError string type RemoteConfigError string
// Error returns the formatted remote provider error // Error returns the formatted remote provider error.
func (rce RemoteConfigError) Error() string { func (rce RemoteConfigError) Error() string {
return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
} }
@@ -126,7 +127,7 @@ func (faee ConfigFileAlreadyExistsError) Error() string {
} }
// A DecoderConfigOption can be passed to viper.Unmarshal to configure // A DecoderConfigOption can be passed to viper.Unmarshal to configure
// mapstructure.DecoderConfig options // mapstructure.DecoderConfig options.
type DecoderConfigOption func(*mapstructure.DecoderConfig) type DecoderConfigOption func(*mapstructure.DecoderConfig)
// DecodeHook returns a DecoderConfigOption which overrides the default // DecodeHook returns a DecoderConfigOption which overrides the default
@@ -206,10 +207,11 @@ type Viper struct {
envKeyReplacer StringReplacer envKeyReplacer StringReplacer
allowEmptyEnv bool allowEmptyEnv bool
config map[string]interface{} parents []string
override map[string]interface{} config map[string]any
defaults map[string]interface{} override map[string]any
kvstore map[string]interface{} defaults map[string]any
kvstore map[string]any
pflags map[string]FlagValue pflags map[string]FlagValue
env map[string][]string env map[string][]string
aliases map[string]string aliases map[string]string
@@ -217,7 +219,7 @@ type Viper struct {
onConfigChange func(fsnotify.Event) onConfigChange func(fsnotify.Event)
logger Logger logger *slog.Logger
// TODO: should probably be protected with a mutex // TODO: should probably be protected with a mutex
encoderRegistry *encoding.EncoderRegistry encoderRegistry *encoding.EncoderRegistry
@@ -231,15 +233,16 @@ func New() *Viper {
v.configName = "config" v.configName = "config"
v.configPermissions = os.FileMode(0o644) v.configPermissions = os.FileMode(0o644)
v.fs = afero.NewOsFs() v.fs = afero.NewOsFs()
v.config = make(map[string]interface{}) v.config = make(map[string]any)
v.override = make(map[string]interface{}) v.parents = []string{}
v.defaults = make(map[string]interface{}) v.override = make(map[string]any)
v.kvstore = make(map[string]interface{}) v.defaults = make(map[string]any)
v.kvstore = make(map[string]any)
v.pflags = make(map[string]FlagValue) v.pflags = make(map[string]FlagValue)
v.env = make(map[string][]string) v.env = make(map[string][]string)
v.aliases = make(map[string]string) v.aliases = make(map[string]string)
v.typeByDefValue = false v.typeByDefValue = false
v.logger = jwwLogger{} v.logger = slog.New(&discardHandler{})
v.resetEncoding() v.resetEncoding()
@@ -300,10 +303,10 @@ func NewWithOptions(opts ...Option) *Viper {
func Reset() { func Reset() {
v = New() v = New()
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} 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 // TODO: make this lazy initialization instead.
func (v *Viper) resetEncoding() { func (v *Viper) resetEncoding() {
encoderRegistry := encoding.NewEncoderRegistry() encoderRegistry := encoding.NewEncoderRegistry()
decoderRegistry := encoding.NewDecoderRegistry() decoderRegistry := encoding.NewDecoderRegistry()
@@ -419,28 +422,34 @@ type RemoteProvider interface {
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
// SupportedRemoteProviders are universally supported remote providers. // 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) } func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
// OnConfigChange sets the event handler that is called when a config file changes.
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
v.onConfigChange = run v.onConfigChange = run
} }
// WatchConfig starts watching a config file for changes.
func WatchConfig() { v.WatchConfig() } func WatchConfig() { v.WatchConfig() }
// WatchConfig starts watching a config file for changes.
func (v *Viper) WatchConfig() { func (v *Viper) WatchConfig() {
initWG := sync.WaitGroup{} initWG := sync.WaitGroup{}
initWG.Add(1) initWG.Add(1)
go func() { go func() {
watcher, err := newWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
log.Fatal(err) v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err))
os.Exit(1)
} }
defer watcher.Close() defer watcher.Close()
// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
filename, err := v.getConfigFile() filename, err := v.getConfigFile()
if err != nil { if err != nil {
log.Printf("error: %v\n", err) v.logger.Error(fmt.Sprintf("get config file: %s", err))
initWG.Done() initWG.Done()
return return
} }
@@ -463,27 +472,25 @@ func (v *Viper) WatchConfig() {
// we only care about the config file with the following cases: // we only care about the config file with the following cases:
// 1 - if the config file was modified or created // 1 - if the config file was modified or created
// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
const writeOrCreateMask = fsnotify.Write | fsnotify.Create
if (filepath.Clean(event.Name) == configFile && if (filepath.Clean(event.Name) == configFile &&
event.Op&writeOrCreateMask != 0) || (event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||
(currentConfigFile != "" && currentConfigFile != realConfigFile) { (currentConfigFile != "" && currentConfigFile != realConfigFile) {
realConfigFile = currentConfigFile realConfigFile = currentConfigFile
err := v.ReadInConfig() err := v.ReadInConfig()
if err != nil { if err != nil {
log.Printf("error reading config file: %v\n", err) v.logger.Error(fmt.Sprintf("read config file: %s", err))
} }
if v.onConfigChange != nil { if v.onConfigChange != nil {
v.onConfigChange(event) v.onConfigChange(event)
} }
} else if filepath.Clean(event.Name) == configFile && } else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {
event.Op&fsnotify.Remove != 0 {
eventsWG.Done() eventsWG.Done()
return return
} }
case err, ok := <-watcher.Errors: case err, ok := <-watcher.Errors:
if ok { // 'Errors' channel is not closed if ok { // 'Errors' channel is not closed
log.Printf("watcher error: %v\n", err) v.logger.Error(fmt.Sprintf("watcher error: %s", err))
} }
eventsWG.Done() eventsWG.Done()
return return
@@ -518,6 +525,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 { func (v *Viper) mergeWithEnvPrefix(in string) string {
if v.envPrefix != "" { if v.envPrefix != "" {
return strings.ToUpper(v.envPrefix + "_" + in) return strings.ToUpper(v.envPrefix + "_" + in)
@@ -573,12 +586,12 @@ func (v *Viper) AddConfigPath(in string) {
// AddRemoteProvider adds a remote configuration source. // AddRemoteProvider adds a remote configuration source.
// Remote Providers are searched in the order they are added. // 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 // 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 // path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json // To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to // you should set path to /configs and set config name (SetConfigName()) to
// "myapp" // "myapp".
func AddRemoteProvider(provider, endpoint, path string) error { func AddRemoteProvider(provider, endpoint, path string) error {
return v.AddRemoteProvider(provider, endpoint, path) return v.AddRemoteProvider(provider, endpoint, path)
} }
@@ -604,14 +617,14 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
// AddSecureRemoteProvider adds a remote configuration source. // AddSecureRemoteProvider adds a remote configuration source.
// Secure Remote Providers are searched in the order they are added. // 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 // 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 // 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 // path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json // To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to // you should set path to /configs and set config name (SetConfigName()) to
// "myapp" // "myapp".
// Secure Remote Providers are implemented with github.com/bketelsen/crypt // Secure Remote Providers are implemented with github.com/bketelsen/crypt.
func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
} }
@@ -648,7 +661,7 @@ func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
// searchMap recursively searches for a value for path in source map. // searchMap recursively searches for a value for path in source map.
// Returns nil if not found. // Returns nil if not found.
// Note: This assumes that the path entries and map keys are lower cased. // 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 { if len(path) == 0 {
return source return source
} }
@@ -661,13 +674,13 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
} }
// Nested case // Nested case
switch next.(type) { switch next := next.(type) {
case map[interface{}]interface{}: case map[any]any:
return v.searchMap(cast.ToStringMap(next), path[1:]) 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 // Type assertion is safe here since it is only reached
// if the type of `next` is the same as the type being asserted // 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: default:
// got a value but nested key expected, return "nil" for not found // got a value but nested key expected, return "nil" for not found
return nil return nil
@@ -687,7 +700,7 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
// in their keys). // in their keys).
// //
// Note: This assumes that the path entries and map keys are lower cased. // 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 { if len(path) == 0 {
return source return source
} }
@@ -696,11 +709,11 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
for i := len(path); i > 0; i-- { for i := len(path); i > 0; i-- {
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim)) prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
var val interface{} var val any
switch sourceIndexable := source.(type) { switch sourceIndexable := source.(type) {
case []interface{}: case []any:
val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path) val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path)
case map[string]interface{}: case map[string]any:
val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path) val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path)
} }
if val != nil { if val != nil {
@@ -717,11 +730,11 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
// This function is part of the searchIndexableWithPathPrefixes recurring search and // This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes. // should not be called directly from functions other than searchIndexableWithPathPrefixes.
func (v *Viper) searchSliceWithPathPrefixes( func (v *Viper) searchSliceWithPathPrefixes(
sourceSlice []interface{}, sourceSlice []any,
prefixKey string, prefixKey string,
pathIndex int, pathIndex int,
path []string, path []string,
) interface{} { ) any {
// if the prefixKey is not a number or it is out of bounds of the slice // if the prefixKey is not a number or it is out of bounds of the slice
index, err := strconv.Atoi(prefixKey) index, err := strconv.Atoi(prefixKey)
if err != nil || len(sourceSlice) <= index { if err != nil || len(sourceSlice) <= index {
@@ -736,9 +749,9 @@ func (v *Viper) searchSliceWithPathPrefixes(
} }
switch n := next.(type) { switch n := next.(type) {
case map[interface{}]interface{}: case map[any]any:
return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:]) return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
case map[string]interface{}, []interface{}: case map[string]any, []any:
return v.searchIndexableWithPathPrefixes(n, path[pathIndex:]) return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
default: default:
// got a value but nested key expected, do nothing and look for next prefix // got a value but nested key expected, do nothing and look for next prefix
@@ -753,11 +766,11 @@ func (v *Viper) searchSliceWithPathPrefixes(
// This function is part of the searchIndexableWithPathPrefixes recurring search and // This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes. // should not be called directly from functions other than searchIndexableWithPathPrefixes.
func (v *Viper) searchMapWithPathPrefixes( func (v *Viper) searchMapWithPathPrefixes(
sourceMap map[string]interface{}, sourceMap map[string]any,
prefixKey string, prefixKey string,
pathIndex int, pathIndex int,
path []string, path []string,
) interface{} { ) any {
next, ok := sourceMap[prefixKey] next, ok := sourceMap[prefixKey]
if !ok { if !ok {
return nil return nil
@@ -770,9 +783,9 @@ func (v *Viper) searchMapWithPathPrefixes(
// Nested case // Nested case
switch n := next.(type) { switch n := next.(type) {
case map[interface{}]interface{}: case map[any]any:
return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:]) return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
case map[string]interface{}, []interface{}: case map[string]any, []any:
return v.searchIndexableWithPathPrefixes(n, path[pathIndex:]) return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
default: default:
// got a value but nested key expected, do nothing and look for next prefix // got a value but nested key expected, do nothing and look for next prefix
@@ -787,8 +800,8 @@ func (v *Viper) searchMapWithPathPrefixes(
// e.g., if "foo.bar" has a value in the given map, it “shadows” // e.g., if "foo.bar" has a value in the given map, it “shadows”
// //
// "foo.bar.baz" in a lower-priority map // "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string { func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]any) string {
var parentVal interface{} var parentVal any
for i := 1; i < len(path); i++ { for i := 1; i < len(path); i++ {
parentVal = v.searchMap(m, path[0:i]) parentVal = v.searchMap(m, path[0:i])
if parentVal == nil { if parentVal == nil {
@@ -796,9 +809,9 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
return "" return ""
} }
switch parentVal.(type) { switch parentVal.(type) {
case map[interface{}]interface{}: case map[any]any:
continue continue
case map[string]interface{}: case map[string]any:
continue continue
default: default:
// parentVal is a regular value which shadows "path" // parentVal is a regular value which shadows "path"
@@ -813,12 +826,14 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
// e.g., if "foo.bar" has a value in the given map, it “shadows” // e.g., if "foo.bar" has a value in the given map, it “shadows”
// //
// "foo.bar.baz" in a lower-priority map // "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 // unify input map
var m map[string]interface{} var m map[string]interface{}
switch mi.(type) { switch miv := mi.(type) {
case map[string]string, map[string]FlagValue: case map[string]string:
m = cast.ToStringMap(mi) m = castMapStringToMapInterface(miv)
case map[string]FlagValue:
m = castMapFlagToMapInterface(miv)
default: default:
return "" return ""
} }
@@ -882,9 +897,9 @@ func GetViper() *Viper {
// override, flag, env, config file, key/value store, default // override, flag, env, config file, key/value store, default
// //
// Get returns an interface. For a specific value use one of the Get____ methods. // 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) lcaseKey := strings.ToLower(key)
val := v.find(lcaseKey, true) val := v.find(lcaseKey, true)
if val == nil { if val == nil {
@@ -925,6 +940,8 @@ func (v *Viper) Get(key string) interface{} {
return cast.ToStringSlice(val) return cast.ToStringSlice(val)
case []int: case []int:
return cast.ToIntSlice(val) return cast.ToIntSlice(val)
case []time.Duration:
return cast.ToDurationSlice(val)
} }
} }
@@ -943,6 +960,11 @@ func (v *Viper) Sub(key string) *Viper {
} }
if reflect.TypeOf(data).Kind() == reflect.Map { if reflect.TypeOf(data).Kind() == reflect.Map {
subv.parents = append([]string(nil), v.parents...)
subv.parents = append(subv.parents, strings.ToLower(key))
subv.automaticEnvApplied = v.automaticEnvApplied
subv.envPrefix = v.envPrefix
subv.envKeyReplacer = v.envKeyReplacer
subv.config = cast.ToStringMap(data) subv.config = cast.ToStringMap(data)
return subv return subv
} }
@@ -1048,9 +1070,9 @@ func (v *Viper) GetStringSlice(key string) []string {
} }
// GetStringMap returns the value associated with the key as a map of interfaces. // 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)) return cast.ToStringMap(v.Get(key))
} }
@@ -1078,34 +1100,66 @@ func (v *Viper) GetSizeInBytes(key string) uint {
} }
// UnmarshalKey takes a single key and unmarshals it into a Struct. // 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...) 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...)) return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
} }
// Unmarshal unmarshals the config into a Struct. Make sure that the tags // Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set. // 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...) 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...)) keys := v.AllKeys()
if features.BindStruct {
// TODO: make this optional?
structKeys, err := v.decodeStructKeys(rawVal, opts...)
if err != nil {
return err
}
keys = append(keys, structKeys...)
}
// TODO: struct keys should be enough?
return decode(v.getSettings(keys), defaultDecoderConfig(rawVal, opts...))
} }
// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) {
// of time.Duration values & string slices var structKeyMap map[string]any
func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
err := decode(input, defaultDecoderConfig(&structKeyMap, opts...))
if err != nil {
return nil, err
}
flattenedStructKeyMap := v.flattenAndMergeMap(map[string]bool{}, structKeyMap, "")
r := make([]string, 0, len(flattenedStructKeyMap))
for v := range flattenedStructKeyMap {
r = append(r, v)
}
return r, nil
}
// defaultDecoderConfig returns default mapstructure.DecoderConfig with support
// of time.Duration values & string slices.
func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
c := &mapstructure.DecoderConfig{ c := &mapstructure.DecoderConfig{
Metadata: nil, Metadata: nil,
Result: output, Result: output,
WeaklyTypedInput: true, WeaklyTypedInput: true,
DecodeHook: mapstructure.ComposeDecodeHookFunc( DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","), // mapstructure.StringToSliceHookFunc(","),
stringToWeakSliceHookFunc(","),
), ),
} }
for _, opt := range opts { for _, opt := range opts {
@@ -1114,8 +1168,30 @@ func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *maps
return c return c
} }
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality // As of mapstructure v2.0.0 StringToSliceHookFunc checks if the return type is a string slice.
func decode(input interface{}, config *mapstructure.DecoderConfig) error { // This function removes that check.
// TODO: implement a function that checks if the value can be converted to the return type and use it instead.
func stringToWeakSliceHookFunc(sep string) mapstructure.DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{},
) (interface{}, error) {
if f.Kind() != reflect.String || t.Kind() != reflect.Slice {
return data, nil
}
raw := data.(string)
if raw == "" {
return []string{}, nil
}
return strings.Split(raw, sep), nil
}
}
// decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality.
func decode(input any, config *mapstructure.DecoderConfig) error {
decoder, err := mapstructure.NewDecoder(config) decoder, err := mapstructure.NewDecoder(config)
if err != nil { if err != nil {
return err return err
@@ -1125,15 +1201,28 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error {
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct. // in the destination struct.
func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
return v.UnmarshalExact(rawVal, opts...) 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 := defaultDecoderConfig(rawVal, opts...)
config.ErrorUnused = true config.ErrorUnused = true
return decode(v.AllSettings(), config) keys := v.AllKeys()
if features.BindStruct {
// TODO: make this optional?
structKeys, err := v.decodeStructKeys(rawVal, opts...)
if err != nil {
return err
}
keys = append(keys, structKeys...)
}
// TODO: struct keys should be enough?
return decode(v.getSettings(keys), config)
} }
// BindPFlags binds a full flag set to the configuration, using each flag's long // BindPFlags binds a full flag set to the configuration, using each flag's long
@@ -1226,9 +1315,9 @@ func (v *Viper) MustBindEnv(input ...string) {
// corresponds to a flag, the flag's default value is returned. // corresponds to a flag, the flag's default value is returned.
// //
// Note: this assumes a lower-cased key given. // 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 ( var (
val interface{} val any
exists bool exists bool
path = strings.Split(lcaseKey, v.keyDelim) path = strings.Split(lcaseKey, v.keyDelim)
nested = len(path) > 1 nested = len(path) > 1
@@ -1271,8 +1360,15 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
s = strings.TrimSuffix(s, "]") s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s) res, _ := readAsCSV(s)
return cast.ToIntSlice(res) return cast.ToIntSlice(res)
case "durationSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
slice := strings.Split(s, ",")
return cast.ToDurationSlice(slice)
case "stringToString": case "stringToString":
return stringToStringConv(flag.ValueString()) return stringToStringConv(flag.ValueString())
case "stringToInt":
return stringToIntConv(flag.ValueString())
default: default:
return flag.ValueString() return flag.ValueString()
} }
@@ -1283,9 +1379,10 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
// Env override next // Env override next
if v.automaticEnvApplied { if v.automaticEnvApplied {
envKey := strings.Join(append(v.parents, lcaseKey), ".")
// even if it hasn't been registered, if automaticEnv is used, // even if it hasn't been registered, if automaticEnv is used,
// check any Get request // check any Get request
if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok { if val, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
return val return val
} }
if nested && v.isPathShadowedInAutoEnv(path) != "" { if nested && v.isPathShadowedInAutoEnv(path) != "" {
@@ -1352,6 +1449,13 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
return cast.ToIntSlice(res) return cast.ToIntSlice(res)
case "stringToString": case "stringToString":
return stringToStringConv(flag.ValueString()) return stringToStringConv(flag.ValueString())
case "stringToInt":
return stringToIntConv(flag.ValueString())
case "durationSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
slice := strings.Split(s, ",")
return cast.ToDurationSlice(slice)
default: default:
return flag.ValueString() return flag.ValueString()
} }
@@ -1372,25 +1476,49 @@ 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 // 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 // alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
func stringToStringConv(val string) interface{} { func stringToStringConv(val string) any {
val = strings.Trim(val, "[]") val = strings.Trim(val, "[]")
// An empty string would cause an empty map // An empty string would cause an empty map
if len(val) == 0 { if val == "" {
return map[string]interface{}{} return map[string]any{}
} }
r := csv.NewReader(strings.NewReader(val)) r := csv.NewReader(strings.NewReader(val))
ss, err := r.Read() ss, err := r.Read()
if err != nil { if err != nil {
return nil return nil
} }
out := make(map[string]interface{}, len(ss)) out := make(map[string]any, len(ss))
for _, pair := range ss { for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2) k, vv, found := strings.Cut(pair, "=")
if len(kv) != 2 { if !found {
return nil
}
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]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 val == "" {
return map[string]any{}
}
ss := strings.Split(val, ",")
out := make(map[string]any, len(ss))
for _, pair := range ss {
k, vv, found := strings.Cut(pair, "=")
if !found {
return nil
}
var err error
out[k], err = strconv.Atoi(vv)
if err != nil {
return nil return nil
} }
out[kv[0]] = kv[1]
} }
return out return out
} }
@@ -1424,13 +1552,13 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
// RegisterAlias creates an alias that provides another accessor for the same key. // RegisterAlias creates an alias that provides another accessor for the same key.
// This enables one to change a name without breaking the application. // This enables one to change a name without breaking the application.
func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) } func RegisterAlias(alias, key string) { v.RegisterAlias(alias, key) }
func (v *Viper) RegisterAlias(alias string, key string) { func (v *Viper) RegisterAlias(alias, key string) {
v.registerAlias(alias, strings.ToLower(key)) v.registerAlias(alias, strings.ToLower(key))
} }
func (v *Viper) registerAlias(alias string, key string) { func (v *Viper) registerAlias(alias, key string) {
alias = strings.ToLower(alias) alias = strings.ToLower(alias)
if alias != key && alias != v.realKey(key) { if alias != key && alias != v.realKey(key) {
_, exists := v.aliases[alias] _, exists := v.aliases[alias]
@@ -1488,9 +1616,9 @@ func (v *Viper) InConfig(key string) bool {
// SetDefault sets the default value for this key. // SetDefault sets the default value for this key.
// SetDefault is case-insensitive for a key. // SetDefault is case-insensitive for a key.
// Default only used when no value is provided by the user via flag, config or ENV. // 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 // If alias passed in, then set the proper default
key = v.realKey(strings.ToLower(key)) key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value) value = toCaseInsensitiveValue(value)
@@ -1507,9 +1635,9 @@ func (v *Viper) SetDefault(key string, value interface{}) {
// Set is case-insensitive for a key. // Set is case-insensitive for a key.
// Will be used instead of values obtained via // Will be used instead of values obtained via
// flags, config file, ENV, default, or key/value store. // 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 // If alias passed in, then set the proper override
key = v.realKey(strings.ToLower(key)) key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value) value = toCaseInsensitiveValue(value)
@@ -1543,7 +1671,7 @@ func (v *Viper) ReadInConfig() error {
return err return err
} }
config := make(map[string]interface{}) config := make(map[string]any)
err = v.unmarshalReader(bytes.NewReader(file), config) err = v.unmarshalReader(bytes.NewReader(file), config)
if err != nil { if err != nil {
@@ -1581,7 +1709,7 @@ func (v *Viper) MergeInConfig() error {
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
func (v *Viper) ReadConfig(in io.Reader) error { 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) return v.unmarshalReader(in, v.config)
} }
@@ -1589,7 +1717,7 @@ func (v *Viper) ReadConfig(in io.Reader) error {
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
func (v *Viper) MergeConfig(in io.Reader) error { 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 { if err := v.unmarshalReader(in, cfg); err != nil {
return err return err
} }
@@ -1598,11 +1726,11 @@ func (v *Viper) MergeConfig(in io.Reader) error {
// MergeConfigMap merges the configuration from the map given with an existing config. // MergeConfigMap merges the configuration from the map given with an existing config.
// Note that the map given may be modified. // 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 { if v.config == nil {
v.config = make(map[string]interface{}) v.config = make(map[string]any)
} }
insensitiviseMap(cfg) insensitiviseMap(cfg)
mergeMaps(cfg, v.config, nil) mergeMaps(cfg, v.config, nil)
@@ -1667,7 +1795,7 @@ func (v *Viper) writeConfig(filename string, force bool) error {
return UnsupportedConfigError(configType) return UnsupportedConfigError(configType)
} }
if v.config == nil { 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 flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
if !force { if !force {
@@ -1688,11 +1816,11 @@ func (v *Viper) writeConfig(filename string, force bool) error {
// Unmarshal a Reader into a map. // Unmarshal a Reader into a map.
// Should probably be an unexported function. // 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) 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 := new(bytes.Buffer)
buf.ReadFrom(in) buf.ReadFrom(in)
@@ -1726,7 +1854,7 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error {
return nil return nil
} }
func keyExists(k string, m map[string]interface{}) string { func keyExists(k string, m map[string]any) string {
lk := strings.ToLower(k) lk := strings.ToLower(k)
for mk := range m { for mk := range m {
lmk := strings.ToLower(mk) lmk := strings.ToLower(mk)
@@ -1738,33 +1866,33 @@ func keyExists(k string, m map[string]interface{}) string {
} }
func castToMapStringInterface( func castToMapStringInterface(
src map[interface{}]interface{}, src map[any]any,
) map[string]interface{} { ) map[string]any {
tgt := map[string]interface{}{} tgt := map[string]any{}
for k, v := range src { for k, v := range src {
tgt[fmt.Sprintf("%v", k)] = v tgt[fmt.Sprintf("%v", k)] = v
} }
return tgt return tgt
} }
func castMapStringSliceToMapInterface(src map[string][]string) map[string]interface{} { func castMapStringSliceToMapInterface(src map[string][]string) map[string]any {
tgt := map[string]interface{}{} tgt := map[string]any{}
for k, v := range src { for k, v := range src {
tgt[k] = v tgt[k] = v
} }
return tgt return tgt
} }
func castMapStringToMapInterface(src map[string]string) map[string]interface{} { func castMapStringToMapInterface(src map[string]string) map[string]any {
tgt := map[string]interface{}{} tgt := map[string]any{}
for k, v := range src { for k, v := range src {
tgt[k] = v tgt[k] = v
} }
return tgt return tgt
} }
func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} { func castMapFlagToMapInterface(src map[string]FlagValue) map[string]any {
tgt := map[string]interface{}{} tgt := map[string]any{}
for k, v := range src { for k, v := range src {
tgt[k] = v tgt[k] = v
} }
@@ -1772,17 +1900,15 @@ func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{}
} }
// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's // 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 // 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 // deep. Both map types are supported as there is a go-yaml fork that uses
// `map[string]interface{}` instead. // `map[string]any` instead.
func mergeMaps( func mergeMaps(src, tgt map[string]any, itgt map[any]any) {
src, tgt map[string]interface{}, itgt map[interface{}]interface{},
) {
for sk, sv := range src { for sk, sv := range src {
tk := keyExists(sk, tgt) tk := keyExists(sk, tgt)
if tk == "" { 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 tgt[sk] = sv
if itgt != nil { if itgt != nil {
itgt[sk] = sv itgt[sk] = sv
@@ -1792,7 +1918,7 @@ func mergeMaps(
tv, ok := tgt[tk] tv, ok := tgt[tk]
if !ok { 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 tgt[sk] = sv
if itgt != nil { if itgt != nil {
itgt[sk] = sv itgt[sk] = sv
@@ -1803,7 +1929,7 @@ func mergeMaps(
svType := reflect.TypeOf(sv) svType := reflect.TypeOf(sv)
tvType := reflect.TypeOf(tv) tvType := reflect.TypeOf(tv)
v.logger.Trace( v.logger.Debug(
"processing", "processing",
"key", sk, "key", sk,
"st", svType, "st", svType,
@@ -1813,12 +1939,12 @@ func mergeMaps(
) )
switch ttv := tv.(type) { switch ttv := tv.(type) {
case map[interface{}]interface{}: case map[any]any:
v.logger.Trace("merging maps (must convert)") v.logger.Debug("merging maps (must convert)")
tsv, ok := sv.(map[interface{}]interface{}) tsv, ok := sv.(map[any]any)
if !ok { if !ok {
v.logger.Error( v.logger.Error(
"Could not cast sv to map[interface{}]interface{}", "Could not cast sv to map[any]any",
"key", sk, "key", sk,
"st", svType, "st", svType,
"tt", tvType, "tt", tvType,
@@ -1831,12 +1957,12 @@ func mergeMaps(
ssv := castToMapStringInterface(tsv) ssv := castToMapStringInterface(tsv)
stv := castToMapStringInterface(ttv) stv := castToMapStringInterface(ttv)
mergeMaps(ssv, stv, ttv) mergeMaps(ssv, stv, ttv)
case map[string]interface{}: case map[string]any:
v.logger.Trace("merging maps") v.logger.Debug("merging maps")
tsv, ok := sv.(map[string]interface{}) tsv, ok := sv.(map[string]any)
if !ok { if !ok {
v.logger.Error( v.logger.Error(
"Could not cast sv to map[string]interface{}", "Could not cast sv to map[string]any",
"key", sk, "key", sk,
"st", svType, "st", svType,
"tt", tvType, "tt", tvType,
@@ -1847,7 +1973,7 @@ func mergeMaps(
} }
mergeMaps(tsv, ttv, nil) mergeMaps(tsv, ttv, nil)
default: default:
v.logger.Trace("setting value") v.logger.Debug("setting value")
tgt[tk] = sv tgt[tk] = sv
if itgt != nil { if itgt != nil {
itgt[tk] = sv itgt[tk] = sv
@@ -1898,7 +2024,7 @@ func (v *Viper) getKeyValueConfig() error {
return RemoteConfigError("No Files Found") 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) reader, err := RemoteConfig.Get(provider)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1947,7 +2073,7 @@ func (v *Viper) watchKeyValueConfig() error {
return RemoteConfigError("No Files Found") 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) reader, err := RemoteConfig.Watch(provider)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1957,7 +2083,7 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface
} }
// AllKeys returns all keys holding a value, regardless of where they are set. // AllKeys returns all keys holding a value, regardless of where they are set.
// Nested keys are returned with a v.keyDelim separator // Nested keys are returned with a v.keyDelim separator.
func AllKeys() []string { return v.AllKeys() } func AllKeys() []string { return v.AllKeys() }
func (v *Viper) AllKeys() []string { func (v *Viper) AllKeys() []string {
@@ -1986,7 +2112,7 @@ func (v *Viper) AllKeys() []string {
// it is skipped. // it is skipped.
// //
// The resulting set of paths is merged to the given shadow set at the same time. // 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] { if shadow != nil && prefix != "" && shadow[prefix] {
// prefix is shadowed => nothing more to flatten // prefix is shadowed => nothing more to flatten
return shadow return shadow
@@ -1995,16 +2121,16 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
shadow = make(map[string]bool) shadow = make(map[string]bool)
} }
var m2 map[string]interface{} var m2 map[string]any
if prefix != "" { if prefix != "" {
prefix += v.keyDelim prefix += v.keyDelim
} }
for k, val := range m { for k, val := range m {
fullKey := prefix + k fullKey := prefix + k
switch val.(type) { switch val := val.(type) {
case map[string]interface{}: case map[string]any:
m2 = val.(map[string]interface{}) m2 = val
case map[interface{}]interface{}: case map[any]any:
m2 = cast.ToStringMap(val) m2 = cast.ToStringMap(val)
default: default:
// immediate value // immediate value
@@ -2019,7 +2145,7 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
// mergeFlatMap merges the given maps, excluding values of the second map // mergeFlatMap merges the given maps, excluding values of the second map
// shadowed by values from the first 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 // scan keys
outer: outer:
for k := range m { for k := range m {
@@ -2039,13 +2165,17 @@ outer:
return shadow return shadow
} }
// AllSettings merges all settings and returns them as a map[string]interface{}. // AllSettings merges all settings and returns them as a map[string]any.
func AllSettings() map[string]interface{} { return v.AllSettings() } func AllSettings() map[string]any { return v.AllSettings() }
func (v *Viper) AllSettings() map[string]interface{} { func (v *Viper) AllSettings() map[string]any {
m := map[string]interface{}{} return v.getSettings(v.AllKeys())
}
func (v *Viper) getSettings(keys []string) map[string]any {
m := map[string]any{}
// start from the list of keys, and construct the map one value at a time // start from the list of keys, and construct the map one value at a time
for _, k := range v.AllKeys() { for _, k := range keys {
value := v.Get(k) value := v.Get(k)
if value == nil { if value == nil {
// should not happen, since AllKeys() returns only keys holding a value, // should not happen, since AllKeys() returns only keys holding a value,
-32
View File
@@ -1,32 +0,0 @@
//go:build go1.16 && finder
// +build go1.16,finder
package viper
import (
"fmt"
"github.com/spf13/afero"
)
// 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 != "",
}
file, err := finder.Find(afero.NewIOFS(v.fs))
if err != nil {
return "", err
}
if file == "" {
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
}
return file, nil
}
+505 -421
View File
File diff suppressed because it is too large Load Diff
-56
View File
@@ -1,56 +0,0 @@
//go:build viper_yaml2
// +build viper_yaml2
package viper
var yamlExample = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
pants:
size: large
age: 35
eyes : brown
beard: true
`)
var yamlWriteExpected = []byte(`age: 35
beard: true
clothing:
jacket: leather
pants:
size: large
trousers: denim
eyes: brown
hacker: true
hobbies:
- skateboarding
- snowboarding
- go
name: steve
`)
var yamlExampleWithDot = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
pants:
size: large
age: 35
eyes : brown
beard: true
emails:
steve@hacker.com:
created: 01/02/03
active: true
`)
@@ -1,6 +1,3 @@
//go:build !viper_yaml2
// +build !viper_yaml2
package viper package viper
var yamlExample = []byte(`Hacker: true var yamlExample = []byte(`Hacker: true
-12
View File
@@ -1,12 +0,0 @@
//go:build !js
// +build !js
package viper
import "github.com/fsnotify/fsnotify"
type watcher = fsnotify.Watcher
func newWatcher() (*watcher, error) {
return fsnotify.NewWatcher()
}
-30
View File
@@ -1,30 +0,0 @@
// +build js,wasm
package viper
import (
"errors"
"github.com/fsnotify/fsnotify"
)
type watcher struct {
Events chan fsnotify.Event
Errors chan error
}
func (*watcher) Close() error {
return nil
}
func (*watcher) Add(name string) error {
return nil
}
func (*watcher) Remove(name string) error {
return nil
}
func newWatcher() (*watcher, error) {
return &watcher{}, errors.New("fsnotify is not supported on WASM")
}