Enable some linters (#3129)

Mostly those that did not require much work.

From #2960

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
qwerty287 2024-01-09 21:35:37 +01:00 committed by GitHub
parent 631b7c2fed
commit 768fd71841
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 121 additions and 63 deletions

View file

@ -102,6 +102,24 @@ linters-settings:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/agent' - pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/cli' - pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker' - pkg: 'go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker'
gci:
sections:
- standard
- default
- prefix(go.woodpecker-ci.org/woodpecker)
gomnd:
ignored-numbers:
- '0o600'
- '0o660'
- '0o644'
- '0o755'
- '0o700'
ignored-functions:
- make
- time.*
- strings.Split
- callerName
- securecookie.GenerateRandomKey
linters: linters:
disable-all: true disable-all: true
@ -124,6 +142,25 @@ linters:
- forbidigo - forbidigo
- zerologlint - zerologlint
- depguard - depguard
- asciicheck
- bodyclose
- dogsled
- durationcheck
- errchkjson
- gochecknoinits
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- importas
- makezero
- rowserrcheck
- sqlclosecheck
- tenv
- unconvert
- unparam
- wastedassign
- whitespace
run: run:
timeout: 15m timeout: 15m
@ -140,7 +177,12 @@ issues:
- path: 'cmd/*|cli/*' - path: 'cmd/*|cli/*'
linters: linters:
- forbidigo - forbidigo
# allow some setup functions to use log.Fatal() # allow some setup functions to use log.Fatal()
- path: 'server/web/web.go|server/plugins/encryption/tink_keyset_watcher.go|shared/logger/logger.go' - path: 'server/web/web.go|server/plugins/encryption/tink_keyset_watcher.go|shared/logger/logger.go'
linters: linters:
- forbidigo - forbidigo
- path: '_test.go'
linters:
- forcetypeassert

View file

@ -74,8 +74,11 @@ func (s *State) Healthy() bool {
func (s *State) WriteTo(w io.Writer) (int64, error) { func (s *State) WriteTo(w io.Writer) (int64, error) {
s.Lock() s.Lock()
out, _ := json.Marshal(s) out, err := json.Marshal(s)
s.Unlock() s.Unlock()
if err != nil {
return 0, err
}
ret, err := w.Write(out) ret, err := w.Write(out)
return int64(ret), err return int64(ret), err
} }

View file

@ -271,6 +271,8 @@ func getBackendEngine(backendCtx context.Context, backendName string, addons []s
} }
func runWithRetry(context *cli.Context) error { func runWithRetry(context *cli.Context) error {
initHealth()
retryCount := context.Int("connect-retry-count") retryCount := context.Int("connect-retry-count")
retryDelay := context.Duration("connect-retry-delay") retryDelay := context.Duration("connect-retry-delay")
var err error var err error

View file

@ -31,7 +31,7 @@ import (
// following specification: // following specification:
// https://github.com/mozilla-services/Dockerflow // https://github.com/mozilla-services/Dockerflow
func init() { func initHealth() {
http.HandleFunc("/varz", handleStats) http.HandleFunc("/varz", handleStats)
http.HandleFunc("/healthz", handleHeartbeat) http.HandleFunc("/healthz", handleHeartbeat)
http.HandleFunc("/version", handleVersion) http.HandleFunc("/version", handleVersion)
@ -48,10 +48,13 @@ func handleHeartbeat(w http.ResponseWriter, _ *http.Request) {
func handleVersion(w http.ResponseWriter, _ *http.Request) { func handleVersion(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200) w.WriteHeader(200)
w.Header().Add("Content-Type", "text/json") w.Header().Add("Content-Type", "text/json")
_ = json.NewEncoder(w).Encode(versionResp{ err := json.NewEncoder(w).Encode(versionResp{
Source: "https://github.com/woodpecker-ci/woodpecker", Source: "https://github.com/woodpecker-ci/woodpecker",
Version: version.String(), Version: version.String(),
}) })
if err != nil {
log.Error().Err(err).Msg("handleVersion")
}
} }
func handleStats(w http.ResponseWriter, _ *http.Request) { func handleStats(w http.ResponseWriter, _ *http.Request) {

View file

@ -87,10 +87,7 @@ func run(c *cli.Context) error {
log.Fatal().Err(err).Msg("can't setup forge") log.Fatal().Err(err).Msg("can't setup forge")
} }
_store, err := setupStore(c) _store := setupStore(c)
if err != nil {
log.Fatal().Err(err).Msg("can't setup database store")
}
defer func() { defer func() {
if err := _store.Close(); err != nil { if err := _store.Close(); err != nil {
log.Error().Err(err).Msg("could not close store") log.Error().Err(err).Msg("could not close store")
@ -317,7 +314,7 @@ func setupEvilGlobals(c *cli.Context, v store.Store, f forge.Forge) error {
// Execution // Execution
_events := c.StringSlice("default-cancel-previous-pipeline-events") _events := c.StringSlice("default-cancel-previous-pipeline-events")
events := make([]model.WebhookEvent, len(_events)) events := make([]model.WebhookEvent, len(_events), 0)
for _, v := range _events { for _, v := range _events {
events = append(events, model.WebhookEvent(v)) events = append(events, model.WebhookEvent(v))
} }

View file

@ -54,7 +54,7 @@ import (
addonTypes "go.woodpecker-ci.org/woodpecker/v2/shared/addon/types" addonTypes "go.woodpecker-ci.org/woodpecker/v2/shared/addon/types"
) )
func setupStore(c *cli.Context) (store.Store, error) { func setupStore(c *cli.Context) store.Store {
datasource := c.String("datasource") datasource := c.String("datasource")
driver := c.String("driver") driver := c.String("driver")
xorm := store.XORM{ xorm := store.XORM{
@ -95,7 +95,7 @@ func setupStore(c *cli.Context) (store.Store, error) {
log.Fatal().Err(err).Msg("could not migrate datastore") log.Fatal().Err(err).Msg("could not migrate datastore")
} }
return store, nil return store
} }
func checkSqliteFileExist(path string) error { func checkSqliteFileExist(path string) error {

View file

@ -586,9 +586,9 @@ Specify a configuration service endpoint, see [Configuration Extension](./100-ex
### `WOODPECKER_FORGE_TIMEOUT` ### `WOODPECKER_FORGE_TIMEOUT`
> Default: 3sec > Default: 3s
Specify how many seconds before timeout when fetching the Woodpecker configuration from a Forge Specify timeout when fetching the Woodpecker configuration from forge. See <https://pkg.go.dev/time#ParseDuration> for syntax reference.
### `WOODPECKER_ENABLE_SWAGGER` ### `WOODPECKER_ENABLE_SWAGGER`

View file

@ -26,7 +26,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
) )
func mkService(namespace, name string, ports []uint16, selector map[string]string) (*v1.Service, error) { func mkService(namespace, name string, ports []uint16, selector map[string]string) *v1.Service {
log.Trace().Str("name", name).Interface("selector", selector).Interface("ports", ports).Msg("Creating service") log.Trace().Str("name", name).Interface("selector", selector).Interface("ports", ports).Msg("Creating service")
var svcPorts []v1.ServicePort var svcPorts []v1.ServicePort
@ -48,7 +48,7 @@ func mkService(namespace, name string, ports []uint16, selector map[string]strin
Selector: selector, Selector: selector,
Ports: svcPorts, Ports: svcPorts,
}, },
}, nil }
} }
func serviceName(step *types.Step) (string, error) { func serviceName(step *types.Step) (string, error) {
@ -69,10 +69,7 @@ func startService(ctx context.Context, engine *kube, step *types.Step) (*v1.Serv
StepLabel: podName, StepLabel: podName,
} }
svc, err := mkService(engine.config.Namespace, name, step.Ports, selector) svc := mkService(engine.config.Namespace, name, step.Ports, selector)
if err != nil {
return nil, err
}
return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{}) return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{})
} }

View file

@ -71,7 +71,7 @@ func TestService(t *testing.T) {
} }
}` }`
s, _ := mkService("foo", "bar", []uint16{1, 2, 3}, map[string]string{"step": "baz"}) s := mkService("foo", "bar", []uint16{1, 2, 3}, map[string]string{"step": "baz"})
j, err := json.Marshal(s) j, err := json.Marshal(s)
assert.NoError(t, err) assert.NoError(t, err)
assert.JSONEq(t, expected, string(j)) assert.JSONEq(t, expected, string(j))

View file

@ -21,6 +21,8 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/rs/zerolog/log"
) )
var ( var (
@ -136,7 +138,10 @@ func (m *Metadata) Environ() map[string]string {
params["CI_PIPELINE_FILES"] = "[]" params["CI_PIPELINE_FILES"] = "[]"
} else if len(m.Curr.Commit.ChangedFiles) <= maxChangedFiles { } else if len(m.Curr.Commit.ChangedFiles) <= maxChangedFiles {
// we have to use json, as other separators like ;, or space are valid filename chars // we have to use json, as other separators like ;, or space are valid filename chars
changedFiles, _ := json.Marshal(m.Curr.Commit.ChangedFiles) changedFiles, err := json.Marshal(m.Curr.Commit.ChangedFiles)
if err != nil {
log.Error().Err(err).Msg("marshal changed files")
}
params["CI_PIPELINE_FILES"] = string(changedFiles) params["CI_PIPELINE_FILES"] = string(changedFiles)
} }

View file

@ -61,7 +61,7 @@ func (cf *configFetcher) Fetch(ctx context.Context) (files []*types.FileMeta, er
// try to fetch 3 times // try to fetch 3 times
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
files, err = cf.fetch(ctx, time.Second*cf.timeout, strings.TrimSpace(cf.configPath)) files, err = cf.fetch(ctx, cf.timeout, strings.TrimSpace(cf.configPath))
if err != nil { if err != nil {
log.Trace().Err(err).Msgf("%d. try failed", i+1) log.Trace().Err(err).Msgf("%d. try failed", i+1)
} }

View file

@ -77,7 +77,7 @@ func pipelineFromPush(hook *pushHook) *model.Pipeline {
fixMalformedAvatar(hook.Sender.AvatarURL), fixMalformedAvatar(hook.Sender.AvatarURL),
) )
message := "" var message string
link := hook.Compare link := hook.Compare
if len(hook.Commits) > 0 { if len(hook.Commits) > 0 {
message = hook.Commits[0].Message message = hook.Commits[0].Message

View file

@ -232,8 +232,7 @@ func Test_helper(t *testing.T) {
from.Sender.Login = github.String("octocat") from.Sender.Login = github.String("octocat")
from.Sender.AvatarURL = github.String("https://avatars1.githubusercontent.com/u/583231") from.Sender.AvatarURL = github.String("https://avatars1.githubusercontent.com/u/583231")
_, pipeline, err := parseDeployHook(from) _, pipeline := parseDeployHook(from)
g.Assert(err).IsNil()
g.Assert(pipeline.Event).Equal(model.EventDeploy) g.Assert(pipeline.Event).Equal(model.EventDeploy)
g.Assert(pipeline.Branch).Equal("main") g.Assert(pipeline.Branch).Equal("main")
g.Assert(pipeline.Ref).Equal("refs/heads/main") g.Assert(pipeline.Ref).Equal("refs/heads/main")
@ -255,8 +254,7 @@ func Test_helper(t *testing.T) {
from.HeadCommit.ID = github.String("f72fc19") from.HeadCommit.ID = github.String("f72fc19")
from.Ref = github.String("refs/heads/main") from.Ref = github.String("refs/heads/main")
_, pipeline, err := parsePushHook(from) _, pipeline := parsePushHook(from)
g.Assert(err).IsNil()
g.Assert(pipeline.Event).Equal(model.EventPush) g.Assert(pipeline.Event).Equal(model.EventPush)
g.Assert(pipeline.Branch).Equal("main") g.Assert(pipeline.Branch).Equal("main")
g.Assert(pipeline.Ref).Equal("refs/heads/main") g.Assert(pipeline.Ref).Equal("refs/heads/main")
@ -273,8 +271,7 @@ func Test_helper(t *testing.T) {
from := &github.PushEvent{} from := &github.PushEvent{}
from.Ref = github.String("refs/tags/v1.0.0") from.Ref = github.String("refs/tags/v1.0.0")
_, pipeline, err := parsePushHook(from) _, pipeline := parsePushHook(from)
g.Assert(err).IsNil()
g.Assert(pipeline.Event).Equal(model.EventTag) g.Assert(pipeline.Event).Equal(model.EventTag)
g.Assert(pipeline.Ref).Equal("refs/tags/v1.0.0") g.Assert(pipeline.Ref).Equal("refs/tags/v1.0.0")
}) })
@ -284,8 +281,7 @@ func Test_helper(t *testing.T) {
from.Ref = github.String("refs/tags/v1.0.0") from.Ref = github.String("refs/tags/v1.0.0")
from.BaseRef = github.String("refs/heads/main") from.BaseRef = github.String("refs/heads/main")
_, pipeline, err := parsePushHook(from) _, pipeline := parsePushHook(from)
g.Assert(err).IsNil()
g.Assert(pipeline.Event).Equal(model.EventTag) g.Assert(pipeline.Event).Equal(model.EventTag)
g.Assert(pipeline.Branch).Equal("main") g.Assert(pipeline.Branch).Equal("main")
}) })
@ -295,8 +291,7 @@ func Test_helper(t *testing.T) {
from.Ref = github.String("refs/tags/v1.0.0") from.Ref = github.String("refs/tags/v1.0.0")
from.BaseRef = github.String("refs/refs/main") from.BaseRef = github.String("refs/refs/main")
_, pipeline, err := parsePushHook(from) _, pipeline := parsePushHook(from)
g.Assert(err).IsNil()
g.Assert(pipeline.Event).Equal(model.EventTag) g.Assert(pipeline.Event).Equal(model.EventTag)
g.Assert(pipeline.Branch).Equal("refs/tags/v1.0.0") g.Assert(pipeline.Branch).Equal("refs/tags/v1.0.0")
}) })

