mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-22 04:55:33 +03:00
Merge branch 'master' into AG-27492-client-storage-runtime-sources
This commit is contained in:
commit
80f7e98d30
23 changed files with 483 additions and 149 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
'name': 'build'
|
'name': 'build'
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.22.6'
|
'GO_VERSION': '1.23.1'
|
||||||
'NODE_VERSION': '16'
|
'NODE_VERSION': '16'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
|
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
'name': 'lint'
|
'name': 'lint'
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.22.6'
|
'GO_VERSION': '1.23.1'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
|
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -27,6 +27,11 @@ See also the [v0.107.53 GitHub milestone][ms-v0.107.53].
|
||||||
NOTE: Add new changes BELOW THIS COMMENT.
|
NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Go version has been updated to prevent the possibility of exploiting the Go
|
||||||
|
vulnerabilities fixed in [1.23.1][go-1.23.1].
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for 64-bit RISC-V architecture ([#5704]).
|
- Support for 64-bit RISC-V architecture ([#5704]).
|
||||||
|
@ -36,10 +41,11 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
|
|
||||||
- Upstream server URL domain names requirements has been relaxed and now follow
|
- Upstream server URL domain names requirements has been relaxed and now follow
|
||||||
the same rules as their domain specifications.
|
the same rules as their domain specifications.
|
||||||
- Go version has been updated to [1.22.6][go-1.22.6].
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Property `clients.runtime_sources.dhcp` in the configuration file not taking
|
||||||
|
effect.
|
||||||
- Update Google safe search domains list ([#7155]).
|
- Update Google safe search domains list ([#7155]).
|
||||||
- Enforce Bing safe search from Edge sidebar ([#7154]).
|
- Enforce Bing safe search from Edge sidebar ([#7154]).
|
||||||
- Text overflow on the query log page ([#7119]).
|
- Text overflow on the query log page ([#7119]).
|
||||||
|
@ -49,7 +55,7 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
[#7154]: https://github.com/AdguardTeam/AdGuardHome/pull/7154
|
[#7154]: https://github.com/AdguardTeam/AdGuardHome/pull/7154
|
||||||
[#7155]: https://github.com/AdguardTeam/AdGuardHome/pull/7155
|
[#7155]: https://github.com/AdguardTeam/AdGuardHome/pull/7155
|
||||||
|
|
||||||
[go-1.22.6]: https://groups.google.com/g/golang-announce/c/X4q_-Wf-5g4
|
[go-1.23.1]: https://groups.google.com/g/golang-announce/c/K-cEzDeCtpc
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -23,11 +23,12 @@ VERBOSE.MACRO = $${VERBOSE:-0}
|
||||||
CHANNEL = development
|
CHANNEL = development
|
||||||
CLIENT_DIR = client
|
CLIENT_DIR = client
|
||||||
COMMIT = $$( git rev-parse --short HEAD )
|
COMMIT = $$( git rev-parse --short HEAD )
|
||||||
|
DEPLOY_SCRIPT_PATH = not/a/real/path
|
||||||
DIST_DIR = dist
|
DIST_DIR = dist
|
||||||
GOAMD64 = v1
|
GOAMD64 = v1
|
||||||
GOPROXY = https://proxy.golang.org|direct
|
GOPROXY = https://proxy.golang.org|direct
|
||||||
GOSUMDB = sum.golang.google.cn
|
GOSUMDB = sum.golang.google.cn
|
||||||
GOTOOLCHAIN = go1.22.6
|
GOTOOLCHAIN = go1.23.1
|
||||||
GOTELEMETRY = off
|
GOTELEMETRY = off
|
||||||
GPG_KEY = devteam@adguard.com
|
GPG_KEY = devteam@adguard.com
|
||||||
GPG_KEY_PASSPHRASE = not-a-real-password
|
GPG_KEY_PASSPHRASE = not-a-real-password
|
||||||
|
@ -37,6 +38,7 @@ NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress --ignore-engines\
|
||||||
--ignore-optional --ignore-platform --ignore-scripts
|
--ignore-optional --ignore-platform --ignore-scripts
|
||||||
RACE = 0
|
RACE = 0
|
||||||
SIGN = 1
|
SIGN = 1
|
||||||
|
SIGNER_API_KEY = not-a-real-key
|
||||||
VERSION = v0.0.0
|
VERSION = v0.0.0
|
||||||
YARN = yarn
|
YARN = yarn
|
||||||
|
|
||||||
|
@ -60,6 +62,7 @@ BUILD_RELEASE_DEPS_1 = go-deps
|
||||||
ENV = env\
|
ENV = env\
|
||||||
CHANNEL='$(CHANNEL)'\
|
CHANNEL='$(CHANNEL)'\
|
||||||
COMMIT='$(COMMIT)'\
|
COMMIT='$(COMMIT)'\
|
||||||
|
DEPLOY_SCRIPT_PATH='$(DEPLOY_SCRIPT_PATH)' \
|
||||||
DIST_DIR='$(DIST_DIR)'\
|
DIST_DIR='$(DIST_DIR)'\
|
||||||
GO="$(GO.MACRO)"\
|
GO="$(GO.MACRO)"\
|
||||||
GOAMD64='$(GOAMD64)'\
|
GOAMD64='$(GOAMD64)'\
|
||||||
|
@ -72,6 +75,7 @@ ENV = env\
|
||||||
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
|
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
|
||||||
RACE='$(RACE)'\
|
RACE='$(RACE)'\
|
||||||
SIGN='$(SIGN)'\
|
SIGN='$(SIGN)'\
|
||||||
|
SIGNER_API_KEY='$(SIGNER_API_KEY)' \
|
||||||
NEXTAPI='$(NEXTAPI)'\
|
NEXTAPI='$(NEXTAPI)'\
|
||||||
VERBOSE="$(VERBOSE.MACRO)"\
|
VERBOSE="$(VERBOSE.MACRO)"\
|
||||||
VERSION="$(VERSION)"\
|
VERSION="$(VERSION)"\
|
||||||
|
|
|
@ -205,7 +205,7 @@ Run `make init` to prepare the development environment.
|
||||||
|
|
||||||
You will need this to build AdGuard Home:
|
You will need this to build AdGuard Home:
|
||||||
|
|
||||||
- [Go](https://golang.org/dl/) v1.22 or later;
|
- [Go](https://golang.org/dl/) v1.23 or later;
|
||||||
- [Node.js](https://nodejs.org/en/download/) v18.18 or later;
|
- [Node.js](https://nodejs.org/en/download/) v18.18 or later;
|
||||||
- [npm](https://www.npmjs.com/) v8 or later;
|
- [npm](https://www.npmjs.com/) v8 or later;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'edge'
|
'channel': 'edge'
|
||||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.22.6--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
|
|
||||||
'stages':
|
'stages':
|
||||||
- 'Build frontend':
|
- 'Build frontend':
|
||||||
|
@ -91,6 +91,11 @@
|
||||||
'tasks':
|
'tasks':
|
||||||
- 'checkout':
|
- 'checkout':
|
||||||
'force-clean-build': true
|
'force-clean-build': true
|
||||||
|
- 'checkout':
|
||||||
|
'repository': 'bamboo-deploy-publisher'
|
||||||
|
# The paths are always relative to the working directory.
|
||||||
|
'path': 'bamboo-deploy-publisher'
|
||||||
|
'force-clean-build': true
|
||||||
- 'script':
|
- 'script':
|
||||||
'interpreter': 'SHELL'
|
'interpreter': 'SHELL'
|
||||||
'scripts':
|
'scripts':
|
||||||
|
@ -99,6 +104,9 @@
|
||||||
|
|
||||||
set -e -f -u -x
|
set -e -f -u -x
|
||||||
|
|
||||||
|
# Explicitly checkout the revision that we need.
|
||||||
|
git checkout "${bamboo.repository.revision.number}"
|
||||||
|
|
||||||
# Run the build with the specified channel.
|
# Run the build with the specified channel.
|
||||||
echo "${bamboo.gpgSecretKeyPart1}${bamboo.gpgSecretKeyPart2}"\
|
echo "${bamboo.gpgSecretKeyPart1}${bamboo.gpgSecretKeyPart2}"\
|
||||||
| awk '{ gsub(/\\n/, "\n"); print; }'\
|
| awk '{ gsub(/\\n/, "\n"); print; }'\
|
||||||
|
@ -107,6 +115,8 @@
|
||||||
make\
|
make\
|
||||||
CHANNEL=${bamboo.channel}\
|
CHANNEL=${bamboo.channel}\
|
||||||
GPG_KEY_PASSPHRASE=${bamboo.gpgPassword}\
|
GPG_KEY_PASSPHRASE=${bamboo.gpgPassword}\
|
||||||
|
DEPLOY_SCRIPT_PATH="./bamboo-deploy-publisher/deploy.sh"\
|
||||||
|
SIGNER_API_KEY="${bamboo.adguardHomeWinSignerSecretApiKey}"\
|
||||||
FRONTEND_PREBUILT=1\
|
FRONTEND_PREBUILT=1\
|
||||||
PARALLELISM=1\
|
PARALLELISM=1\
|
||||||
VERBOSE=2\
|
VERBOSE=2\
|
||||||
|
@ -266,7 +276,7 @@
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'beta'
|
'channel': 'beta'
|
||||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.22.6--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
# release-vX.Y.Z branches are the branches from which the actual final
|
# release-vX.Y.Z branches are the branches from which the actual final
|
||||||
# release is built.
|
# release is built.
|
||||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||||
|
@ -282,4 +292,4 @@
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'release'
|
'channel': 'release'
|
||||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.22.6--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
'name': 'AdGuard Home - Build and run tests'
|
'name': 'AdGuard Home - Build and run tests'
|
||||||
'variables':
|
'variables':
|
||||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.22.6--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
'channel': 'development'
|
'channel': 'development'
|
||||||
|
|
||||||
'stages':
|
'stages':
|
||||||
|
@ -196,5 +196,5 @@
|
||||||
# may need to build a few of these.
|
# may need to build a few of these.
|
||||||
'variables':
|
'variables':
|
||||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.22.6--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
'channel': 'candidate'
|
'channel': 'candidate'
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module github.com/AdguardTeam/AdGuardHome
|
module github.com/AdguardTeam/AdGuardHome
|
||||||
|
|
||||||
go 1.22.6
|
go 1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdguardTeam/dnsproxy v0.73.0
|
github.com/AdguardTeam/dnsproxy v0.73.0
|
||||||
|
|
|
@ -2,13 +2,16 @@
|
||||||
package aghhttp
|
package aghhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
||||||
"github.com/AdguardTeam/golibs/httphdr"
|
"github.com/AdguardTeam/golibs/httphdr"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP scheme constants.
|
// HTTP scheme constants.
|
||||||
|
@ -31,12 +34,39 @@ func OK(w http.ResponseWriter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error writes formatted message to w and also logs it.
|
// Error writes formatted message to w and also logs it.
|
||||||
|
//
|
||||||
|
// TODO(s.chzhen): Remove it.
|
||||||
func Error(r *http.Request, w http.ResponseWriter, code int, format string, args ...any) {
|
func Error(r *http.Request, w http.ResponseWriter, code int, format string, args ...any) {
|
||||||
text := fmt.Sprintf(format, args...)
|
text := fmt.Sprintf(format, args...)
|
||||||
log.Error("%s %s %s: %s", r.Method, r.Host, r.URL, text)
|
log.Error("%s %s %s: %s", r.Method, r.Host, r.URL, text)
|
||||||
http.Error(w, text, code)
|
http.Error(w, text, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorAndLog writes formatted message to w and also logs it with the specified
|
||||||
|
// logging level.
|
||||||
|
func ErrorAndLog(
|
||||||
|
ctx context.Context,
|
||||||
|
l *slog.Logger,
|
||||||
|
r *http.Request,
|
||||||
|
w http.ResponseWriter,
|
||||||
|
code int,
|
||||||
|
format string,
|
||||||
|
args ...any,
|
||||||
|
) {
|
||||||
|
text := fmt.Sprintf(format, args...)
|
||||||
|
l.ErrorContext(
|
||||||
|
ctx,
|
||||||
|
"http error",
|
||||||
|
"host", r.Host,
|
||||||
|
"method", r.Method,
|
||||||
|
"raddr", r.RemoteAddr,
|
||||||
|
"request_uri", r.RequestURI,
|
||||||
|
slogutil.KeyError, text,
|
||||||
|
)
|
||||||
|
|
||||||
|
http.Error(w, text, code)
|
||||||
|
}
|
||||||
|
|
||||||
// UserAgent returns the ID of the service as a User-Agent string. It can also
|
// UserAgent returns the ID of the service as a User-Agent string. It can also
|
||||||
// be used as the value of the Server HTTP header.
|
// be used as the value of the Server HTTP header.
|
||||||
func UserAgent() (ua string) {
|
func UserAgent() (ua string) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
"github.com/ameshkov/dnscrypt/v2"
|
"github.com/ameshkov/dnscrypt/v2"
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
|
@ -55,6 +56,7 @@ func initDNS(l *slog.Logger) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
statsConf := stats.Config{
|
statsConf := stats.Config{
|
||||||
|
Logger: l.With(slogutil.KeyPrefix, "stats"),
|
||||||
Filename: filepath.Join(statsDir, "stats.db"),
|
Filename: filepath.Join(statsDir, "stats.db"),
|
||||||
Limit: config.Stats.Interval.Duration,
|
Limit: config.Stats.Interval.Duration,
|
||||||
ConfigModified: onConfigModified,
|
ConfigModified: onConfigModified,
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"github.com/AdguardTeam/golibs/timeutil"
|
"github.com/AdguardTeam/golibs/timeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,6 +50,8 @@ type StatsResp struct {
|
||||||
func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
|
func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
resp *StatsResp
|
resp *StatsResp
|
||||||
ok bool
|
ok bool
|
||||||
|
@ -62,12 +63,17 @@ func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||||
resp, ok = s.getData(uint32(s.limit.Hours()))
|
resp, ok = s.getData(uint32(s.limit.Hours()))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Debug("stats: prepared data in %v", time.Since(start))
|
s.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"prepared data",
|
||||||
|
"elapsed", timeutil.Duration{Duration: time.Since(start)},
|
||||||
|
)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
// Don't bring the message to the lower case since it's a part of UI
|
// Don't bring the message to the lower case since it's a part of UI
|
||||||
// text for the moment.
|
// text for the moment.
|
||||||
aghhttp.Error(r, w, http.StatusInternalServerError, "Couldn't get statistics data")
|
const msg = "Couldn't get statistics data"
|
||||||
|
aghhttp.ErrorAndLog(ctx, s.logger, r, w, http.StatusInternalServerError, msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -146,16 +152,18 @@ func (s *StatsCtx) handleGetStatsConfig(w http.ResponseWriter, r *http.Request)
|
||||||
//
|
//
|
||||||
// Deprecated: Remove it when migration to the new API is over.
|
// Deprecated: Remove it when migration to the new API is over.
|
||||||
func (s *StatsCtx) handleStatsConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *StatsCtx) handleStatsConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
reqData := configResp{}
|
reqData := configResp{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&reqData)
|
err := json.NewDecoder(r.Body).Decode(&reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "json decode: %s", err)
|
aghhttp.ErrorAndLog(ctx, s.logger, r, w, http.StatusBadRequest, "json decode: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !checkInterval(reqData.IntervalDays) {
|
if !checkInterval(reqData.IntervalDays) {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "Unsupported interval")
|
aghhttp.ErrorAndLog(ctx, s.logger, r, w, http.StatusBadRequest, "Unsupported interval")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -173,17 +181,19 @@ func (s *StatsCtx) handleStatsConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
// handlePutStatsConfig is the handler for the PUT /control/stats/config/update
|
// handlePutStatsConfig is the handler for the PUT /control/stats/config/update
|
||||||
// HTTP API.
|
// HTTP API.
|
||||||
func (s *StatsCtx) handlePutStatsConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *StatsCtx) handlePutStatsConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
reqData := getConfigResp{}
|
reqData := getConfigResp{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&reqData)
|
err := json.NewDecoder(r.Body).Decode(&reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "json decode: %s", err)
|
aghhttp.ErrorAndLog(ctx, s.logger, r, w, http.StatusBadRequest, "json decode: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
engine, err := aghnet.NewIgnoreEngine(reqData.Ignored)
|
engine, err := aghnet.NewIgnoreEngine(reqData.Ignored)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusUnprocessableEntity, "ignored: %s", err)
|
aghhttp.ErrorAndLog(ctx, s.logger, r, w, http.StatusUnprocessableEntity, "ignored: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -191,13 +201,21 @@ func (s *StatsCtx) handlePutStatsConfig(w http.ResponseWriter, r *http.Request)
|
||||||
ivl := time.Duration(reqData.Interval) * time.Millisecond
|
ivl := time.Duration(reqData.Interval) * time.Millisecond
|
||||||
err = validateIvl(ivl)
|
err = validateIvl(ivl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusUnprocessableEntity, "unsupported interval: %s", err)
|
aghhttp.ErrorAndLog(
|
||||||
|
ctx,
|
||||||
|
s.logger,
|
||||||
|
r,
|
||||||
|
w,
|
||||||
|
http.StatusUnprocessableEntity,
|
||||||
|
"unsupported interval: %s",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if reqData.Enabled == aghalg.NBNull {
|
if reqData.Enabled == aghalg.NBNull {
|
||||||
aghhttp.Error(r, w, http.StatusUnprocessableEntity, "enabled is null")
|
aghhttp.ErrorAndLog(ctx, s.logger, r, w, http.StatusUnprocessableEntity, "enabled is null")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -216,7 +234,15 @@ func (s *StatsCtx) handlePutStatsConfig(w http.ResponseWriter, r *http.Request)
|
||||||
func (s *StatsCtx) handleStatsReset(w http.ResponseWriter, r *http.Request) {
|
func (s *StatsCtx) handleStatsReset(w http.ResponseWriter, r *http.Request) {
|
||||||
err := s.clear()
|
err := s.clear()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusInternalServerError, "stats: %s", err)
|
aghhttp.ErrorAndLog(
|
||||||
|
r.Context(),
|
||||||
|
s.logger,
|
||||||
|
r,
|
||||||
|
w,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"stats: %s",
|
||||||
|
err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/testutil"
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/AdguardTeam/golibs/timeutil"
|
"github.com/AdguardTeam/golibs/timeutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -24,6 +25,7 @@ func TestHandleStatsConfig(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
conf := Config{
|
conf := Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
UnitID: func() (id uint32) { return 0 },
|
UnitID: func() (id uint32) { return 0 },
|
||||||
ConfigModified: func() {},
|
ConfigModified: func() {},
|
||||||
ShouldCountClient: func([]string) bool { return true },
|
ShouldCountClient: func([]string) bool { return true },
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -14,7 +16,7 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/timeutil"
|
"github.com/AdguardTeam/golibs/timeutil"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
@ -43,6 +45,10 @@ func validateIvl(ivl time.Duration) (err error) {
|
||||||
//
|
//
|
||||||
// Do not alter any fields of this structure after using it.
|
// Do not alter any fields of this structure after using it.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
// Logger is used for logging the operation of the statistics management.
|
||||||
|
// It must not be nil.
|
||||||
|
Logger *slog.Logger
|
||||||
|
|
||||||
// UnitID is the function to generate the identifier for current unit. If
|
// UnitID is the function to generate the identifier for current unit. If
|
||||||
// nil, the default function is used, see newUnitID.
|
// nil, the default function is used, see newUnitID.
|
||||||
UnitID UnitIDGenFunc
|
UnitID UnitIDGenFunc
|
||||||
|
@ -96,6 +102,10 @@ type Interface interface {
|
||||||
// StatsCtx collects the statistics and flushes it to the database. Its default
|
// StatsCtx collects the statistics and flushes it to the database. Its default
|
||||||
// flushing interval is one hour.
|
// flushing interval is one hour.
|
||||||
type StatsCtx struct {
|
type StatsCtx struct {
|
||||||
|
// logger is used for logging the operation of the statistics management.
|
||||||
|
// It must not be nil.
|
||||||
|
logger *slog.Logger
|
||||||
|
|
||||||
// currMu protects curr.
|
// currMu protects curr.
|
||||||
currMu *sync.RWMutex
|
currMu *sync.RWMutex
|
||||||
// curr is the actual statistics collection result.
|
// curr is the actual statistics collection result.
|
||||||
|
@ -150,6 +160,7 @@ func New(conf Config) (s *StatsCtx, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s = &StatsCtx{
|
s = &StatsCtx{
|
||||||
|
logger: conf.Logger,
|
||||||
currMu: &sync.RWMutex{},
|
currMu: &sync.RWMutex{},
|
||||||
httpRegister: conf.HTTPRegister,
|
httpRegister: conf.HTTPRegister,
|
||||||
configModified: conf.ConfigModified,
|
configModified: conf.ConfigModified,
|
||||||
|
@ -178,21 +189,21 @@ func New(conf Config) (s *StatsCtx, err error) {
|
||||||
|
|
||||||
tx, err := s.db.Load().Begin(true)
|
tx, err := s.db.Load().Begin(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("stats: opening a transaction: %w", err)
|
return nil, fmt.Errorf("opening a transaction: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleted := deleteOldUnits(tx, id-uint32(s.limit.Hours())-1)
|
deleted := s.deleteOldUnits(tx, id-uint32(s.limit.Hours())-1)
|
||||||
udb = loadUnitFromDB(tx, id)
|
udb = s.loadUnitFromDB(tx, id)
|
||||||
|
|
||||||
err = finishTxn(tx, deleted > 0)
|
err = finishTxn(tx, deleted > 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: %s", err)
|
s.logger.Error("finishing transacation", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.curr = newUnit(id)
|
s.curr = newUnit(id)
|
||||||
s.curr.deserialize(udb)
|
s.curr.deserialize(udb)
|
||||||
|
|
||||||
log.Debug("stats: initialized")
|
s.logger.Debug("initialized")
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
@ -228,8 +239,6 @@ func (s *StatsCtx) Start() {
|
||||||
|
|
||||||
// Close implements the [io.Closer] interface for *StatsCtx.
|
// Close implements the [io.Closer] interface for *StatsCtx.
|
||||||
func (s *StatsCtx) Close() (err error) {
|
func (s *StatsCtx) Close() (err error) {
|
||||||
defer func() { err = errors.Annotate(err, "stats: closing: %w") }()
|
|
||||||
|
|
||||||
db := s.db.Swap(nil)
|
db := s.db.Swap(nil)
|
||||||
if db == nil {
|
if db == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -237,7 +246,7 @@ func (s *StatsCtx) Close() (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
cerr := db.Close()
|
cerr := db.Close()
|
||||||
if cerr == nil {
|
if cerr == nil {
|
||||||
log.Debug("stats: database closed")
|
s.logger.Debug("database closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = errors.WithDeferred(err, cerr)
|
err = errors.WithDeferred(err, cerr)
|
||||||
|
@ -254,7 +263,7 @@ func (s *StatsCtx) Close() (err error) {
|
||||||
|
|
||||||
udb := s.curr.serialize()
|
udb := s.curr.serialize()
|
||||||
|
|
||||||
return udb.flushUnitToDB(tx, s.curr.id)
|
return s.flushUnitToDB(udb, tx, s.curr.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update implements the [Interface] interface for *StatsCtx. e must not be
|
// Update implements the [Interface] interface for *StatsCtx. e must not be
|
||||||
|
@ -269,7 +278,7 @@ func (s *StatsCtx) Update(e *Entry) {
|
||||||
|
|
||||||
err := e.validate()
|
err := e.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("stats: updating: validating entry: %s", err)
|
s.logger.Debug("validating entry", slogutil.KeyError, err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -278,7 +287,7 @@ func (s *StatsCtx) Update(e *Entry) {
|
||||||
defer s.currMu.Unlock()
|
defer s.currMu.Unlock()
|
||||||
|
|
||||||
if s.curr == nil {
|
if s.curr == nil {
|
||||||
log.Error("stats: current unit is nil")
|
s.logger.Error("current unit is nil")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -333,8 +342,8 @@ func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) {
|
||||||
|
|
||||||
// deleteOldUnits walks the buckets available to tx and deletes old units. It
|
// deleteOldUnits walks the buckets available to tx and deletes old units. It
|
||||||
// returns the number of deletions performed.
|
// returns the number of deletions performed.
|
||||||
func deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) {
|
func (s *StatsCtx) deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) {
|
||||||
log.Debug("stats: deleting old units until id %d", firstID)
|
s.logger.Debug("deleting old units up to", "unit", firstID)
|
||||||
|
|
||||||
// TODO(a.garipov): See if this is actually necessary. Looks like a rather
|
// TODO(a.garipov): See if this is actually necessary. Looks like a rather
|
||||||
// bizarre solution.
|
// bizarre solution.
|
||||||
|
@ -348,12 +357,12 @@ func deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) {
|
||||||
|
|
||||||
err = tx.DeleteBucket(name)
|
err = tx.DeleteBucket(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("stats: deleting bucket: %s", err)
|
s.logger.Debug("deleting bucket", slogutil.KeyError, err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("stats: deleted unit %d (name %x)", nameID, name)
|
s.logger.Debug("deleted unit", "name_id", nameID, "name", fmt.Sprintf("%x", name))
|
||||||
|
|
||||||
deleted++
|
deleted++
|
||||||
|
|
||||||
|
@ -362,7 +371,7 @@ func deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) {
|
||||||
|
|
||||||
err := tx.ForEach(walk)
|
err := tx.ForEach(walk)
|
||||||
if err != nil && !errors.Is(err, errStop) {
|
if err != nil && !errors.Is(err, errStop) {
|
||||||
log.Debug("stats: deleting units: %s", err)
|
s.logger.Debug("deleting units", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deleted
|
return deleted
|
||||||
|
@ -371,20 +380,29 @@ func deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) {
|
||||||
// openDB returns an error if the database can't be opened from the specified
|
// openDB returns an error if the database can't be opened from the specified
|
||||||
// file. It's safe for concurrent use.
|
// file. It's safe for concurrent use.
|
||||||
func (s *StatsCtx) openDB() (err error) {
|
func (s *StatsCtx) openDB() (err error) {
|
||||||
log.Debug("stats: opening database")
|
s.logger.Debug("opening database")
|
||||||
|
|
||||||
var db *bbolt.DB
|
var db *bbolt.DB
|
||||||
db, err = bbolt.Open(s.filename, 0o644, nil)
|
db, err = bbolt.Open(s.filename, 0o644, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "invalid argument" {
|
if err.Error() == "invalid argument" {
|
||||||
log.Error("AdGuard Home cannot be initialized due to an incompatible file system.\nPlease read the explanation here: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#limitations")
|
const lines = `AdGuard Home cannot be initialized due to an incompatible file system.
|
||||||
|
Please read the explanation here: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#limitations`
|
||||||
|
|
||||||
|
// TODO(s.chzhen): Use passed context.
|
||||||
|
slogutil.PrintLines(
|
||||||
|
context.TODO(),
|
||||||
|
s.logger,
|
||||||
|
slog.LevelError,
|
||||||
|
"opening database",
|
||||||
|
lines,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use defer to unlock the mutex as soon as possible.
|
defer s.logger.Debug("database opened")
|
||||||
defer log.Debug("stats: database opened")
|
|
||||||
|
|
||||||
s.db.Store(db)
|
s.db.Store(db)
|
||||||
|
|
||||||
|
@ -424,34 +442,37 @@ func (s *StatsCtx) flushDB(id, limit uint32, ptr *unit) (cont bool, sleepFor tim
|
||||||
isCommitable := true
|
isCommitable := true
|
||||||
tx, err := db.Begin(true)
|
tx, err := db.Begin(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: opening transaction: %s", err)
|
s.logger.Error("opening transaction", slogutil.KeyError, err)
|
||||||
|
|
||||||
return true, 0
|
return true, 0
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err = finishTxn(tx, isCommitable); err != nil {
|
if err = finishTxn(tx, isCommitable); err != nil {
|
||||||
log.Error("stats: %s", err)
|
s.logger.Error("finishing transaction", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
s.curr = newUnit(id)
|
s.curr = newUnit(id)
|
||||||
|
|
||||||
flushErr := ptr.serialize().flushUnitToDB(tx, ptr.id)
|
udb := ptr.serialize()
|
||||||
|
flushErr := s.flushUnitToDB(udb, tx, ptr.id)
|
||||||
if flushErr != nil {
|
if flushErr != nil {
|
||||||
log.Error("stats: flushing unit: %s", flushErr)
|
s.logger.Error("flushing unit", slogutil.KeyError, flushErr)
|
||||||
isCommitable = false
|
isCommitable = false
|
||||||
}
|
}
|
||||||
|
|
||||||
delErr := tx.DeleteBucket(idToUnitName(id - limit))
|
delErr := tx.DeleteBucket(idToUnitName(id - limit))
|
||||||
|
|
||||||
if delErr != nil {
|
if delErr != nil {
|
||||||
// TODO(e.burkov): Improve the algorithm of deleting the oldest bucket
|
// TODO(e.burkov): Improve the algorithm of deleting the oldest bucket
|
||||||
// to avoid the error.
|
// to avoid the error.
|
||||||
if errors.Is(delErr, bbolt.ErrBucketNotFound) {
|
lvl := slog.LevelWarn
|
||||||
log.Debug("stats: warning: deleting unit: %s", delErr)
|
if !errors.Is(delErr, bbolt.ErrBucketNotFound) {
|
||||||
} else {
|
|
||||||
isCommitable = false
|
isCommitable = false
|
||||||
log.Error("stats: deleting unit: %s", delErr)
|
lvl = slog.LevelError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.logger.Log(context.TODO(), lvl, "deleting bucket", slogutil.KeyError, delErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, 0
|
return true, 0
|
||||||
|
@ -467,7 +488,7 @@ func (s *StatsCtx) periodicFlush() {
|
||||||
cont, sleepFor = s.flush()
|
cont, sleepFor = s.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("periodic flushing finished")
|
s.logger.Debug("periodic flushing finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setLimit sets the limit. s.lock is expected to be locked.
|
// setLimit sets the limit. s.lock is expected to be locked.
|
||||||
|
@ -477,16 +498,16 @@ func (s *StatsCtx) setLimit(limit time.Duration) {
|
||||||
if limit != 0 {
|
if limit != 0 {
|
||||||
s.enabled = true
|
s.enabled = true
|
||||||
s.limit = limit
|
s.limit = limit
|
||||||
log.Debug("stats: set limit: %d days", limit/timeutil.Day)
|
s.logger.Debug("setting limit in days", "num", limit/timeutil.Day)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.enabled = false
|
s.enabled = false
|
||||||
log.Debug("stats: disabled")
|
s.logger.Debug("disabled")
|
||||||
|
|
||||||
if err := s.clear(); err != nil {
|
if err := s.clear(); err != nil {
|
||||||
log.Error("stats: %s", err)
|
s.logger.Error("clearing", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,7 +520,7 @@ func (s *StatsCtx) clear() (err error) {
|
||||||
var tx *bbolt.Tx
|
var tx *bbolt.Tx
|
||||||
tx, err = db.Begin(true)
|
tx, err = db.Begin(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: opening a transaction: %s", err)
|
s.logger.Error("opening transaction", slogutil.KeyError, err)
|
||||||
} else if err = finishTxn(tx, false); err != nil {
|
} else if err = finishTxn(tx, false); err != nil {
|
||||||
// Don't wrap the error since it's informative enough as is.
|
// Don't wrap the error since it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
|
@ -513,21 +534,21 @@ func (s *StatsCtx) clear() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// All active transactions are now closed.
|
// All active transactions are now closed.
|
||||||
log.Debug("stats: database closed")
|
s.logger.Debug("database closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Remove(s.filename)
|
err = os.Remove(s.filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: %s", err)
|
s.logger.Error("removing", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.openDB()
|
err = s.openDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: opening database: %s", err)
|
s.logger.Error("opening database", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use defer to unlock the mutex as soon as possible.
|
// Use defer to unlock the mutex as soon as possible.
|
||||||
defer log.Debug("stats: cleared")
|
defer s.logger.Debug("cleared")
|
||||||
|
|
||||||
s.currMu.Lock()
|
s.currMu.Lock()
|
||||||
defer s.currMu.Unlock()
|
defer s.currMu.Unlock()
|
||||||
|
@ -548,7 +569,7 @@ func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, curID uint32) {
|
||||||
// taken into account.
|
// taken into account.
|
||||||
tx, err := db.Begin(true)
|
tx, err := db.Begin(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: opening transaction: %s", err)
|
s.logger.Error("opening transaction", slogutil.KeyError, err)
|
||||||
|
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
@ -568,7 +589,7 @@ func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, curID uint32) {
|
||||||
units = make([]*unitDB, 0, limit)
|
units = make([]*unitDB, 0, limit)
|
||||||
firstID := curID - limit + 1
|
firstID := curID - limit + 1
|
||||||
for i := firstID; i != curID; i++ {
|
for i := firstID; i != curID; i++ {
|
||||||
u := loadUnitFromDB(tx, i)
|
u := s.loadUnitFromDB(tx, i)
|
||||||
if u == nil {
|
if u == nil {
|
||||||
u = &unitDB{NResult: make([]uint64, resultLast)}
|
u = &unitDB{NResult: make([]uint64, resultLast)}
|
||||||
}
|
}
|
||||||
|
@ -577,7 +598,7 @@ func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, curID uint32) {
|
||||||
|
|
||||||
err = finishTxn(tx, false)
|
err = finishTxn(tx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("stats: %s", err)
|
s.logger.Error("finishing transaction", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cur != nil {
|
if cur != nil {
|
||||||
|
@ -585,7 +606,8 @@ func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, curID uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if unitsLen := len(units); unitsLen != int(limit) {
|
if unitsLen := len(units); unitsLen != int(limit) {
|
||||||
log.Fatalf("loaded %d units whilst the desired number is %d", unitsLen, limit)
|
// Should not happen.
|
||||||
|
panic(fmt.Errorf("loaded %d units when the desired number is %d", unitsLen, limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
return units, curID
|
return units, curID
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/testutil"
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/AdguardTeam/golibs/timeutil"
|
"github.com/AdguardTeam/golibs/timeutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -18,6 +19,7 @@ func TestStats_races(t *testing.T) {
|
||||||
var r uint32
|
var r uint32
|
||||||
idGen := func() (id uint32) { return atomic.LoadUint32(&r) }
|
idGen := func() (id uint32) { return atomic.LoadUint32(&r) }
|
||||||
conf := Config{
|
conf := Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
ShouldCountClient: func([]string) bool { return true },
|
ShouldCountClient: func([]string) bool { return true },
|
||||||
UnitID: idGen,
|
UnitID: idGen,
|
||||||
Filename: filepath.Join(t.TempDir(), "./stats.db"),
|
Filename: filepath.Join(t.TempDir(), "./stats.db"),
|
||||||
|
@ -94,6 +96,7 @@ func TestStatsCtx_FillCollectedStats_daily(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
s, err := New(Config{
|
s, err := New(Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
ShouldCountClient: func([]string) bool { return true },
|
ShouldCountClient: func([]string) bool { return true },
|
||||||
Filename: filepath.Join(t.TempDir(), "./stats.db"),
|
Filename: filepath.Join(t.TempDir(), "./stats.db"),
|
||||||
Limit: time.Hour,
|
Limit: time.Hour,
|
||||||
|
@ -151,6 +154,7 @@ func TestStatsCtx_DataFromUnits_month(t *testing.T) {
|
||||||
const hoursInMonth = 720
|
const hoursInMonth = 720
|
||||||
|
|
||||||
s, err := New(Config{
|
s, err := New(Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
ShouldCountClient: func([]string) bool { return true },
|
ShouldCountClient: func([]string) bool { return true },
|
||||||
Filename: filepath.Join(t.TempDir(), "./stats.db"),
|
Filename: filepath.Join(t.TempDir(), "./stats.db"),
|
||||||
Limit: time.Hour,
|
Limit: time.Hour,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
"github.com/AdguardTeam/golibs/testutil"
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/AdguardTeam/golibs/timeutil"
|
"github.com/AdguardTeam/golibs/timeutil"
|
||||||
|
@ -21,10 +22,6 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
testutil.DiscardLogOutput(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// constUnitID is the UnitIDGenFunc which always return 0.
|
// constUnitID is the UnitIDGenFunc which always return 0.
|
||||||
func constUnitID() (id uint32) { return 0 }
|
func constUnitID() (id uint32) { return 0 }
|
||||||
|
|
||||||
|
@ -55,6 +52,7 @@ func TestStats(t *testing.T) {
|
||||||
|
|
||||||
handlers := map[string]http.Handler{}
|
handlers := map[string]http.Handler{}
|
||||||
conf := stats.Config{
|
conf := stats.Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
ShouldCountClient: func([]string) bool { return true },
|
ShouldCountClient: func([]string) bool { return true },
|
||||||
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
||||||
Limit: timeutil.Day,
|
Limit: timeutil.Day,
|
||||||
|
@ -171,6 +169,7 @@ func TestLargeNumbers(t *testing.T) {
|
||||||
handlers := map[string]http.Handler{}
|
handlers := map[string]http.Handler{}
|
||||||
|
|
||||||
conf := stats.Config{
|
conf := stats.Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
ShouldCountClient: func([]string) bool { return true },
|
ShouldCountClient: func([]string) bool { return true },
|
||||||
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
||||||
Limit: timeutil.Day,
|
Limit: timeutil.Day,
|
||||||
|
@ -222,6 +221,7 @@ func TestShouldCount(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s, err := stats.New(stats.Config{
|
s, err := stats.New(stats.Config{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
||||||
Limit: timeutil.Day,
|
Limit: timeutil.Day,
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
@ -277,13 +277,14 @@ func (u *unit) serialize() (udb *unitDB) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadUnitFromDB(tx *bbolt.Tx, id uint32) (udb *unitDB) {
|
// loadUnitFromDB loads unit by id from the database.
|
||||||
|
func (s *StatsCtx) loadUnitFromDB(tx *bbolt.Tx, id uint32) (udb *unitDB) {
|
||||||
bkt := tx.Bucket(idToUnitName(id))
|
bkt := tx.Bucket(idToUnitName(id))
|
||||||
if bkt == nil {
|
if bkt == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("Loading unit %d", id)
|
s.logger.Debug("loading unit", "id", id)
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.Write(bkt.Get([]byte{0}))
|
buf.Write(bkt.Get([]byte{0}))
|
||||||
|
@ -291,7 +292,7 @@ func loadUnitFromDB(tx *bbolt.Tx, id uint32) (udb *unitDB) {
|
||||||
|
|
||||||
err := gob.NewDecoder(&buf).Decode(udb)
|
err := gob.NewDecoder(&buf).Decode(udb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("gob Decode: %s", err)
|
s.logger.Error("gob decode", slogutil.KeyError, err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -339,8 +340,8 @@ func (u *unit) add(e *Entry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// flushUnitToDB puts udb to the database at id.
|
// flushUnitToDB puts udb to the database at id.
|
||||||
func (udb *unitDB) flushUnitToDB(tx *bbolt.Tx, id uint32) (err error) {
|
func (s *StatsCtx) flushUnitToDB(udb *unitDB, tx *bbolt.Tx, id uint32) (err error) {
|
||||||
log.Debug("stats: flushing unit with id %d and total of %d", id, udb.NTotal)
|
s.logger.Debug("flushing unit", "id", id, "req_num", udb.NTotal)
|
||||||
|
|
||||||
bkt, err := tx.CreateBucketIfNotExists(idToUnitName(id))
|
bkt, err := tx.CreateBucketIfNotExists(idToUnitName(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/AdguardTeam/AdGuardHome/internal/tools
|
module github.com/AdguardTeam/AdGuardHome/internal/tools
|
||||||
|
|
||||||
go 1.22.6
|
go 1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fzipp/gocyclo v0.6.0
|
github.com/fzipp/gocyclo v0.6.0
|
||||||
|
@ -8,9 +8,9 @@ require (
|
||||||
github.com/gordonklaus/ineffassign v0.1.0
|
github.com/gordonklaus/ineffassign v0.1.0
|
||||||
github.com/kisielk/errcheck v1.7.0
|
github.com/kisielk/errcheck v1.7.0
|
||||||
github.com/kyoh86/looppointer v0.2.1
|
github.com/kyoh86/looppointer v0.2.1
|
||||||
github.com/securego/gosec/v2 v2.20.0
|
github.com/securego/gosec/v2 v2.21.2
|
||||||
github.com/uudashr/gocognit v1.1.3
|
github.com/uudashr/gocognit v1.1.3
|
||||||
golang.org/x/tools v0.24.0
|
golang.org/x/tools v0.25.0
|
||||||
golang.org/x/vuln v1.1.3
|
golang.org/x/vuln v1.1.3
|
||||||
honnef.co/go/tools v0.5.1
|
honnef.co/go/tools v0.5.1
|
||||||
mvdan.cc/gofumpt v0.7.0
|
mvdan.cc/gofumpt v0.7.0
|
||||||
|
@ -18,17 +18,48 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go v0.115.1 // indirect
|
||||||
|
cloud.google.com/go/ai v0.8.2 // indirect
|
||||||
|
cloud.google.com/go/auth v0.9.3 // indirect
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||||
|
cloud.google.com/go/longrunning v0.6.0 // indirect
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/google/generative-ai-go v0.18.0 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.8 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||||
github.com/gookit/color v1.5.4 // indirect
|
github.com/gookit/color v1.5.4 // indirect
|
||||||
github.com/kyoh86/nolint v0.0.1 // indirect
|
github.com/kyoh86/nolint v0.0.1 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240808152545-0cdaa3abc0fa // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/mod v0.20.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||||
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||||
|
golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||||
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
|
golang.org/x/net v0.29.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.23.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.24.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
golang.org/x/telemetry v0.0.0-20240815150606-0693e6240b9b // indirect
|
golang.org/x/telemetry v0.0.0-20240906150435-6d9f2eb83631 // indirect
|
||||||
|
golang.org/x/text v0.18.0 // indirect
|
||||||
|
golang.org/x/time v0.6.0 // indirect
|
||||||
|
google.golang.org/api v0.196.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
|
google.golang.org/grpc v1.66.1 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,29 +1,87 @@
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
|
||||||
|
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
|
||||||
|
cloud.google.com/go/ai v0.8.2 h1:LEaQwqBv+k2ybrcdTtCTc9OPZXoEdcQaGrfvDYS6Bnk=
|
||||||
|
cloud.google.com/go/ai v0.8.2/go.mod h1:Wb3EUUGWwB6yHBaUf/+oxUq/6XbCaU1yh0GrwUS8lr4=
|
||||||
|
cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
|
||||||
|
cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||||
|
cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI=
|
||||||
|
cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
||||||
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||||
|
github.com/google/generative-ai-go v0.18.0 h1:6ybg9vOCLcI/UpBBYXOTVgvKmcUKFRNj+2Cj3GnebSo=
|
||||||
|
github.com/google/generative-ai-go v0.18.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
|
||||||
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
|
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
|
||||||
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
|
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||||
|
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||||
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
||||||
|
@ -38,76 +96,154 @@ github.com/kyoh86/looppointer v0.2.1 h1:Jx9fnkBj/JrIryBLMTYNTj9rvc2SrPS98Dg0w7fx
|
||||||
github.com/kyoh86/looppointer v0.2.1/go.mod h1:q358WcM8cMWU+5vzqukvaZtnJi1kw/MpRHQm3xvTrjw=
|
github.com/kyoh86/looppointer v0.2.1/go.mod h1:q358WcM8cMWU+5vzqukvaZtnJi1kw/MpRHQm3xvTrjw=
|
||||||
github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
|
github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
|
||||||
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
|
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
|
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/securego/gosec/v2 v2.20.0 h1:z/d5qp1niWa2avgFyUIglYTYYuGq2LrJwNj1HRVXsqc=
|
github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M=
|
||||||
github.com/securego/gosec/v2 v2.20.0/go.mod h1:hkiArbBZLwK1cehBcg3oFWUlYPWTBffPwwJVWChu83o=
|
github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
|
github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
|
||||||
github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
|
github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||||
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240808152545-0cdaa3abc0fa h1:54T+HVkPu4D3lltpEHyI3Fs2pG/GqjGkXLgyKOmifXk=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||||
|
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||||
|
golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 h1:bVwtbF629Xlyxk6xLQq2TDYmqP0uiWaet5LwRebuY0k=
|
||||||
|
golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||||
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/telemetry v0.0.0-20240815150606-0693e6240b9b h1:bqoSGda4GdIdppjxRPd2mhhJDviKUBPMaBDIZeHi4Po=
|
golang.org/x/telemetry v0.0.0-20240906150435-6d9f2eb83631 h1:/udK7fpCEWRkSveodOtAfxZbfWmVndguEdET1gRwdHY=
|
||||||
golang.org/x/telemetry v0.0.0-20240815150606-0693e6240b9b/go.mod h1:m7R/r+o5h7UvF2JD9n2iLSGY4v8v+zNSyTJ6xynLrqs=
|
golang.org/x/telemetry v0.0.0-20240906150435-6d9f2eb83631/go.mod h1:PsFMgI0jiuY7j+qwXANuh9a/x5kQESTSnRow3gapUyk=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||||
golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw=
|
golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw=
|
||||||
golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY=
|
golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.196.0 h1:k/RafYqebaIJBO3+SMnfEGtFVlvp5vSgqTUF54UN/zg=
|
||||||
|
google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
|
||||||
|
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
|
honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
|
||||||
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
|
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
|
||||||
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
|
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -14,10 +16,13 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
l := slogutil.New(nil)
|
||||||
|
|
||||||
urlStr := "https://adguardteam.github.io/HostlistsRegistry/assets/services.json"
|
urlStr := "https://adguardteam.github.io/HostlistsRegistry/assets/services.json"
|
||||||
if v, ok := os.LookupEnv("URL"); ok {
|
if v, ok := os.LookupEnv("URL"); ok {
|
||||||
urlStr = v
|
urlStr = v
|
||||||
|
@ -33,7 +38,7 @@ func main() {
|
||||||
|
|
||||||
resp, err := c.Get(urlStr)
|
resp, err := c.Get(urlStr)
|
||||||
check(err)
|
check(err)
|
||||||
defer log.OnCloserError(resp.Body, log.ERROR)
|
defer slogutil.CloseAndLog(ctx, l, resp.Body, slog.LevelError)
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
panic(fmt.Errorf("expected code %d, got %d", http.StatusOK, resp.StatusCode))
|
panic(fmt.Errorf("expected code %d, got %d", http.StatusOK, resp.StatusCode))
|
||||||
|
@ -64,7 +69,7 @@ func main() {
|
||||||
0o644,
|
0o644,
|
||||||
)
|
)
|
||||||
check(err)
|
check(err)
|
||||||
defer log.OnCloserError(f, log.ERROR)
|
defer slogutil.CloseAndLog(ctx, l, f, slog.LevelError)
|
||||||
|
|
||||||
err = tmpl.Execute(f, hlSvcs)
|
err = tmpl.Execute(f, hlSvcs)
|
||||||
check(err)
|
check(err)
|
||||||
|
|
|
@ -83,11 +83,15 @@ if [ "$sign" -eq '1' ]
|
||||||
then
|
then
|
||||||
gpg_key_passphrase="${GPG_KEY_PASSPHRASE:?please set GPG_KEY_PASSPHRASE or unset SIGN}"
|
gpg_key_passphrase="${GPG_KEY_PASSPHRASE:?please set GPG_KEY_PASSPHRASE or unset SIGN}"
|
||||||
gpg_key="${GPG_KEY:?please set GPG_KEY or unset SIGN}"
|
gpg_key="${GPG_KEY:?please set GPG_KEY or unset SIGN}"
|
||||||
|
signer_api_key="${SIGNER_API_KEY:?please set SIGNER_API_KEY or unset SIGN}"
|
||||||
|
deploy_script_path="${DEPLOY_SCRIPT_PATH:?please set DEPLOY_SCRIPT_PATH or unset SIGN}"
|
||||||
else
|
else
|
||||||
gpg_key_passphrase=''
|
gpg_key_passphrase=''
|
||||||
gpg_key=''
|
gpg_key=''
|
||||||
|
signer_api_key=''
|
||||||
|
deploy_script_path=''
|
||||||
fi
|
fi
|
||||||
readonly gpg_key_passphrase gpg_key
|
readonly gpg_key_passphrase gpg_key signer_api_key deploy_script_path
|
||||||
|
|
||||||
# The default distribution files directory is dist.
|
# The default distribution files directory is dist.
|
||||||
dist="${DIST_DIR:-dist}"
|
dist="${DIST_DIR:-dist}"
|
||||||
|
@ -149,6 +153,50 @@ windows amd64 - -
|
||||||
windows arm64 - -"
|
windows arm64 - -"
|
||||||
readonly platforms
|
readonly platforms
|
||||||
|
|
||||||
|
# Function sign signs the specified build as intended by the target operating
|
||||||
|
# system.
|
||||||
|
sign() {
|
||||||
|
# Only sign if needed.
|
||||||
|
if [ "$sign" -ne '1' ]
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the arguments. Here and below, use the "sign_" prefix for all
|
||||||
|
# variables local to function sign.
|
||||||
|
sign_os="$1"
|
||||||
|
sign_bin_path="$2"
|
||||||
|
|
||||||
|
if [ "$sign_os" != 'windows' ]
|
||||||
|
then
|
||||||
|
gpg\
|
||||||
|
--default-key "$gpg_key"\
|
||||||
|
--detach-sig\
|
||||||
|
--passphrase "$gpg_key_passphrase"\
|
||||||
|
--pinentry-mode loopback\
|
||||||
|
-q\
|
||||||
|
"$sign_bin_path"\
|
||||||
|
;
|
||||||
|
|
||||||
|
return
|
||||||
|
# TODO(e.burkov): Enable for all releases.
|
||||||
|
elif [ "$channel" != 'beta' ]
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
signed_bin_path="${sign_bin_path}.signed"
|
||||||
|
|
||||||
|
env\
|
||||||
|
INPUT_FILE="$sign_bin_path"\
|
||||||
|
OUTPUT_FILE="$signed_bin_path"\
|
||||||
|
SIGNER_API_KEY="$signer_api_key"\
|
||||||
|
"$deploy_script_path" sign-executable\
|
||||||
|
;
|
||||||
|
|
||||||
|
mv "$signed_bin_path" "$sign_bin_path"
|
||||||
|
}
|
||||||
|
|
||||||
# Function build builds the release for one platform. It builds a binary and an
|
# Function build builds the release for one platform. It builds a binary and an
|
||||||
# archive.
|
# archive.
|
||||||
build() {
|
build() {
|
||||||
|
@ -189,17 +237,7 @@ build() {
|
||||||
|
|
||||||
log "$build_output"
|
log "$build_output"
|
||||||
|
|
||||||
if [ "$sign" -eq '1' ]
|
sign "$os" "$build_output"
|
||||||
then
|
|
||||||
gpg\
|
|
||||||
--default-key "$gpg_key"\
|
|
||||||
--detach-sig\
|
|
||||||
--passphrase "$gpg_key_passphrase"\
|
|
||||||
--pinentry-mode loopback\
|
|
||||||
-q\
|
|
||||||
"$build_output"\
|
|
||||||
;
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prepare the build directory for archiving.
|
# Prepare the build directory for archiving.
|
||||||
cp ./CHANGELOG.md ./LICENSE.txt ./README.md "$build_dir"
|
cp ./CHANGELOG.md ./LICENSE.txt ./README.md "$build_dir"
|
||||||
|
|
|
@ -244,9 +244,9 @@ run_linter fieldalignment \
|
||||||
run_linter -e shadow --strict ./...
|
run_linter -e shadow --strict ./...
|
||||||
|
|
||||||
# TODO(a.garipov): Enable for all.
|
# TODO(a.garipov): Enable for all.
|
||||||
run_linter gosec --quiet\
|
# TODO(e.burkov): Re-enable G115.
|
||||||
|
run_linter gosec --exclude G115 --quiet\
|
||||||
./internal/aghalg/\
|
./internal/aghalg/\
|
||||||
./internal/aghchan/\
|
|
||||||
./internal/aghhttp/\
|
./internal/aghhttp/\
|
||||||
./internal/aghnet/\
|
./internal/aghnet/\
|
||||||
./internal/aghos/\
|
./internal/aghos/\
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/ioutil"
|
"github.com/AdguardTeam/golibs/ioutil"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// download and save all translations.
|
// download and save all translations.
|
||||||
func (c *twoskyClient) download() (err error) {
|
func (c *twoskyClient) download(ctx context.Context, l *slog.Logger) (err error) {
|
||||||
var numWorker int
|
var numWorker int
|
||||||
|
|
||||||
flagSet := flag.NewFlagSet("download", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("download", flag.ExitOnError)
|
||||||
|
@ -50,7 +51,7 @@ func (c *twoskyClient) download() (err error) {
|
||||||
|
|
||||||
for range numWorker {
|
for range numWorker {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go downloadWorker(wg, failed, client, uriCh)
|
go downloadWorker(ctx, l, wg, failed, client, uriCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, lang := range c.langs {
|
for _, lang := range c.langs {
|
||||||
|
@ -62,13 +63,13 @@ func (c *twoskyClient) download() (err error) {
|
||||||
close(uriCh)
|
close(uriCh)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
printFailedLocales(failed)
|
printFailedLocales(ctx, l, failed)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// printFailedLocales prints sorted list of failed downloads, if any.
|
// printFailedLocales prints sorted list of failed downloads, if any.
|
||||||
func printFailedLocales(failed *sync.Map) {
|
func printFailedLocales(ctx context.Context, l *slog.Logger, failed *sync.Map) {
|
||||||
keys := []string{}
|
keys := []string{}
|
||||||
failed.Range(func(k, _ any) bool {
|
failed.Range(func(k, _ any) bool {
|
||||||
s, ok := k.(string)
|
s, ok := k.(string)
|
||||||
|
@ -86,12 +87,14 @@ func printFailedLocales(failed *sync.Map) {
|
||||||
}
|
}
|
||||||
|
|
||||||
slices.Sort(keys)
|
slices.Sort(keys)
|
||||||
log.Info("failed locales: %s", strings.Join(keys, " "))
|
l.InfoContext(ctx, "failed", "locales", keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadWorker downloads translations by received urls and saves them.
|
// downloadWorker downloads translations by received urls and saves them.
|
||||||
// Where failed is a map for storing failed downloads.
|
// Where failed is a map for storing failed downloads.
|
||||||
func downloadWorker(
|
func downloadWorker(
|
||||||
|
ctx context.Context,
|
||||||
|
l *slog.Logger,
|
||||||
wg *sync.WaitGroup,
|
wg *sync.WaitGroup,
|
||||||
failed *sync.Map,
|
failed *sync.Map,
|
||||||
client *http.Client,
|
client *http.Client,
|
||||||
|
@ -103,9 +106,9 @@ func downloadWorker(
|
||||||
q := uri.Query()
|
q := uri.Query()
|
||||||
code := q.Get("language")
|
code := q.Get("language")
|
||||||
|
|
||||||
err := saveToFile(client, uri, code)
|
err := saveToFile(ctx, l, client, uri, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("download: worker: %s", err)
|
l.ErrorContext(ctx, "download worker", slogutil.KeyError, err)
|
||||||
failed.Store(code, struct{}{})
|
failed.Store(code, struct{}{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,12 +116,16 @@ func downloadWorker(
|
||||||
|
|
||||||
// saveToFile downloads translation by url and saves it to a file, or returns
|
// saveToFile downloads translation by url and saves it to a file, or returns
|
||||||
// error.
|
// error.
|
||||||
func saveToFile(client *http.Client, uri *url.URL, code string) (err error) {
|
func saveToFile(
|
||||||
data, err := getTranslation(client, uri.String())
|
ctx context.Context,
|
||||||
|
l *slog.Logger,
|
||||||
|
client *http.Client,
|
||||||
|
uri *url.URL,
|
||||||
|
code string,
|
||||||
|
) (err error) {
|
||||||
|
data, err := getTranslation(ctx, l, client, uri.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("%s", data)
|
return fmt.Errorf("getting translation %q: %s", code, err)
|
||||||
|
|
||||||
return fmt.Errorf("getting translation: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name := filepath.Join(localesDir, code+".json")
|
name := filepath.Join(localesDir, code+".json")
|
||||||
|
@ -134,13 +141,18 @@ func saveToFile(client *http.Client, uri *url.URL, code string) (err error) {
|
||||||
|
|
||||||
// getTranslation returns received translation data and error. If err is not
|
// getTranslation returns received translation data and error. If err is not
|
||||||
// nil, data may contain a response from server for inspection.
|
// nil, data may contain a response from server for inspection.
|
||||||
func getTranslation(client *http.Client, url string) (data []byte, err error) {
|
func getTranslation(
|
||||||
|
ctx context.Context,
|
||||||
|
l *slog.Logger,
|
||||||
|
client *http.Client,
|
||||||
|
url string,
|
||||||
|
) (data []byte, err error) {
|
||||||
resp, err := client.Get(url)
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("requesting: %w", err)
|
return nil, fmt.Errorf("requesting: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer log.OnCloserError(resp.Body, log.ERROR)
|
defer slogutil.CloseAndLog(ctx, l, resp.Body, slog.LevelError)
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
err = fmt.Errorf("url: %q; status code: %s", url, http.StatusText(resp.StatusCode))
|
err = fmt.Errorf("url: %q; status code: %s", url, http.StatusText(resp.StatusCode))
|
||||||
|
|
|
@ -6,8 +6,10 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -18,7 +20,7 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,6 +65,9 @@ type textLabel string
|
||||||
type locales map[textLabel]string
|
type locales map[textLabel]string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
l := slogutil.New(nil)
|
||||||
|
|
||||||
if len(os.Args) == 1 {
|
if len(os.Args) == 1 {
|
||||||
usage("need a command")
|
usage("need a command")
|
||||||
}
|
}
|
||||||
|
@ -83,9 +88,9 @@ func main() {
|
||||||
cli, err = conf.toClient()
|
cli, err = conf.toClient()
|
||||||
check(err)
|
check(err)
|
||||||
|
|
||||||
err = cli.download()
|
err = cli.download(ctx, l)
|
||||||
case "unused":
|
case "unused":
|
||||||
err = unused(conf.LocalizableFiles[0])
|
err = unused(ctx, l, conf.LocalizableFiles[0])
|
||||||
case "upload":
|
case "upload":
|
||||||
cli, err = conf.toClient()
|
cli, err = conf.toClient()
|
||||||
check(err)
|
check(err)
|
||||||
|
@ -322,7 +327,7 @@ func summary(langs languages) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// unused prints unused text labels.
|
// unused prints unused text labels.
|
||||||
func unused(basePath string) (err error) {
|
func unused(ctx context.Context, l *slog.Logger, basePath string) (err error) {
|
||||||
defer func() { err = errors.Annotate(err, "unused: %w") }()
|
defer func() { err = errors.Annotate(err, "unused: %w") }()
|
||||||
|
|
||||||
baseLoc, err := readLocales(basePath)
|
baseLoc, err := readLocales(basePath)
|
||||||
|
@ -331,7 +336,7 @@ func unused(basePath string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
locDir := filepath.Clean(localesDir)
|
locDir := filepath.Clean(localesDir)
|
||||||
js, err := findJS(locDir)
|
js, err := findJS(ctx, l, locDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -340,10 +345,10 @@ func unused(basePath string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// findJS returns list of JavaScript and JSON files or error.
|
// findJS returns list of JavaScript and JSON files or error.
|
||||||
func findJS(locDir string) (fileNames []string, err error) {
|
func findJS(ctx context.Context, l *slog.Logger, locDir string) (fileNames []string, err error) {
|
||||||
walkFn := func(name string, _ os.FileInfo, err error) error {
|
walkFn := func(name string, _ os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("warning: accessing a path %q: %s", name, err)
|
l.WarnContext(ctx, "accessing a path", slogutil.KeyError, err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue