Compare commits

...

34 Commits

Author SHA1 Message Date
Kevin Franklin Kim
61d671341c
Merge pull request #140 from foomo/feature/pinterest
Some checks failed
Test Branch / test (push) Has been cancelled
feat: add pinterest provider
2025-09-10 16:19:41 +02:00
Kevin Franklin Kim
df04a14c25
chore: add docker 2025-09-10 16:13:51 +02:00
Kevin Franklin Kim
c3a1a68c9b
docs: update README 2025-09-10 16:03:09 +02:00
Kevin Franklin Kim
97ef3a7ce6
feat: add pinterest provider 2025-09-10 16:01:02 +02:00
Kevin Franklin Kim
35d4393f4e
chore: add permissions 2025-07-21 10:58:49 +02:00
Kevin Franklin Kim
fe98357ece
Merge pull request #134 from foomo/fix/meta-test-event-token
fix(facebook): allow empty meta test event token
2025-07-21 10:35:12 +02:00
Kevin Franklin Kim
3e6697a98d
chore: ignore var-naming 2025-07-21 10:31:27 +02:00
Kevin Franklin Kim
d5358405af
feat: update deps 2025-07-21 10:19:01 +02:00
Kevin Franklin Kim
27dff33e47
fix: allow optional test even token 2025-07-21 07:19:42 +02:00
Kevin Franklin Kim
acf13a73b1
Merge pull request #129 from foomo/feature/increase-timeouts
feat: increase timeouts
2025-06-11 13:36:40 +02:00
Kevin Franklin Kim
de67860229
feat: increase timeouts 2025-06-11 11:54:33 +02:00
Kevin Franklin Kim
f6539da3c0
Merge pull request #127 from foomo/feature/sesamy-go-0.11.2
feat: sesamy-go 0.11.2
2025-06-06 15:36:19 +02:00
Kevin Franklin Kim
53794ef0f8
feat: sesamy-go 0.11.2 2025-06-06 15:16:01 +02:00
Kevin Franklin Kim
a021b3eafd
Merge pull request #125 from foomo/dependabot/go_modules/gomod-update-0417d39fac
chore(deps): bump google.golang.org/api from 0.234.0 to 0.235.0 in the gomod-update group
2025-06-05 12:15:58 +02:00
Kevin Franklin Kim
e2ce7e64fe
Merge pull request #126 from foomo/feature/open-diff
feat: add open & diff command
2025-06-05 12:15:37 +02:00
Kevin Franklin Kim
539624ceb3
feat: update schema 2025-06-05 12:13:26 +02:00
Kevin Franklin Kim
740d4981ea
fix: lint issues 2025-06-05 12:09:01 +02:00
Kevin Franklin Kim
5fdd26d867
feat: add open & diff command 2025-06-05 11:53:37 +02:00
dependabot[bot]
b1b71cc166
chore(deps): bump google.golang.org/api in the gomod-update group
Bumps the gomod-update group with 1 update: [google.golang.org/api](https://github.com/googleapis/google-api-go-client).


Updates `google.golang.org/api` from 0.234.0 to 0.235.0
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.234.0...v0.235.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-version: 0.235.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gomod-update
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 08:54:56 +00:00
Kevin Franklin Kim
c6f5292df3
Merge pull request #124 from foomo/feature/conversion-linker-accept-params
feat(conversionlinker): add accept params
2025-05-27 10:58:49 +02:00
Kevin Franklin Kim
9df36edda0
docs(conversionlinker): update docs 2025-05-27 10:49:41 +02:00
Kevin Franklin Kim
3e03bc1226
feat(conversionlinker): add enable linker params 2025-05-27 10:48:11 +02:00
Kevin Franklin Kim
1cfbaecfee
Merge pull request #123 from foomo/feature/mixpanel-event-types
feat(mixpanel): add event types
2025-05-26 23:00:43 +02:00
Kevin Franklin Kim
937d05dd7e
test(mixpanel): update 2025-05-26 22:55:22 +02:00
Kevin Franklin Kim
8b13d71d7a
chore: update ownbrew 2025-05-26 22:55:06 +02:00
Kevin Franklin Kim
2561217100
feat(mixpanel): add events 2025-05-26 22:45:44 +02:00
Kevin Franklin Kim
48c093b015
Merge pull request #121 from foomo/feature/multiple-gads-conversions
feat(googleads): support multiple conversions
2025-05-23 15:12:21 +02:00
Kevin Franklin Kim
203ad0bfa4
Merge pull request #118 from foomo/dependabot/go_modules/gomod-security-bbb8b02913
chore(deps): bump golang.org/x/net from 0.37.0 to 0.38.0 in the gomod-security group
2025-05-23 13:23:12 +02:00
Kevin Franklin Kim
be65ea9157
feat(googleads): support multiple conversions 2025-05-23 13:22:28 +02:00
dependabot[bot]
7fcd2912a6
chore(deps): bump golang.org/x/net in the gomod-security group
Bumps the gomod-security group with 1 update: [golang.org/x/net](https://github.com/golang/net).


Updates `golang.org/x/net` from 0.37.0 to 0.38.0
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
  dependency-group: gomod-security
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-23 11:06:46 +00:00
Kevin Franklin Kim
ab5bb5d07f
Merge pull request #120 from foomo/feature/mixpanel
feat: mixpanel
2025-05-23 13:04:49 +02:00
Kevin Franklin Kim
15fc18c62c
feat: add mixpanel provider 2025-05-23 12:35:19 +02:00
Kevin Franklin Kim
e3fa04dd0a
chore: bump action 2025-05-23 12:33:30 +02:00
Kevin Franklin Kim
487358ee89
feat: go 1.24.3 2025-05-23 12:32:51 +02:00
65 changed files with 2467 additions and 301 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
sesamy
dist

View File

@ -22,15 +22,22 @@ jobs:
check-latest: true
go-version-file: go.mod
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- id: app_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- name: Login to docker.io
run: docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
- uses: goreleaser/goreleaser-action@v6
with:
version: latest
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ steps.app_token.outputs.token }}

View File

@ -7,6 +7,10 @@ on:
merge_group:
workflow_dispatch:
permissions:
contents: read
pull-requests: write
concurrency:
group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
cancel-in-progress: true
@ -26,7 +30,7 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
- uses: golangci/golangci-lint-action@v7
- uses: golangci/golangci-lint-action@v8
with:
version: latest

8
.gitignore vendored
View File

@ -16,10 +16,18 @@
## Editorconfig
!.editorconfig
## Docker
!.dockerignore
## Husky
!.husky/
!.husky.yaml
## Golang
go.work
go.work.sum
!.golangci.yml
!.goreleaser.yml
## Ownbrew
!.ownbrew.yaml

View File

@ -1,8 +1,7 @@
version: "2"
run:
go: 1.24.1
build-tags:
- safe
go: 1.24.3
build-tags: [safe]
modules-download-mode: readonly
linters:
default: none
@ -182,6 +181,8 @@ linters:
disabled: true
- name: indent-error-flow
disabled: true
- name: var-naming
disabled: true
testifylint:
disable:
- float-compare

View File

@ -41,3 +41,74 @@ brews:
description: "CLI utitlity to manage Server Side Tag Management"
test: |
system "#{bin}/sesamy --version"
dockers:
- use: buildx
goos: linux
goarch: amd64
dockerfile: Dockerfile
image_templates:
- '{{ if eq .Prerelease "" }}foomo/sesamy:latest-amd64{{ end }}'
- 'foomo/sesamy:{{ .Version }}-amd64'
- '{{ if eq .Prerelease "" }}foomo/sesamy:{{ .Major }}-amd64{{ end }}'
- '{{ if eq .Prerelease "" }}foomo/sesamy:{{ .Major }}.{{ .Minor }}-amd64{{ end }}'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=CLI utility manage infrastructure as code with helm'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation={{.GitURL}}'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/amd64'
- use: buildx
goos: linux
goarch: arm64
dockerfile: Dockerfile
image_templates:
- '{{ if eq .Prerelease "" }}foomo/sesamy:latest-arm64{{ end }}'
- 'foomo/sesamy:{{ .Version }}-arm64'
- '{{ if eq .Prerelease "" }}foomo/sesamy:{{ .Major }}-arm64{{ end }}'
- '{{ if eq .Prerelease "" }}foomo/sesamy:{{ .Major }}.{{ .Minor }}-arm64{{ end }}'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=CLI utility manage infrastructure as code with helm'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation={{.GitURL}}'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/arm64'
docker_manifests:
# basic
- name_template: 'foomo/sesamy:latest'
image_templates:
- 'foomo/sesamy:latest-amd64'
- 'foomo/sesamy:latest-arm64'
skip_push: auto
- name_template: 'foomo/sesamy:{{ .Version }}'
image_templates:
- 'foomo/sesamy:{{ .Version }}-amd64'
- 'foomo/sesamy:{{ .Version }}-arm64'
skip_push: auto
- name_template: 'foomo/sesamy:{{ .Major }}'
image_templates:
- 'foomo/sesamy:{{ .Major }}-amd64'
- 'foomo/sesamy:{{ .Major }}-arm64'
skip_push: auto
- name_template: 'foomo/sesamy:{{ .Major }}.{{ .Minor }}'
image_templates:
- 'foomo/sesamy:{{ .Major }}.{{ .Minor }}-amd64'
- 'foomo/sesamy:{{ .Major }}.{{ .Minor }}-arm64'
skip_push: auto

View File