View file

@ -61,11 +61,11 @@ func parseHook(r *http.Request, merge bool) (*github.PullRequest, *model.Repo, *
switch hook := payload.(type) { switch hook := payload.(type) {
case *github.PushEvent: case *github.PushEvent:
repo, pipeline, err := parsePushHook(hook) repo, pipeline := parsePushHook(hook)
return nil, repo, pipeline, err return nil, repo, pipeline, nil
case *github.DeploymentEvent: case *github.DeploymentEvent:
repo, pipeline, err := parseDeployHook(hook) repo, pipeline := parseDeployHook(hook)
return nil, repo, pipeline, err return nil, repo, pipeline, nil
case *github.PullRequestEvent: case *github.PullRequestEvent:
return parsePullHook(hook, merge) return parsePullHook(hook, merge)
default: default:
@ -75,9 +75,9 @@ func parseHook(r *http.Request, merge bool) (*github.PullRequest, *model.Repo, *
// parsePushHook parses a push hook and returns the Repo and Pipeline details. // parsePushHook parses a push hook and returns the Repo and Pipeline details.
// If the commit type is unsupported nil values are returned. // If the commit type is unsupported nil values are returned.
func parsePushHook(hook *github.PushEvent) (*model.Repo, *model.Pipeline, error) { func parsePushHook(hook *github.PushEvent) (*model.Repo, *model.Pipeline) {
if hook.Deleted != nil && *hook.Deleted { if hook.Deleted != nil && *hook.Deleted {
return nil, nil, nil return nil, nil
} }
pipeline := &model.Pipeline{ pipeline := &model.Pipeline{
@ -110,12 +110,12 @@ func parsePushHook(hook *github.PushEvent) (*model.Repo, *model.Pipeline, error)
} }
} }
return convertRepoHook(hook.GetRepo()), pipeline, nil return convertRepoHook(hook.GetRepo()), pipeline
} }
// parseDeployHook parses a deployment and returns the Repo and Pipeline details. // parseDeployHook parses a deployment and returns the Repo and Pipeline details.
// If the commit type is unsupported nil values are returned. // If the commit type is unsupported nil values are returned.
func parseDeployHook(hook *github.DeploymentEvent) (*model.Repo, *model.Pipeline, error) { func parseDeployHook(hook *github.DeploymentEvent) (*model.Repo, *model.Pipeline) {
pipeline := &model.Pipeline{ pipeline := &model.Pipeline{
Event: model.EventDeploy, Event: model.EventDeploy,
Commit: hook.GetDeployment().GetSHA(), Commit: hook.GetDeployment().GetSHA(),
@ -138,7 +138,7 @@ func parseDeployHook(hook *github.DeploymentEvent) (*model.Repo, *model.Pipeline
pipeline.Ref = fmt.Sprintf("refs/heads/%s", pipeline.Branch) pipeline.Ref = fmt.Sprintf("refs/heads/%s", pipeline.Branch)
} }
return convertRepo(hook.GetRepo()), pipeline, nil return convertRepo(hook.GetRepo()), pipeline
} }
// parsePullHook parses a pull request hook and returns the Repo and Pipeline // parsePullHook parses a pull request hook and returns the Repo and Pipeline

View file

@ -20,7 +20,7 @@ import (
"go.woodpecker-ci.org/woodpecker/v2/server/queue" "go.woodpecker-ci.org/woodpecker/v2/server/queue"
) )
func createFilterFunc(agentFilter rpc.Filter) (queue.FilterFn, error) { func createFilterFunc(agentFilter rpc.Filter) queue.FilterFn {
return func(task *model.Task) bool { return func(task *model.Task) bool {
for taskLabel, taskLabelValue := range task.Labels { for taskLabel, taskLabelValue := range task.Labels {
// if a task label is empty it will be ignored // if a task label is empty it will be ignored
@ -44,5 +44,5 @@ func createFilterFunc(agentFilter rpc.Filter) (queue.FilterFn, error) {
} }
} }
return true return true
}, nil }
} }

View file

@ -84,11 +84,7 @@ func TestCreateFilterFunc(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
fn, err := createFilterFunc(rpc.Filter{Labels: test.agentLabels}) fn := createFilterFunc(rpc.Filter{Labels: test.agentLabels})
if !assert.NoError(t, err) {
t.Fail()
}
assert.EqualValues(t, test.exp, fn(&test.task)) assert.EqualValues(t, test.exp, fn(&test.task))
}) })
} }

View file

@ -56,10 +56,7 @@ func (s *RPC) Next(c context.Context, agentFilter rpc.Filter) (*rpc.Workflow, er
log.Debug().Msgf("agent connected: %s: polling", hostname) log.Debug().Msgf("agent connected: %s: polling", hostname)
} }
fn, err := createFilterFunc(agentFilter) fn := createFilterFunc(agentFilter)
if err != nil {
return nil, err
}
for { for {
agent, err := s.getAgentFromContext(c) agent, err := s.getAgentFromContext(c)
if err != nil { if err != nil {
@ -151,10 +148,13 @@ func (s *RPC) Update(_ context.Context, id string, state rpc.State) error {
"private": strconv.FormatBool(repo.IsSCMPrivate), "private": strconv.FormatBool(repo.IsSCMPrivate),
}, },
} }
message.Data, _ = json.Marshal(model.Event{ message.Data, err = json.Marshal(model.Event{
Repo: *repo, Repo: *repo,
Pipeline: *currentPipeline, Pipeline: *currentPipeline,
}) })
if err != nil {
return err
}
s.pubsub.Publish(message) s.pubsub.Publish(message)
return nil return nil
@ -207,10 +207,14 @@ func (s *RPC) Init(c context.Context, id string, state rpc.State) error {
"private": strconv.FormatBool(repo.IsSCMPrivate), "private": strconv.FormatBool(repo.IsSCMPrivate),
}, },
} }
message.Data, _ = json.Marshal(model.Event{ message.Data, err = json.Marshal(model.Event{
Repo: *repo, Repo: *repo,
Pipeline: *currentPipeline, Pipeline: *currentPipeline,
}) })
if err != nil {
log.Error().Err(err).Msgf("could not marshal JSON")
return
}
s.pubsub.Publish(message) s.pubsub.Publish(message)
}() }()
@ -427,10 +431,13 @@ func (s *RPC) notify(repo *model.Repo, pipeline *model.Pipeline) (err error) {
"private": strconv.FormatBool(repo.IsSCMPrivate), "private": strconv.FormatBool(repo.IsSCMPrivate),
}, },
} }
message.Data, _ = json.Marshal(model.Event{ message.Data, err = json.Marshal(model.Event{
Repo: *repo, Repo: *repo,
Pipeline: *pipeline, Pipeline: *pipeline,
}) })
if err != nil {
return err
}
s.pubsub.Publish(message) s.pubsub.Publish(message)
return nil return nil
} }