@ -1,5 +1,5 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/foomo/ownbrew/refs/tags/v0.1.0/ownbrew.schema.json
version: '1.0'
# yaml-language-server: $schema=https://raw.githubusercontent.com/foomo/ownbrew/refs/tags/v0.2.3/ownbrew.schema.json
version: '1.1'
binDir: "bin"
tapDir: ".ownbrew/tap"
@ -9,7 +9,7 @@ packages:
## https://github.com/golangci/golangci-lint/releases
- name: golangci-lint
tap: foomo/tap/golangci/golangci-lint
version: 1.61.0
version: 2.1.6
## https://github.com/go-courier/husky/releases
- name: husky
tap: foomo/tap/go-courier/husky

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM alpine:latest
RUN apk --no-cache add ca-certificates
RUN adduser -D -u 1001 -g 1001 sesamy
COPY sesamy /usr/bin/
USER sesamy
WORKDIR /home/sesamy
ENTRYPOINT ["sesamy"]

View File

@ -1,5 +1,6 @@
.DEFAULT_GOAL := help
-include .makerc
PATH:=bin:$(PATH)
# --- Targets -----------------------------------------------------------------
@ -16,12 +17,7 @@
fi
@git config core.hooksPath .husky
## === Tasks ===
.PHONY: brew
## Install project binaries
brew:
@ownbrew install
### Tasks
.PHONY: doc
## Open go docs
@ -58,11 +54,13 @@ outdated:
## Build binary
build:
@mkdir -p bin
@echo "building: bin/sesamy"
@go build -tags=safe -o bin/sesamy main.go
.PHONY: install
## Install binary
install:
@echo "installing: ${GOPATH}/bin/sesamy"
@go build -tags=safe -o ${GOPATH}/bin/sesamy main.go
.PHONY: install.debug
@ -70,36 +68,27 @@ install:
install.debug:
@go build -tags=safe -gclags="all=-N -l" -o ${GOPATH}/bin/sesamy main.go
## === Utils ===
### Utils
.PHONY: brew
## Install project binaries
brew:
@ownbrew install
.PHONY: help
## Show help text
help:
@echo "\033[1;36mSesamy CLI\033[0m"
@awk '{ \
if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \
helpCommand = substr($$0, index($$0, ":") + 2); \
if (helpMessage) { \
printf "\033[36m%-23s\033[0m %s\n", \
helpCommand, helpMessage; \
helpMessage = ""; \
} \
} else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \
helpCommand = substr($$0, 0, index($$0, ":")); \
if (helpMessage) { \
printf "\033[36m%-23s\033[0m %s\n", \
helpCommand, helpMessage"\n"; \
helpMessage = ""; \
} \
} else if ($$0 ~ /^##/) { \
if (helpMessage) { \
helpMessage = helpMessage"\n "substr($$0, 3); \
} else { \
helpMessage = substr($$0, 3); \
} \
} else { \
if (helpMessage) { \
print "\n "helpMessage"\n" \
} \
helpMessage = ""; \
} \
}' \
$(MAKEFILE_LIST)
if($$0 ~ /^### /){ \
if(help) printf "\033[36m%-23s\033[0m %s\n\n", cmd, help; help=""; \
printf "\n\033[1;36m%s\033[0m\n", substr($$0,5); \
} else if($$0 ~ /^[a-zA-Z0-9._-]+:/){ \
cmd = substr($$0, 1, index($$0, ":")-1); \
if(help) printf " \033[36m%-23s\033[0m %s\n", cmd, help; help=""; \
} else if($$0 ~ /^##/){ \
help = help ? help "\n " substr($$0,3) : substr($$0,3); \
} else if(help){ \
print "\n " help "\n"; help=""; \
} \
}' $(MAKEFILE_LIST)

View File

@ -53,7 +53,7 @@ Add a `sesamy.yaml` configuration
```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/foomo/sesamy-cli/refs/heads/main/sesamy.schema.json
version: '1.0'
version: '1.1'
# Whether to redact the visitor ip
redactVisitorIp: true
@ -249,12 +249,20 @@ googleAds:
conversion:
# Enable Google Ads Conversion
enabled: true
# Google Ads Conversion Tracking Label
conversionLabel: ''
# Google Tag Manager server container settings
serverContainer:
# Path to the go.mod file
directory: .
# Conversion settings map
settings:
add_to_cart:
- label: ''
- conversionId: ''
label: ''
purchase:
- label: ''
- conversionId: ''
label: ''
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -272,6 +280,8 @@ conversionLinker:
enabled: true
# Consent mode name
mode: ad_storage
# Accept incoming linker parameters
enableLinkerParams: true
# --- Umami settings
umami:
@ -447,6 +457,73 @@ cookiebot:
adUserData: denied
# Default consent ad_personalization
adPersonalization: denied
# --- Mixpanel
mixpanel:
# Enable provider
enabled: true
# Project Token
projectToken: ''
# Google Consent settings
googleConsent:
# Enable consent mode
enabled: true
# Consent mode name
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Track events
track:
# Directory containing the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: 'github.com/foomo/sesamy-go/pkg/event'
types:
- AddPaymentInfo
- AddShippingInfo
- AddToCart
- BeginCheckout
- PageView
- Purchase
- RemoveFromCart
- Search
- SelectItem
- ViewCart
- ViewItem
- ViewItemList
# --- Pinterest
pinterest:
# Enable provider
enabled: true
# Pinterest advertiser id
advertiserId: ''
# Pinterest API access token
apiAccessToken: ''
# Enable test mode
testModeEnabled: false
# Google Consent settings
googleConsent:
# Enable consent mode
enabled: true
# Consent mode name
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Directory containing the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: 'github.com/foomo/sesamy-go/pkg/event'
types:
- AddToCart
- GenerateLead
- PageView
- Purchase
- Search
- SignUp
- ViewItemList
```
## Caveats

23
cmd/diff.go Normal file
View File

@ -0,0 +1,23 @@
package cmd
import (
"log/slog"
"github.com/foomo/sesamy-cli/cmd/diff"
"github.com/spf13/cobra"
)
// NewDiff represents the diff command
func NewDiff(l *slog.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "diff",
Short: "Print Google Tag Manager container status diff",
}
cmd.AddCommand(
diff.NewWeb(l),
diff.NewServer(l),
)
return cmd
}

400
cmd/diff/diff.go Normal file
View File