View file

@ -74,17 +74,14 @@ func (s *WoodpeckerServer) Next(c context.Context, req *proto.NextRequest) (*pro
res := new(proto.NextResponse) res := new(proto.NextResponse)
pipeline, err := s.peer.Next(c, filter) pipeline, err := s.peer.Next(c, filter)
if err != nil { if err != nil || pipeline == nil {
return res, err
}
if pipeline == nil {
return res, err return res, err
} }
res.Workflow = new(proto.Workflow) res.Workflow = new(proto.Workflow)
res.Workflow.Id = pipeline.ID res.Workflow.Id = pipeline.ID
res.Workflow.Timeout = pipeline.Timeout res.Workflow.Timeout = pipeline.Timeout
res.Workflow.Payload, _ = json.Marshal(pipeline.Config) res.Workflow.Payload, err = json.Marshal(pipeline.Config)
return res, err return res, err
} }

View file

@ -42,11 +42,15 @@ func queuePipeline(repo *model.Repo, pipelineItems []*stepbuilder.Item) error {
task.RunOn = item.RunsOn task.RunOn = item.RunsOn
task.DepStatus = make(map[string]model.StatusValue) task.DepStatus = make(map[string]model.StatusValue)
task.Data, _ = json.Marshal(rpc.Workflow{ var err error
task.Data, err = json.Marshal(rpc.Workflow{
ID: fmt.Sprint(item.Workflow.ID), ID: fmt.Sprint(item.Workflow.ID),
Config: item.Config, Config: item.Config,
Timeout: repo.Timeout, Timeout: repo.Timeout,
}) })
if err != nil {
return err
}
tasks = append(tasks, task) tasks = append(tasks, task)
} }