@ -0,0 +1,400 @@
package diff
import (
"bytes"
"context"
"log/slog"
"strings"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/itchyny/json2yaml"
"github.com/sters/yaml-diff/yamldiff"
)
const ChangeStatusDeleted = "deleted"
func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (string, error) {
l.Info("└ ⬇︎ Loading status")
s, err := tm.Service().Accounts.Containers.Workspaces.GetStatus(tm.WorkspacePath()).Context(ctx).Do()
if err != nil {
return "", err
} else if len(s.WorkspaceChange) == 0 {
return "", nil
}
l.Info("└ ⬇︎ Loading live version")
live, err := tm.Service().Accounts.Containers.Versions.Live(tm.ContainerPath()).Do()
if err != nil {
return "", err
}
var res []string
for _, entity := range s.WorkspaceChange {
switch {
case entity.Tag != nil:
res = append(res, " # Tag: "+entity.Tag.Name+" ("+entity.ChangeStatus+")\n")
// unset props
entity.Tag.Path = ""
entity.Tag.Fingerprint = ""
entity.Tag.WorkspaceId = ""
entity.Tag.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.Tag)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.Tag {
if value.Name == entity.Tag.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.Folder != nil:
res = append(res, " # Folder: "+entity.Folder.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.Folder.Path = ""
entity.Folder.Fingerprint = ""
entity.Folder.WorkspaceId = ""
entity.Folder.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.Folder)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.Folder {
if value.Name == entity.Folder.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.Trigger != nil:
res = append(res, " # Trigger: "+entity.Trigger.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.Trigger.Path = ""
entity.Trigger.Fingerprint = ""
entity.Trigger.WorkspaceId = ""
entity.Trigger.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.Trigger)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.Trigger {
if value.Name == entity.Trigger.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.Variable != nil:
res = append(res, " # Variable: "+entity.Variable.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.Variable.Path = ""
entity.Variable.Fingerprint = ""
entity.Variable.WorkspaceId = ""
entity.Variable.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.Variable)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.Variable {
if value.Name == entity.Variable.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.Client != nil:
res = append(res, " # Client: "+entity.Client.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.Client.Path = ""
entity.Client.Fingerprint = ""
entity.Client.WorkspaceId = ""
entity.Client.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.Client)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.Client {
if value.Name == entity.Client.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.GtagConfig != nil:
res = append(res, " # GtagConfig: "+entity.GtagConfig.AccountId+" ("+entity.ChangeStatus+")")
// unset props
entity.GtagConfig.Path = ""
entity.GtagConfig.Fingerprint = ""
entity.GtagConfig.WorkspaceId = ""
entity.GtagConfig.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.GtagConfig)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.GtagConfig {
if value.AccountId == entity.GtagConfig.AccountId {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.BuiltInVariable != nil:
res = append(res, " # BuiltInVariable: "+entity.BuiltInVariable.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.BuiltInVariable.Path = ""
entity.BuiltInVariable.WorkspaceId = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.BuiltInVariable)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.BuiltInVariable {
if value.Name == entity.BuiltInVariable.Name {
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.CustomTemplate != nil:
res = append(res, " # CustomTemplate: "+entity.CustomTemplate.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.CustomTemplate.Path = ""
entity.CustomTemplate.Fingerprint = ""
entity.CustomTemplate.WorkspaceId = ""
entity.CustomTemplate.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.CustomTemplate)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.CustomTemplate {
if value.Name == entity.CustomTemplate.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
case entity.Transformation != nil:
res = append(res, " # Transformation: "+entity.Transformation.Name+" ("+entity.ChangeStatus+")")
// unset props
entity.Transformation.Path = ""
entity.Transformation.Fingerprint = ""
entity.Transformation.WorkspaceId = ""
entity.Transformation.TagManagerUrl = ""
var changed string
if entity.ChangeStatus != ChangeStatusDeleted {
changed, err = ToYalm(entity.Transformation)
if err != nil {
return "", err
}
}
var original string
for _, value := range live.Transformation {
if value.Name == entity.Transformation.Name {
// unset props
value.Fingerprint = ""
original, err = ToYalm(value)
if err != nil {
return "", err
}
break
}
}
d, err := ToDiff(original, changed)
if err != nil {
return "", err
}
res = append(res, d...)
default:
l.Warn("unknown entity type", "entity", entity)
}
}
return strings.Join(res, " ---\n"), nil
}
type Marshelable interface {
MarshalJSON() ([]byte, error)
}
func ToDiff(original, changed string) ([]string, error) {
yamls1, err := yamldiff.Load(original)
if err != nil {
return nil, err
}
yamls2, err := yamldiff.Load(changed)
if err != nil {
return nil, err
}
var ret []string
for _, d := range yamldiff.Do(yamls1, yamls2) {
if value := d.Dump(); len(value) > 4 {
ret = append(ret, value)
}
}
return ret, nil
}
func ToYalm(m Marshelable) (string, error) {
if m == nil {
return "", nil
}
out, err := m.MarshalJSON()
if err != nil {
return "", err
}
var ret bytes.Buffer
if err := json2yaml.Convert(&ret, bytes.NewBuffer(out)); err != nil {
return "", err
}
return ret.String(), nil
}

68
cmd/diff/server.go Normal file
View File

@ -0,0 +1,68 @@
package diff
import (
"fmt"
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// NewServer represents the server command
func NewServer(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "server",
Short: "Print Google Tag Manager Server Container status diff",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
l.Info("☕ Retrieving Server Container status")
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
tm, err := tagmanager.New(
cmd.Context(),
l,
cfg.GoogleTagManager.AccountID,
cfg.GoogleTagManager.ServerContainer,
tagmanager.WithRequestQuota(cfg.GoogleAPI.RequestQuota),
tagmanager.WithClientOptions(cfg.GoogleAPI.GetClientOption()),
)
if err != nil {
return err
}
if err := tm.EnsureWorkspaceID(cmd.Context()); err != nil {
return err
}
out, err := diff(cmd.Context(), l, tm)
if err != nil {
return err
}
if !c.GetBool("raw") {
out = utils.Highlight(out)
}
_, err = fmt.Println(out)
return err
},
}
flags := cmd.Flags()
flags.Bool("raw", false, "print raw output")
_ = c.BindPFlag("raw", flags.Lookup("raw"))
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))
return cmd
}

68
cmd/diff/web.go Normal file
View File

@ -0,0 +1,68 @@
package diff
import (
"fmt"
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// NewWeb represents the web command
func NewWeb(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "web",
Short: "Print Google Tag Manager Web Container status diff",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
l.Info("☕ Retrieving Web Container status")
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
tm, err := tagmanager.New(
cmd.Context(),
l,
cfg.GoogleTagManager.AccountID,
cfg.GoogleTagManager.WebContainer,
tagmanager.WithRequestQuota(cfg.GoogleAPI.RequestQuota),
tagmanager.WithClientOptions(cfg.GoogleAPI.GetClientOption()),
)
if err != nil {
return err
}
if err := tm.EnsureWorkspaceID(cmd.Context()); err != nil {
return err
}
out, err := diff(cmd.Context(), l, tm)
if err != nil {
return err
}
if !c.GetBool("raw") {
out = utils.Highlight(out)
}
_, err = fmt.Println(out)
return err
},
}
flags := cmd.Flags()
flags.Bool("raw", false, "print raw output")
_ = c.BindPFlag("raw", flags.Lookup("raw"))
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))
return cmd
}

View File

@ -5,42 +5,34 @@ import (
"context"
"fmt"
"log/slog"
"os"
"strings"
"github.com/alecthomas/chroma/quick"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/itchyny/json2yaml"
)
func dump(i interface{ MarshalJSON() ([]byte, error) }, err error) error {
func dump(i interface{ MarshalJSON() ([]byte, error) }, err error) (string, error) {
if err != nil {
return err
return "", err
}
out, err := i.MarshalJSON()
if err != nil {
return err
return "", err
}
// if err := json.Indent(ret, out, "", " "); err != nil {
// return err
// }
// fmt.Println(ret.String())
var output strings.Builder
if err := json2yaml.Convert(&output, bytes.NewBuffer(out)); err != nil {
return err
return "", err
}
// fmt.Print(output.String())
return quick.Highlight(os.Stdout, output.String(), "yaml", "terminal", "monokai")
return output.String(), nil
}
func list(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, resource string) error {
func list(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, resource string) (string, error) {
switch resource {
case "environments":
return dump(tm.Service().Accounts.Containers.Environments.List(tm.ContainerPath()).Context(ctx).Do())
case "workspaces":
return dump(tm.Service().Accounts.Containers.Workspaces.List(tm.ContainerPath()).Context(ctx).Do())
case "status":
return dump(tm.Service().Accounts.Containers.Workspaces.GetStatus(tm.WorkspacePath()).Context(ctx).Do())
case "clients":
return dump(tm.Service().Accounts.Containers.Workspaces.Clients.List(tm.WorkspacePath()).Context(ctx).Do())
case "tags":
@ -56,13 +48,14 @@ func list(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, resour
case "templates-data":
r, err := tm.Service().Accounts.Containers.Workspaces.Templates.List(tm.WorkspacePath()).Context(ctx).Do()
if err != nil {
return err
return "", err
}
var ret strings.Builder
for _, template := range r.Template {
l.Info("---- Template data: " + template.Name + " ----------------------")
fmt.Println(template.TemplateData)
ret.WriteString("---- Template data: " + template.Name + " ----------------------\n")
ret.WriteString(template.TemplateData + "\n")
}
return nil
return ret.String(), nil
case "gtag-config":
return dump(tm.Service().Accounts.Containers.Workspaces.GtagConfig.List(tm.WorkspacePath()).Context(ctx).Do())
case "triggers":
@ -72,6 +65,6 @@ func list(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, resour
case "zones":
return dump(tm.Service().Accounts.Containers.Workspaces.Zones.List(tm.WorkspacePath()).Context(ctx).Do())
default:
return fmt.Errorf("unknown resource %s", resource)
return "", fmt.Errorf("unknown resource %s", resource)
}
}

View File

@ -1,10 +1,12 @@
package list
import (
"fmt"
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@ -58,12 +60,24 @@ func NewServer(l *slog.Logger) *cobra.Command {
return err
}
return list(cmd.Context(), l, tm, resource)
out, err := list(cmd.Context(), l, tm, resource)
if err != nil {
return err
}
if !c.GetBool("raw") {
out = utils.Highlight(out)
}
_, err = fmt.Println(out)
return err
},
}
flags := cmd.Flags()
flags.Bool("raw", false, "print raw output")
_ = c.BindPFlag("raw", flags.Lookup("raw"))
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))

View File

@ -1,10 +1,12 @@
package list
import (
"fmt"
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@ -57,12 +59,24 @@ func NewWeb(l *slog.Logger) *cobra.Command {
return err
}
return list(cmd.Context(), l, tm, resource)
out, err := list(cmd.Context(), l, tm, resource)
if err != nil {
return err
}
if !c.GetBool("raw") {
out = utils.Highlight(out)
}
_, err = fmt.Println(out)
return err
},
}
flags := cmd.Flags()
flags.Bool("raw", false, "print raw output")
_ = c.BindPFlag("raw", flags.Lookup("raw"))
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))

71
cmd/open.go Normal file
View File

@ -0,0 +1,71 @@
package cmd
import (
"fmt"
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// NewOpen represents the open command
func NewOpen(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "open",
Short: "Open links in the browser",
Args: cobra.OnlyValidArgs,
ValidArgs: []cobra.Completion{
"ga",
"gtm-web",
"gtm-server",
},
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
var url string
switch args[0] {
case "ga":
if cfg.GoogleAnalytics.PropertyID == "" {
return errors.New("missing Google Analytics Property ID")
}
url = fmt.Sprintf(
"https://analytics.google.com/analytics/web/#/p%s/",
cfg.GoogleAnalytics.PropertyID,
)
case "gtm-web":
url = fmt.Sprintf(
"https://tagmanager.google.com/#/container/accounts/%s/containers/%s/",
cfg.GoogleTagManager.AccountID,
cfg.GoogleTagManager.WebContainer.ContainerID,
)
case "gtm-server":
url = fmt.Sprintf(
"https://tagmanager.google.com/#/container/accounts/%s/containers/%s/",
cfg.GoogleTagManager.AccountID,
cfg.GoogleTagManager.ServerContainer.ContainerID,
)
default:
return fmt.Errorf("invalid container type: %s", args[0])
}
l.Info("↗ Navigating to Google Tag Manager Container: " + url)
return browser.OpenURL(url)
},
}
flags := cmd.Flags()
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))
return cmd
}