View file

@ -18,6 +18,7 @@ import (
"encoding/json" "encoding/json"
"strconv" "strconv"
"github.com/rs/zerolog/log"
"go.woodpecker-ci.org/woodpecker/v2/server" "go.woodpecker-ci.org/woodpecker/v2/server"
"go.woodpecker-ci.org/woodpecker/v2/server/model" "go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/server/pubsub" "go.woodpecker-ci.org/woodpecker/v2/server/pubsub"
@ -33,9 +34,14 @@ func publishToTopic(pipeline *model.Pipeline, repo *model.Repo) {
} }
pipelineCopy := *pipeline pipelineCopy := *pipeline
message.Data, _ = json.Marshal(model.Event{ var err error
message.Data, err = json.Marshal(model.Event{
Repo: *repo, Repo: *repo,
Pipeline: pipelineCopy, Pipeline: pipelineCopy,
}) })
if err != nil {
log.Error().Err(err).Msg("can't marshal JSON")
return
}
server.Config.Services.Pubsub.Publish(message) server.Config.Services.Pubsub.Publish(message)
} }

View file

@ -52,7 +52,11 @@ func Config(c *gin.Context) {
// default func map with json parser. // default func map with json parser.
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"json": func(v any) string { "json": func(v any) string {
a, _ := json.Marshal(v) a, err := json.Marshal(v)
if err != nil {
log.Error().Err(err).Msgf("could not marshal JSON")
return ""
}
return string(a) return string(a)
}, },
} }