View File

@ -13,6 +13,8 @@ import (
googletagprovider "github.com/foomo/sesamy-cli/pkg/provider/googletag"
googletagmanagerprovider "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
microsoftadsprovider "github.com/foomo/sesamy-cli/pkg/provider/microsoftads"
mixpanelprovider "github.com/foomo/sesamy-cli/pkg/provider/mixpanel"
pinterestprovider "github.com/foomo/sesamy-cli/pkg/provider/pinterest"
tracifyprovider "github.com/foomo/sesamy-cli/pkg/provider/tracify"
umamiprovider "github.com/foomo/sesamy-cli/pkg/provider/umami"
ptermx "github.com/foomo/sesamy-cli/pkg/pterm"
@ -136,6 +138,20 @@ func NewServer(l *slog.Logger) *cobra.Command {
}
}
if cfg.Mixpanel.Enabled && utils.Tag(mixpanelprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", mixpanelprovider.Name, "tag", mixpanelprovider.Tag)
if err := mixpanelprovider.Server(cmd.Context(), l, tm, cfg.Mixpanel); err != nil {
return errors.Wrap(err, "failed to provision mixpanel")
}
}
if cfg.Pinterest.Enabled && utils.Tag(pinterestprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", pinterestprovider.Name, "tag", pinterestprovider.Tag)
if err := pinterestprovider.Server(cmd.Context(), l, tm, cfg.Pinterest); err != nil {
return errors.Wrap(err, "failed to provision mixpanel")
}
}
if missed := tm.Missed(); len(tags) == 0 && len(missed) > 0 {
tree := pterm.TreeNode{
Text: "♻️ Missed resources (potentially garbage)",

View File

@ -2,6 +2,8 @@ package cmd
import (
"log/slog"
"maps"
"slices"
"github.com/foomo/sesamy-cli/pkg/provider/conversionlinker"
"github.com/foomo/sesamy-cli/pkg/provider/cookiebot"
@ -15,6 +17,8 @@ import (
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
"github.com/foomo/sesamy-cli/pkg/provider/hotjar"
"github.com/foomo/sesamy-cli/pkg/provider/microsoftads"
"github.com/foomo/sesamy-cli/pkg/provider/mixpanel"
"github.com/foomo/sesamy-cli/pkg/provider/pinterest"
"github.com/foomo/sesamy-cli/pkg/provider/tracify"
"github.com/foomo/sesamy-cli/pkg/provider/umami"
"github.com/pterm/pterm"
@ -27,25 +31,29 @@ func NewTags(l *slog.Logger) *cobra.Command {
Use: "tags",
Short: "Print out all available tags",
RunE: func(cmd *cobra.Command, args []string) error {
// Define the data for the first table
data := pterm.TableData{
{"Name", "Tag"},
{conversionlinker.Name, conversionlinker.Tag},
{cookiebot.Name, cookiebot.Tag},
{criteo.Name, criteo.Tag},
{emarsys.Name, emarsys.Tag},
{facebook.Name, facebook.Tag},
{googleads.Name, googleads.Tag},
{googleanalytics.Name, googleanalytics.Tag},
{googletag.Name, googletag.Tag},
{googleconsent.Name, googleconsent.Tag},
{googletagmanager.Name, googletagmanager.Tag},
{hotjar.Name, hotjar.Tag},
{microsoftads.Name, microsoftads.Tag},
{tracify.Name, tracify.Tag},
{umami.Name, umami.Tag},
tags := map[string]string{
conversionlinker.Name: conversionlinker.Tag,
cookiebot.Name: cookiebot.Tag,
criteo.Name: criteo.Tag,
emarsys.Name: emarsys.Tag,
facebook.Name: facebook.Tag,
googleads.Name: googleads.Tag,
googleanalytics.Name: googleanalytics.Tag,
googletag.Name: googletag.Tag,
googleconsent.Name: googleconsent.Tag,
googletagmanager.Name: googletagmanager.Tag,
hotjar.Name: hotjar.Tag,
microsoftads.Name: microsoftads.Tag,
mixpanel.Name: mixpanel.Tag,
tracify.Name: tracify.Tag,
umami.Name: umami.Tag,
pinterest.Name: pinterest.Tag,
}
// Define the data for the first table
data := pterm.TableData{{"Name", "Tag"}}
for _, name := range slices.Sorted(maps.Keys(tags)) {
data = append(data, []string{name, tags[name]})
}
return pterm.DefaultTable.WithHasHeader().WithData(data).Render()
},
}

93
go.mod
View File

@ -1,6 +1,6 @@
module github.com/foomo/sesamy-cli
go 1.24.1
go 1.25.1
require (
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4
@ -8,85 +8,88 @@ require (
github.com/fatih/structtag v1.2.0
github.com/foomo/go v0.0.3
github.com/foomo/gocontemplate v0.2.0
github.com/foomo/sesamy-go v0.9.0
github.com/foomo/sesamy-go v0.11.2
github.com/invopop/jsonschema v0.13.0
github.com/itchyny/json2yaml v0.1.4
github.com/joho/godotenv v1.5.1
github.com/knadh/koanf/parsers/yaml v0.1.0
github.com/knadh/koanf/providers/file v1.1.2
github.com/knadh/koanf/providers/rawbytes v0.1.0
github.com/knadh/koanf/v2 v2.1.2
github.com/knadh/koanf/parsers/yaml v1.1.0
github.com/knadh/koanf/providers/file v1.2.0
github.com/knadh/koanf/providers/rawbytes v1.0.0
github.com/knadh/koanf/v2 v2.2.2
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.80
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.20.1
github.com/stoewer/go-strcase v1.3.0
github.com/stretchr/testify v1.10.0
github.com/wissance/stringFormatter v1.4.1
google.golang.org/api v0.228.0
github.com/pterm/pterm v0.12.81
github.com/spf13/cobra v1.10.1
github.com/spf13/viper v1.21.0
github.com/sters/yaml-diff v1.4.1
github.com/stoewer/go-strcase v1.3.1
github.com/stretchr/testify v1.11.1
github.com/wissance/stringFormatter v1.5.0
google.golang.org/api v0.249.0
)
require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cloud.google.com/go/auth v0.15.0 // indirect
cloud.google.com/go/auth v0.16.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/compute/metadata v0.8.0 // indirect
github.com/Code-Hex/go-wordwrap v1.0.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/console v1.0.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/foomo/gostandards v0.2.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-yaml v1.15.6 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/gookit/color v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.28.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.31.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc v1.71.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/tools v0.36.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

202
go.sum
View File

@ -6,12 +6,12 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4 h1:y80Hd9hmB+rsEH/p4c5ti5PbO0PhBmxw4NgbpFZvoHg=
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4/go.mod h1:6k40Pwrc2FazLf1BUbmAC36E9LvT+DErjZr30isbXhg=
github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=
@ -33,8 +33,8 @@ github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xW
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -53,19 +53,21 @@ github.com/foomo/gocontemplate v0.2.0 h1:YbgNUwZ9mK9Wj05cGVTPamCpMuxlaL4qegkbZv2
github.com/foomo/gocontemplate v0.2.0/go.mod h1:VOYIvxPPAT7HxrderRexS0EttcT8pyfSRkCMvS8rtxA=
github.com/foomo/gostandards v0.2.0 h1:Ryd7TI9yV3Xk5B84DcUDB7KcL3LzQ8NS+TVOrFxTYfA=
github.com/foomo/gostandards v0.2.0/go.mod h1:XQx7Ur6vyvxaIe2cQvAthuhPYDe+d2soibqVcXDXOh4=
github.com/foomo/sesamy-go v0.9.0 h1:pydJkKsAfvGk112HSL9hQj2T+gfsCz7qBJTEVXTM+7c=
github.com/foomo/sesamy-go v0.9.0/go.mod h1:MwbT1lskUrzZH/6LwxZc+slovgMXLLp8LlSK9pZf0HI=
github.com/foomo/sesamy-go v0.11.2 h1:P405IMMDzW/4hMKbAbvCVBaFjOARb26/PdG1wTBAxBM=
github.com/foomo/sesamy-go v0.11.2/go.mod h1:yORAeN0fDaZ0irXx4QdMp61wuDBAeEBHD0bTN4JjKxA=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/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-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-yaml v1.15.6 h1:gy5kf1yjMia3/c3wWD+u1z3lU5XlhpT8FZGaLJU9cOA=
github.com/goccy/go-yaml v1.15.6/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -77,12 +79,14 @@ 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/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
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.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
@ -96,16 +100,16 @@ github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=
github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v1.1.0 h1:3ltfm9ljprAHt4jxgeYLlFPmUaunuCgu1yILuTXRdM4=
github.com/knadh/koanf/parsers/yaml v1.1.0/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg=
github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
github.com/knadh/koanf/providers/rawbytes v1.0.0 h1:MrKDh/HksJlKJmaZjgs4r8aVBb/zsJyc/8qaSnzcdNI=
github.com/knadh/koanf/providers/rawbytes v1.0.0/go.mod h1:KxwYJf1uezTKy6PBtfE+m725NGp4GPVA7XoNTJ/PtLo=
github.com/knadh/koanf/v2 v2.2.2 h1:ghbduIkpFui3L587wavneC9e3WIliCgiCgdxYO/wd7A=
github.com/knadh/koanf/v2 v2.2.2/go.mod h1:abWQc0cBXLSF/PSOMCB/SK+T13NXDsPvOksbpi5e/9Q=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -127,8 +131,10 @@ github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmL
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -141,32 +147,35 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA=
github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/sters/yaml-diff v1.4.1 h1:0W3jnFKCu8/DV7nh2aXSDA2VVfxfHu2+qdh81CuFmZo=
github.com/sters/yaml-diff v1.4.1/go.mod h1:K286Xp2z+aGkok7z9k3zXcq0ZsrDaDp7/wyGwFjM9Y8=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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=
@ -176,12 +185,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/wissance/stringFormatter v1.4.1 h1:E/xiKQz5ZmLPwnUc89u9ez6LW+SabJQNUFbR00CBbbU=
github.com/wissance/stringFormatter v1.4.1/go.mod h1:H7Mz15+5i8ypmv6bLknM/uD+U1teUW99PlW0DNCNscA=
github.com/wissance/stringFormatter v1.5.0 h1:hiGpYEJS3B3H/6M03HecqW8nDO0OXgQ75REVTXp6reM=
github.com/wissance/stringFormatter v1.5.0/go.mod h1:H7Mz15+5i8ypmv6bLknM/uD+U1teUW99PlW0DNCNscA=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
@ -190,43 +199,43 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -238,42 +247,45 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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=
google.golang.org/api v0.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs=
google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w=
google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@ -17,9 +17,10 @@ func main() {
root := cmd.NewRoot(l)
root.AddCommand(
cmd.NewConfig(l),
cmd.NewConfig(l),
cmd.NewList(l),
cmd.NewDiff(l),
cmd.NewOpen(l),
cmd.NewProvision(l),
cmd.NewTags(l),
cmd.NewTypeScript(l),

View File

@ -26,6 +26,10 @@ type Config struct {
Facebook Facebook `json:"facebook" yaml:"facebook"`
// MicrosoftAds provider settings
MicrosoftAds MicrosoftAds `json:"microsoftAds" yaml:"microsoftAds"`
// Mixpanel provider settings
Mixpanel Mixpanel `json:"mixpanel" yaml:"mixpanel"`
// Pinterest provider settings
Pinterest Pinterest `json:"pinterest" yaml:"pinterest"`
// Emarsys provider settings
Emarsys Emarsys `json:"emarsys" yaml:"emarsys"`
// Hotjar provider settings

View File

@ -5,4 +5,6 @@ type ConversionLinker struct {
Enabled bool `json:"enabled" yaml:"enabled"`
// Google Consent settings
GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"`
// Accept incoming linker parameters
EnableLinkerParams bool `json:"enableLinkerParams" yaml:"enableLinkerParams"`
}

View File

@ -13,13 +13,14 @@ type (
}
GoogleAdsConversionServerContainer struct {
contemplate.Config `json:",inline" yaml:",squash"`
Settings map[string]GoogleAdsConversionTracking `json:"settings" yaml:"settings"`
// Conversion settings map
Settings map[string][]GoogleAdsConversionTracking `json:"settings" yaml:"settings"`
}
)
func (s *GoogleAdsConversionServerContainer) Setting(eventName string) GoogleAdsConversionTracking {
func (s *GoogleAdsConversionServerContainer) Setting(eventName string) []GoogleAdsConversionTracking {
if value, ok := s.Settings[eventName]; ok {
return value
}
return GoogleAdsConversionTracking{}
return nil
}

View File

@ -1,5 +1,8 @@
package config
type GoogleAdsConversionTracking struct {
// Conversion label
Label string `json:"label" yaml:"label"`
// Optional conversion id overriding the default
ConversionID string `json:"conversionId" yaml:"conversionId"`
}

View File

@ -7,6 +7,10 @@ import (
type GoogleAnalytics struct {
// Enable provider
Enabled bool `json:"enabled" yaml:"enabled"`
// Google Analytics account id
AccountID string `json:"accountId" yaml:"accountId"`
// Google Analytics property id
PropertyID string `json:"propertyId" yaml:"propertyId"`
// Google Consent settings
GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"`
// GTag.js override configuration

30
pkg/config/mixpanel.go Normal file
View File

@ -0,0 +1,30 @@
package config
import (
"github.com/foomo/gocontemplate/pkg/contemplate"
)
type (
Mixpanel struct {
// Enable provider
Enabled bool `json:"enabled" yaml:"enabled"`
// Mixpanel project token
ProjectToken string `json:"projectToken" yaml:"projectToken"`
// Google Consent settings
GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"`
// Google Tag Manager server container settings
ServerContainer MixpanelServerContainer `json:"serverContainer" yaml:"serverContainer"`
}
MixpanelServerContainer struct {
// Set events
Set contemplate.Config `json:"set" yaml:"set"`
// SetOnce events
SetOnce contemplate.Config `json:"setOnce" yaml:"setOnce"`
// Reset events
Reset contemplate.Config `json:"reset" yaml:"reset"`
// Track events
Track contemplate.Config `json:"track" yaml:"track"`
// Identify events
Identify contemplate.Config `json:"identify" yaml:"identify"`
}
)

20
pkg/config/pinterest.go Normal file
View File

@ -0,0 +1,20 @@
package config
import (
"github.com/foomo/gocontemplate/pkg/contemplate"
)
type Pinterest struct {
// Enable provider
Enabled bool `json:"enabled" yaml:"enabled"`
// Pinterest advertiser id
AdvertiserID string `json:"advertiserId" yaml:"advertiserId"`
// Pinterest API access token
APIAccessToken string `json:"apiAccessToken" yaml:"apiAccessToken"`
// Enable test mode
TestModeEnabled bool `json:"testModeEnabled" yaml:"testModeEnabled"`
// Google Consent settings
GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"`
// Google Tag Manager server container settings
ServerContainer contemplate.Config `json:"serverContainer" yaml:"serverContainer"`
}

View File

@ -1,3 +1,3 @@
package config
const Version = "1.0"
const Version = "1.1"

View File

@ -35,7 +35,7 @@ func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.Conversio
return errors.Wrap(err, "failed to upsert event trigger: "+NameConversionLinkerTrigger)
}
if _, err := tm.UpsertTag(ctx, folder, containertag.NewConversionLinker(Name, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, containertag.NewConversionLinker(Name, cfg.EnableLinkerParams, eventTrigger)); err != nil {
return err
}

View File

@ -1,11 +1,13 @@
package tag
import (
"strconv"
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func NewConversionLinker(name string, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
func NewConversionLinker(name string, enableLinkerParams bool, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: name,
@ -14,7 +16,7 @@ func NewConversionLinker(name string, triggers ...*tagmanager.Trigger) *tagmanag
{
Key: "enableLinkerParams",
Type: "boolean",
Value: "false",
Value: strconv.FormatBool(enableLinkerParams),
},
{
Key: "enableCookieOverrides",

View File

@ -24,7 +24,7 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg
template, err := tm.LookupTemplate(ctx, NameCriteoEventsAPITemplate)
if err != nil {
if errors.Is(err, tagmanager.ErrNotFound) {
l.Warn("Please install the 'Criteo Events API' template manually first")
l.Warn("Please install the 'Criteo Events API' Tag Template manually first")
}
return err
}

View File

@ -113,7 +113,7 @@ const headerList = ["referer", "user-agent"];
const requestUrl = merchantUrl+'?'+serializeData(mappedData);
const requestOptions = {
headers: generateRequestHeaders(headerList, cookieList),
timeout: 500,
timeout: 1000,
};
return sendHttpGet(requestUrl, requestOptions).then((result) => {

View File

@ -13,6 +13,7 @@ import (
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/pkg/errors"
tagmanager2 "google.golang.org/api/tagmanager/v2"
)
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Facebook) error {
@ -31,9 +32,13 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg
return err
}
testEventToken, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameTestEventTokenConstant, cfg.TestEventToken))
if err != nil {
return err
var testEventToken *tagmanager2.Variable
if cfg.TestEventToken != "" {
var err error
testEventToken, err = tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameTestEventTokenConstant, cfg.TestEventToken))
if err != nil {
return err
}
}
template, err := tm.LookupTemplate(ctx, NameConversionsAPITagTemplate)

View File

@ -13,42 +13,48 @@ func ConversionsAPITagName(v string) string {
}
func NewConversionsAPITag(name string, pixelID, apiAccessToken, testEventCode *tagmanager.Variable, settings config.FacebookConversionAPITag, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
params := []*tagmanager.Parameter{
{
Key: "pixelId",
Type: "template",
Value: "{{" + pixelID.Name + "}}",
},
{
Key: "apiAccessToken",
Type: "template",
Value: "{{" + apiAccessToken.Name + "}}",
},
{
Key: "enableEventEnhancement",
Type: "boolean",
Value: strconv.FormatBool(settings.EnableEventEnhancement),
},
{
Key: "extendCookies",
Type: "boolean",
Value: strconv.FormatBool(settings.ExtendCookies),
},
{
Key: "actionSource",
Type: "template",
Value: "website",
},
}
if testEventCode != nil {
params = append(params, &tagmanager.Parameter{
Key: "testEventCode",
Type: "template",
Value: "{{" + testEventCode.Name + "}}",
},
)
}
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: ConversionsAPITagName(name),
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
Key: "pixelId",
Type: "template",
Value: "{{" + pixelID.Name + "}}",
},
{
Key: "apiAccessToken",
Type: "template",
Value: "{{" + apiAccessToken.Name + "}}",
},
{
Key: "testEventCode",
Type: "template",
Value: "{{" + testEventCode.Name + "}}",
},
{
Key: "enableEventEnhancement",
Type: "boolean",
Value: strconv.FormatBool(settings.EnableEventEnhancement),
},
{
Key: "extendCookies",
Type: "boolean",
Value: strconv.FormatBool(settings.ExtendCookies),
},
{
Key: "actionSource",
Type: "template",
Value: "website",
},
},
Type: utils.TemplateType(template),
Parameter: params,
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,12 @@
package googleads
import (
"context"
pkgtagmanager "github.com/foomo/sesamy-cli/pkg/tagmanager"
"google.golang.org/api/tagmanager/v2"
)
func Folder(ctx context.Context, tm *pkgtagmanager.TagManager) (*tagmanager.Folder, error) {
return tm.UpsertFolder(ctx, "Sesamy - "+Name)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/foomo/sesamy-cli/pkg/provider/googleads/server/trigger"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent"
googleconsentvariable "github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/variable"
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
"github.com/foomo/sesamy-cli/pkg/tagmanager/server/variable"
@ -17,7 +18,12 @@ import (
)
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.GoogleAds) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
folder, err := Folder(ctx, tm)
if err != nil {
return err
}
gtmFolder, err := googletagmanager.Folder(ctx, tm)
if err != nil {
return err
}
@ -29,12 +35,12 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg
// conversion
if cfg.Conversion.Enabled {
value, err := tm.UpsertVariable(ctx, folder, variable.NewEventData("value"))
value, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData("value"))
if err != nil {
return err
}
currency, err := tm.UpsertVariable(ctx, folder, variable.NewEventData("currency"))
currency, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData("currency"))
if err != nil {
return err
}
@ -63,8 +69,10 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewGoogleAdsConversionTracking(event, value, currency, conversionID, cfg.Conversion.ServerContainer.Setting(event), eventTrigger)); err != nil {
return err
for _, setting := range cfg.Conversion.ServerContainer.Setting(event) {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewGoogleAdsConversionTracking(event, value, currency, conversionID, setting, eventTrigger)); err != nil {
return err
}
}
}
}

View File

@ -11,9 +11,16 @@ func GoogleAdsConversionTrackingName(v string) string {
}
func NewGoogleAdsConversionTracking(name string, value, currency, conversionID *tagmanager.Variable, settings config.GoogleAdsConversionTracking, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
tagName := GoogleAdsConversionTrackingName(name)
tagConversionID := "{{" + conversionID.Name + "}}"
if settings.ConversionID != "" {
tagName += " (" + settings.ConversionID + ")"
tagConversionID = settings.ConversionID
}
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: GoogleAdsConversionTrackingName(name),
Name: tagName,
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
@ -39,7 +46,7 @@ func NewGoogleAdsConversionTracking(name string, value, currency, conversionID *
{
Key: "conversionId",
Type: "template",
Value: "{{" + conversionID.Name + "}}",
Value: tagConversionID,
},
{
Key: "currencyCode",

View File

@ -0,0 +1,12 @@
package googletag
import (
"context"
pkgtagmanager "github.com/foomo/sesamy-cli/pkg/tagmanager"
"google.golang.org/api/tagmanager/v2"
)
func Folder(ctx context.Context, tm *pkgtagmanager.TagManager) (*tagmanager.Folder, error) {
return tm.UpsertFolder(ctx, "Sesamy - "+Name)
}

View File

@ -9,7 +9,7 @@ import (
)
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
folder, err := Folder(ctx, tm)
if err != nil {
return err
}

View File

@ -16,7 +16,7 @@ import (
)
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
folder, err := Folder(ctx, tm)
if err != nil {
return err
}

View File

@ -0,0 +1,12 @@
package googletagmanager
import (
"context"
pkgtagmanager "github.com/foomo/sesamy-cli/pkg/tagmanager"
"google.golang.org/api/tagmanager/v2"
)
func Folder(ctx context.Context, tm *pkgtagmanager.TagManager) (*tagmanager.Folder, error) {
return tm.UpsertFolder(ctx, "Sesamy - "+Name)
}

View File

@ -12,7 +12,7 @@ import (
)
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTagManager, enableGeoResolution bool) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
folder, err := Folder(ctx, tm)
if err != nil {
return err
}

View File

@ -531,7 +531,7 @@ if (data.activateLogs) {
return sendHttpGet(url, {
headers: {key: 'value'},
timeout: 500,
timeout: 1000,
}).then((result) => {
if (result.statusCode >= 200 && result.statusCode < 300) {
data.gtmOnSuccess();

View File

@ -0,0 +1,8 @@
package mixpanel
const (
Tag = "mixpanel"
Name = "Mixpanel"
NameTagTemplate = "Mixpanel"
NamePrjectTokenConstant = "Mixpanel Project Token"
)

View File

@ -0,0 +1,12 @@
package mixpanel
import (
"context"
pkgtagmanager "github.com/foomo/sesamy-cli/pkg/tagmanager"
"google.golang.org/api/tagmanager/v2"
)
func Folder(ctx context.Context, tm *pkgtagmanager.TagManager) (*tagmanager.Folder, error) {
return tm.UpsertFolder(ctx, "Sesamy - "+Name)
}

View File

@ -0,0 +1,228 @@
package mixpanel
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent"
googleconsentvariable "github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/variable"
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
servertagx "github.com/foomo/sesamy-cli/pkg/provider/mixpanel/server/tag"
"github.com/foomo/sesamy-cli/pkg/provider/mixpanel/server/trigger"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
"github.com/foomo/sesamy-cli/pkg/tagmanager/server/variable"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/pkg/errors"
tagmanager2 "google.golang.org/api/tagmanager/v2"
)
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Mixpanel) error {
folder, err := Folder(ctx, tm)
if err != nil {
return err
}
gtmFolder, err := googletagmanager.Folder(ctx, tm)
if err != nil {
return err
}
template, err := tm.LookupTemplate(ctx, NameTagTemplate)
if err != nil {
if errors.Is(err, tagmanager.ErrNotFound) {
l.Warn("Please install the 'Mixpanel' by stape-io Tag Template manually first")
}
return err
}
projectToken, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NamePrjectTokenConstant, cfg.ProjectToken))
if err != nil {
return err
}
{ // create set tags
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer.Set)
if err != nil {
return err
}
for event, params := range eventParameters {
var eventTriggerOpts []trigger.EventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.EventWithConsentMode(consentVariable))
}
eventParams := map[string]*tagmanager2.Variable{}
for k := range params {
if value, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData(k)); err != nil {
return err
} else {
eventParams[k] = value
}
}
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewSet(event, projectToken, template, eventParams, eventTrigger)); err != nil {
return err
}
}
}
{ // create set once tags
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer.SetOnce)
if err != nil {
return err
}
for event, params := range eventParameters {
var eventTriggerOpts []trigger.EventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.EventWithConsentMode(consentVariable))
}
eventParams := map[string]*tagmanager2.Variable{}
for k := range params {
if value, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData(k)); err != nil {
return err
} else {
eventParams[k] = value
}
}
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewSetOnce(event, projectToken, template, eventParams, eventTrigger)); err != nil {
return err
}
}
}
{ // create reset tags
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer.Reset)
if err != nil {
return err
}
for event := range eventParameters {
var eventTriggerOpts []trigger.EventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.EventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewReset(event, projectToken, template, eventTrigger)); err != nil {
return err
}
}
}
{ // create track tags
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer.Track)
if err != nil {
return err
}
for event, params := range eventParameters {
var eventTriggerOpts []trigger.EventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.EventWithConsentMode(consentVariable))
}
eventParams := map[string]*tagmanager2.Variable{}
for k := range params {
if value, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData(k)); err != nil {
return err
} else {
eventParams[k] = value
}
}
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewTrack(event, projectToken, template, eventParams, eventTrigger)); err != nil {
return err
}
}
}
{ // create reset tags
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer.Identify)
if err != nil {
return err
}
for event := range eventParameters {
var eventTriggerOpts []trigger.EventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.EventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
userID, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData("user_id"))
if err != nil {
return err
}
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewIdentify(event, userID, projectToken, template, eventTrigger)); err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,51 @@
package tag
import (
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func IdentifyName(v string) string {
return "Mixpanel Identify - " + v
}
func NewIdentify(name string, identifier, projectToken *tagmanager.Variable, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: IdentifyName(name),
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
Key: "serverEU",
Type: "boolean",
Value: "true",
},
{
Key: "logType",
Type: "template",
Value: "debug",
},
{
Key: "identifier",
Type: "template",
Value: "{{" + identifier.Name + "}}",
},
{
Key: "identifyAuto",
Type: "boolean",
Value: "true",
},
{
Key: "type",
Type: "template",
Value: "reset",
},
{
Key: "token",
Type: "template",
Value: "{{" + projectToken.Name + "}}",
},
},
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,41 @@
package tag
import (
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func ResetName(v string) string {
return "Mixpanel Reset - " + v
}
func NewReset(name string, projectToken *tagmanager.Variable, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: ResetName(name),
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
Key: "serverEU",
Type: "boolean",
Value: "true",
},
{
Key: "logType",
Type: "template",
Value: "debug",
},
{
Key: "type",
Type: "template",
Value: "reset",
},
{
Key: "token",
Type: "template",
Value: "{{" + projectToken.Name + "}}",
},
},
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,88 @@
package tag
import (
"maps"
"slices"
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func SetName(v string) string {
return "Mixpanel Set - " + v
}
func NewSet(name string, projectToken *tagmanager.Variable, template *tagmanager.CustomTemplate, params map[string]*tagmanager.Variable, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
parameter := []*tagmanager.Parameter{
{
Key: "serverEU",
Type: "boolean",
Value: "true",
},
{
Key: "logType",
Type: "template",
Value: "debug",
},
{
Key: "identifyAuto",
Type: "boolean",
Value: "true",
},
{
Key: "trackName",
Type: "template",
Value: name,
},
{
Key: "type",
Type: "template",
Value: "profile-set",
},
{
Key: "token",
Type: "template",
Value: "{{" + projectToken.Name + "}}",
},
{
Key: "trackFromVariable",
Type: "boolean",
Value: "false",
},
}
if len(params) > 0 {
var list []*tagmanager.Parameter
for _, key := range slices.Sorted(maps.Keys(params)) {
param := params[key]
list = append(list, &tagmanager.Parameter{
Type: "map",
Map: []*tagmanager.Parameter{
{
Key: "name",
Type: "template",
Value: key,
},
{
Key: "value",
Type: "template",
Value: "{{" + param.Name + "}}",
},
},
})
}
parameter = append(parameter, &tagmanager.Parameter{
Key: "userPropertiesTable",
Type: "list",
List: list,
})
}
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: SetName(name),
TagFiringOption: "oncePerEvent",
Parameter: parameter,
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,88 @@
package tag
import (
"maps"
"slices"
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func SetOnceName(v string) string {
return "Mixpanel SetOnce - " + v
}
func NewSetOnce(name string, projectToken *tagmanager.Variable, template *tagmanager.CustomTemplate, params map[string]*tagmanager.Variable, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
parameter := []*tagmanager.Parameter{
{
Key: "serverEU",
Type: "boolean",
Value: "true",
},
{
Key: "logType",
Type: "template",
Value: "debug",
},
{
Key: "identifyAuto",
Type: "boolean",
Value: "true",
},
{
Key: "trackName",
Type: "template",
Value: name,
},
{
Key: "type",
Type: "template",
Value: "profile-set-once",
},
{
Key: "token",
Type: "template",
Value: "{{" + projectToken.Name + "}}",
},
{
Key: "trackFromVariable",
Type: "boolean",
Value: "false",
},
}
if len(params) > 0 {
var list []*tagmanager.Parameter
for _, key := range slices.Sorted(maps.Keys(params)) {
param := params[key]
list = append(list, &tagmanager.Parameter{
Type: "map",
Map: []*tagmanager.Parameter{
{
Key: "name",
Type: "template",
Value: key,
},
{
Key: "value",
Type: "template",
Value: "{{" + param.Name + "}}",
},
},
})
}
parameter = append(parameter, &tagmanager.Parameter{
Key: "userPropertiesToSetOnce",
Type: "list",
List: list,
})
}
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: SetOnceName(name),
TagFiringOption: "oncePerEvent",
Parameter: parameter,
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,93 @@
package tag
import (
"maps"
"slices"
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func TrackName(v string) string {
return "Mixpanel Track - " + v
}
func NewTrack(name string, projectToken *tagmanager.Variable, template *tagmanager.CustomTemplate, params map[string]*tagmanager.Variable, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
parameter := []*tagmanager.Parameter{
{
Key: "serverEU",
Type: "boolean",
Value: "true",
},
{
Key: "logType",
Type: "template",
Value: "debug",
},
{
Key: "trackCommonData",
Type: "boolean",
Value: "true",
},
{
Key: "identifyAuto",
Type: "boolean",
Value: "true",
},
{
Key: "trackName",
Type: "template",
Value: name,
},
{
Key: "type",
Type: "template",
Value: "track",
},
{
Key: "token",
Type: "template",
Value: "{{" + projectToken.Name + "}}",
},
{
Key: "trackFromVariable",
Type: "boolean",
Value: "false",
},
}
if len(params) > 0 {
var list []*tagmanager.Parameter
for _, key := range slices.Sorted(maps.Keys(params)) {
param := params[key]
list = append(list, &tagmanager.Parameter{
Type: "map",
Map: []*tagmanager.Parameter{
{
Key: "name",
Type: "template",
Value: key,
},
{
Key: "value",
Type: "template",
Value: "{{" + param.Name + "}}",
},
},
})
}
parameter = append(parameter, &tagmanager.Parameter{
Key: "trackParameters",
Type: "list",
List: list,
})
}
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: TrackName(name),
TagFiringOption: "oncePerEvent",
Parameter: parameter,
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,75 @@
package trigger
import (
"google.golang.org/api/tagmanager/v2"
)
func EventName(v string) string {
return "Mixpanel - " + v
}
type (
EventOptions struct {
consentMode *tagmanager.Variable
}
EventOption func(*EventOptions)
)
func EventWithConsentMode(mode *tagmanager.Variable) EventOption {
return func(o *EventOptions) {
o.consentMode = mode
}
}
func NewEvent(name string, opts ...EventOption) *tagmanager.Trigger {
o := &EventOptions{}
for _, opt := range opts {
if opt != nil {
opt(o)
}
}
var filter []*tagmanager.Condition
if o.consentMode != nil {
filter = append(filter,
&tagmanager.Condition{
Parameter: []*tagmanager.Parameter{
{
Key: "arg0",
Type: "template",
Value: "{{" + o.consentMode.Name + "}}",
},
{
Key: "arg1",
Type: "template",
Value: "granted",
},
},
Type: "equals",
},
)
}
return &tagmanager.Trigger{
Type: "customEvent",
Name: EventName(name),
CustomEventFilter: []*tagmanager.Condition{
{
Parameter: []*tagmanager.Parameter{
{
Key: "arg0",
Type: "template",
Value: "{{_event}}",
},
{
Key: "arg1",
Type: "template",
Value: name,
},
},
Type: "equals",
},
},
Filter: filter,
}
}

View File

@ -0,0 +1,7 @@
package pinterest
const (
Tag = "pinterest"
Name = "Pinterest"
NameTagTemplate = "Pinterest API for Conversions Tag"
)

View File

@ -0,0 +1,62 @@
package pinterest
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent"
googleconsentvariable "github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/variable"
containertag "github.com/foomo/sesamy-cli/pkg/provider/pinterest/server/tag"
"github.com/foomo/sesamy-cli/pkg/provider/pinterest/server/trigger"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/pkg/errors"
)
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Pinterest) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
template, err := tm.LookupTemplate(ctx, NameTagTemplate)
if err != nil {
if errors.Is(err, tagmanager.ErrNotFound) {
l.Warn("Please install the 'Pinterest API for Conversions Ta' Tag Template manually first")
}
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer)
if err != nil {
return err
}
for event := range eventParameters {
var eventTriggerOpts []trigger.EventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.ConversionWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewConversion(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(ctx, folder, containertag.NewConversion(event, cfg, template, eventTrigger)); err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,59 @@
package tag
import (
"strconv"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/utils"
"google.golang.org/api/tagmanager/v2"
)
func ConversionName(v string) string {
return "Pinterest Conversion - " + v
}
func NewConversion(name string, cfg config.Pinterest, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
return &tagmanager.Tag{
FiringTriggerId: utils.TriggerIDs(triggers),
Name: ConversionName(name),
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
Key: "apiAccessToken",
Type: "template",
Value: cfg.APIAccessToken,
},
{
Key: "testMode",
Type: "boolean",
Value: strconv.FormatBool(cfg.TestModeEnabled),
},
{
Key: "eventName",
Type: "template",
Value: "inherit",
},
{
Key: "logMode",
Type: "template",
Value: func(testMode bool) string {
if testMode {
return "log"
}
return "donotlog"
}(cfg.TestModeEnabled),
},
{
Key: "overrideMode",
Type: "boolean",
Value: "false",
},
{
Key: "advertiserId",
Type: "template",
Value: cfg.AdvertiserID,
},
},
Type: utils.TemplateType(template),
}
}

View File

@ -0,0 +1,83 @@
package trigger
import (
"google.golang.org/api/tagmanager/v2"
)
func ConversionName(v string) string {
return "Pinterest Conversion - " + v
}
type (
EventOptions struct {
consentMode *tagmanager.Variable
}
EventOption func(*EventOptions)
)
// ------------------------------------------------------------------------------------------------
// ~ Options
// ------------------------------------------------------------------------------------------------
func ConversionWithConsentMode(mode *tagmanager.Variable) EventOption {
return func(o *EventOptions) {
o.consentMode = mode
}
}
// ------------------------------------------------------------------------------------------------
// ~ Constructor
// ------------------------------------------------------------------------------------------------
func NewConversion(name string, opts ...EventOption) *tagmanager.Trigger {
o := &EventOptions{}
for _, opt := range opts {
if opt != nil {
opt(o)
}
}
var filter []*tagmanager.Condition
if o.consentMode != nil {
filter = append(filter,
&tagmanager.Condition{
Parameter: []*tagmanager.Parameter{
{
Key: "arg0",
Type: "template",
Value: "{{" + o.consentMode.Name + "}}",
},
{
Key: "arg1",
Type: "template",
Value: "granted",
},
},
Type: "equals",
},
)
}
return &tagmanager.Trigger{
Type: "customEvent",
Name: ConversionName(name),
CustomEventFilter: []*tagmanager.Condition{
{
Parameter: []*tagmanager.Parameter{
{
Key: "arg0",
Type: "template",
Value: "{{_event}}",
},
{
Key: "arg1",
Type: "template",
Value: name,
},
},
Type: "equals",
},
},
Filter: filter,
}
}

View File

@ -129,7 +129,7 @@ if (!isConsentGivenOrNotRequired()) {
const options = {
headers: {'tracify-token': data.token},
method: 'POST',
timeout: 500,
timeout: 1000,
};
const body = mapEventData();

View File

@ -5,7 +5,7 @@ import (
)
func EventDataName(v string) string {
return "event." + v
return "eventData." + v
}
func NewEventData(name string) *tagmanager.Variable {

View File

@ -74,9 +74,13 @@ export interface Item {
}
export interface PageView {
page_title?: string;
page_referrer?: string;
client_id?: string;
language?: string;
page_encoding?: string;
page_location?: string;
page_referrer?: string;
page_title?: string;
user_agent?: string;
}
`
expectedSesamy = `
@ -106,6 +110,7 @@ export enum EventName {
LevelStart = "level_start",
LevelUp = "level_up",
Login = "login",
Logout = "logout",
PageView = "page_view",
PostScore = "post_score",
Purchase = "purchase",
@ -118,7 +123,6 @@ export enum EventName {
SelectContent = "select_content",
SelectItem = "select_item",
SelectPromotion = "select_promotion",
SessionStart = "session_start",
Share = "share",
SignUp = "sign_up",
SpendVirtualCurrency = "spend_virtual_currency",

98
pkg/utils/highlight.go Normal file
View File

@ -0,0 +1,98 @@
package utils
import (
"bytes"
"fmt"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/pterm/pterm"
)
func Highlight(source string) string {
out := &numberWriter{
w: bytes.NewBufferString(""),
currentLine: 1,
}
// Determine lexer.
l := lexers.Get("yaml")
if l == nil {
l = lexers.Analyse(source)
}
if l == nil {
l = lexers.Fallback
}
l = chroma.Coalesce(l)
// Determine formatter.
f := formatters.Get("terminal256")
if f == nil {
f = formatters.Fallback
}
// Determine style.
s := styles.Get("monokai")
if s == nil {
s = styles.Fallback
}
it, err := l.Tokenise(nil, source)
if err != nil {
pterm.Error.Println(err.Error())
}
if err = f.Format(out, s, it); err != nil {
pterm.Error.Println(err.Error())
}
return out.w.String()
}
type numberWriter struct {
w *bytes.Buffer
currentLine uint64
buf []byte
}
func (w *numberWriter) Write(p []byte) (int, error) {
// Early return.
// Can't calculate the line numbers until the line breaks are made, so store them all in a buffer.
if !bytes.Contains(p, []byte{'\n'}) {
w.buf = append(w.buf, p...)
return len(p), nil
}
var (
original = p
tokenLen uint
)
for i, c := range original {
tokenLen++
if c != '\n' {
continue
}
token := p[:tokenLen]
p = original[i+1:]
tokenLen = 0
format := "%4d |\t%s%s"
if w.currentLine > 9999 {
format = "%d |\t%s%s"
}
format = "\033[34m" + format + "\033[0m"
if _, err := fmt.Fprintf(w.w, format, w.currentLine, string(w.buf), string(token)); err != nil {
return i + 1, err
}
w.buf = w.buf[:0]
w.currentLine++
}
if len(p) > 0 {
w.buf = append(w.buf, p...)
}
return len(original), nil
}

View File

@ -15,6 +15,12 @@
},
"type": "array"
},
"[]config.GoogleAdsConversionTracking": {
"items": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleAdsConversionTracking"
},
"type": "array"
},
"[]string": {
"items": {
"type": "string"
@ -95,6 +101,14 @@
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAds",
"description": "MicrosoftAds provider settings"
},
"mixpanel": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.Mixpanel",
"description": "Mixpanel provider settings"
},
"pinterest": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.Pinterest",
"description": "Pinterest provider settings"
},
"emarsys": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.Emarsys",
"description": "Emarsys provider settings"
@ -131,6 +145,10 @@
"googleConsent": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleConsent",
"description": "Google Consent settings"
},
"enableLinkerParams": {
"type": "boolean",
"description": "Accept incoming linker parameters"
}
},
"additionalProperties": false,
@ -384,7 +402,8 @@
"$ref": "#/$defs/[]*contemplate.PackageConfig"
},
"settings": {
"$ref": "#/$defs/map[string]config.GoogleAdsConversionTracking"
"$ref": "#/$defs/map[string][]config.GoogleAdsConversionTracking",
"description": "Conversion settings map"
}
},
"additionalProperties": false,
@ -393,7 +412,12 @@
"github.com.foomo.sesamy-cli.pkg.config.GoogleAdsConversionTracking": {
"properties": {
"label": {
"type": "string"
"type": "string",
"description": "Conversion label"
},
"conversionId": {
"type": "string",
"description": "Optional conversion id overriding the default"
}
},
"additionalProperties": false,
@ -418,6 +442,14 @@
"type": "boolean",
"description": "Enable provider"
},
"accountId": {
"type": "string",
"description": "Google Analytics account id"
},
"propertyId": {
"type": "string",
"description": "Google Analytics property id"
},
"googleConsent": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleConsent",
"description": "Google Consent settings"
@ -669,6 +701,84 @@
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.Mixpanel": {
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable provider"
},
"projectToken": {
"type": "string",
"description": "Mixpanel project token"
},
"googleConsent": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleConsent",
"description": "Google Consent settings"
},
"serverContainer": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MixpanelServerContainer",
"description": "Google Tag Manager server container settings"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.MixpanelServerContainer": {
"properties": {
"set": {
"$ref": "#/$defs/github.com.foomo.gocontemplate.pkg.contemplate.Config",
"description": "Set events"
},
"setOnce": {
"$ref": "#/$defs/github.com.foomo.gocontemplate.pkg.contemplate.Config",
"description": "SetOnce events"
},
"reset": {
"$ref": "#/$defs/github.com.foomo.gocontemplate.pkg.contemplate.Config",
"description": "Reset events"
},
"track": {
"$ref": "#/$defs/github.com.foomo.gocontemplate.pkg.contemplate.Config",
"description": "Track events"
},
"identify": {
"$ref": "#/$defs/github.com.foomo.gocontemplate.pkg.contemplate.Config",
"description": "Identify events"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.Pinterest": {
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable provider"
},
"advertiserId": {
"type": "string",
"description": "Pinterest advertiser id"
},
"apiAccessToken": {
"type": "string",
"description": "Pinterest API access token"
},
"testModeEnabled": {
"type": "boolean",
"description": "Enable test mode"
},
"googleConsent": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleConsent",
"description": "Google Consent settings"
},
"serverContainer": {
"$ref": "#/$defs/github.com.foomo.gocontemplate.pkg.contemplate.Config",
"description": "Google Tag Manager server container settings"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.Tracify": {
"properties": {
"enabled": {
@ -741,15 +851,15 @@
"additionalProperties": false,
"type": "object"
},
"map[string]config.FacebookConversionAPITag": {
"map[string][]config.GoogleAdsConversionTracking": {
"additionalProperties": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.FacebookConversionAPITag"
"$ref": "#/$defs/[]config.GoogleAdsConversionTracking"
},
"type": "object"
},
"map[string]config.GoogleAdsConversionTracking": {
"map[string]config.FacebookConversionAPITag": {
"additionalProperties": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleAdsConversionTracking"
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.FacebookConversionAPITag"
},
"type": "object"
},

View File

@ -1,5 +1,5 @@
# yaml-language-server: $schema=sesamy.schema.json
version: '1.0'
version: '1.1'
# Whether to redact the visitor ip
redactVisitorIp: true
@ -200,14 +200,14 @@ googleAds:
enabled: true
# Google Tag Manager server container settings
serverContainer:
# Conversion labels map
settings:
add_to_cart:
label: ''
purchase:
label: ''
# Path to the go.mod file
directory: .
# Conversion settings map
settings:
add_to_cart:
- label: ''
purchase:
- label: ''
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -225,6 +225,8 @@ conversionLinker:
enabled: true
# Consent mode name
mode: ad_storage
# Accept incoming linker parameters
enableLinkerParams: true
# --- Umami settings
umami:
@ -439,3 +441,70 @@ cookiebot:
adUserData: denied
# Default consent ad_personalization
adPersonalization: denied
# --- Mixpanel
mixpanel:
# Enable provider
enabled: true
# Project Token
projectToken: ''
# Google Consent settings
googleConsent:
# Enable consent mode
enabled: true
# Consent mode name
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Track events
track:
# Directory containing the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: 'github.com/foomo/sesamy-go/pkg/event'
types:
- AddPaymentInfo
- AddShippingInfo
- AddToCart
- BeginCheckout
- PageView
- Purchase
- RemoveFromCart
- Search
- SelectItem
- ViewCart
- ViewItem
- ViewItemList
# --- Pinterest
pinterest:
# Enable provider
enabled: true
# Pinterest advertiser id
advertiserId: ''
# Pinterest API access token
apiAccessToken: ''
# Enable test mode
testModeEnabled: false
# Google Consent settings
googleConsent:
# Enable consent mode
enabled: true
# Consent mode name
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Directory containing the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: 'github.com/foomo/sesamy-go/pkg/event'
types:
- AddToCart
- GenerateLead
- PageView
- Purchase
- Search
- SignUp
- ViewItemList