Compare commits

...

101 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
Kevin Franklin Kim
9d9a7352eb
Merge pull request #117 from foomo/fix/modified-templates
fix: modified template id
2025-04-15 15:49:10 +02:00
Kevin Franklin Kim
f2efac7342
fix: modified template id 2025-04-15 09:43:35 +02:00
Kevin Franklin Kim
f93177867a
Merge pull request #116 from foomo/feature/optional-mpv2-user-data-transformation
feat: optional mpv2 user data transformation
2025-04-11 14:38:44 +02:00
Kevin Franklin Kim
7acd33aa8c
feat: add optional mpv2 user data transformation 2025-04-11 10:26:47 +02:00
Kevin Franklin Kim
9dc885eeb7
chore: add safe tag 2025-04-11 10:26:11 +02:00
Kevin Franklin Kim
36f9e0f81c
refactor: move pterm writer 2025-04-11 10:25:55 +02:00
Kevin Franklin Kim
e7967d1eb2
Merge pull request #115 from foomo/dependabot/go_modules/gomod-update-6182f5bb77
chore(deps): bump the gomod-update group across 1 directory with 3 updates
2025-03-31 11:12:43 +02:00
dependabot[bot]
2591b7b875
chore(deps): bump the gomod-update group across 1 directory with 3 updates
Bumps the gomod-update group with 3 updates in the / directory: [github.com/spf13/viper](https://github.com/spf13/viper), [github.com/wissance/stringFormatter](https://github.com/wissance/stringFormatter) and [google.golang.org/api](https://github.com/googleapis/google-api-go-client).


Updates `github.com/spf13/viper` from 1.20.0 to 1.20.1
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.20.0...v1.20.1)

Updates `github.com/wissance/stringFormatter` from 1.3.0 to 1.4.1
- [Release notes](https://github.com/wissance/stringFormatter/releases)
- [Commits](https://github.com/wissance/stringFormatter/compare/v1.3.0...v1.4.1)

Updates `google.golang.org/api` from 0.226.0 to 0.228.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.226.0...v0.228.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gomod-update
- dependency-name: github.com/wissance/stringFormatter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gomod-update
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gomod-update
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-30 08:59:59 +00:00
Kevin Franklin Kim
ad1fa735e0
Merge pull request #114 from foomo/feature/refactor-cli
refactor: cli setup
2025-03-27 21:25:41 +01:00
Kevin Franklin Kim
72378c8024
chore: update gh worker 2025-03-27 21:23:26 +01:00
Kevin Franklin Kim
408e227f92
chore: update config 2025-03-27 21:21:35 +01:00
Kevin Franklin Kim
705fe333bf
chore: update husky 2025-03-27 21:18:01 +01:00
Kevin Franklin Kim
5b55eb2b84
refactor: cli setup 2025-03-27 21:17:38 +01:00
Kevin Franklin Kim
0c9bb7ea73
Merge pull request #112 from foomo/fix/garbage-warning
fix: use warning logger
2025-03-20 16:50:31 +01:00
Kevin Franklin Kim
bc28c78ad8
fix: use warning logger 2025-03-20 16:45:25 +01:00
Kevin Franklin Kim
0617588911
Merge pull request #111 from foomo/feature/multi-configs
feat: multi configs
2025-03-20 16:21:56 +01:00
Kevin Franklin Kim
7cb97d0475
feat: pretty logging 2025-03-20 16:17:34 +01:00
Kevin Franklin Kim
4f632d515a
feat: allow multiple configs 2025-03-20 16:17:14 +01:00
Kevin Franklin Kim
8569b47ed4
Merge pull request #110 from foomo/fix/emarsys-category
fix(emarsys): format category
2025-03-19 17:32:12 +01:00
Kevin Franklin Kim
f3596f9f06
fix(emarsys): format category 2025-03-19 17:28:52 +01:00
Kevin Franklin Kim
91f32ebf3b
Merge pull request #109 from foomo/fix/emarsys-email
fix(emarsys): use only email if provided
2025-03-18 14:10:58 +01:00
Kevin Franklin Kim
3e40a767da
fix: use only email if provided 2025-03-18 14:07:44 +01:00
Kevin Franklin Kim
268f0c499b
feat: set max width 2025-03-18 14:07:21 +01:00
Kevin Franklin Kim
9826788be4
Merge pull request #108 from foomo/fix/ga-response-compression
fix: GA4 client response compression
2025-03-17 20:49:37 +01:00
Kevin Franklin Kim
284f12171b
fix: use booelan 2025-03-17 20:41:12 +01:00
Kevin Franklin Kim
315738f70c
Merge pull request #107 from foomo/fix/workspace-id
fix: use workspace loader
2025-03-17 20:30:48 +01:00
Kevin Franklin Kim
f1fe8c2c72
fix: use workspace loader 2025-03-17 17:53:59 +01:00
Kevin Franklin Kim
3880d503f1
Merge pull request #106 from foomo/fix/template-reference
fix: use template gallery id
2025-03-17 11:43:03 +01:00
Kevin Franklin Kim
898f63a1ad
fix: check nil 2025-03-17 11:39:06 +01:00
Kevin Franklin Kim
82d5b597f5
fix: use template gallery id 2025-03-17 11:32:41 +01:00
Kevin Franklin Kim
ea147e3865
Merge pull request #105 from foomo/feature/dynamic-workspace-id
feat: dynamic workspace id
2025-03-17 11:08:44 +01:00
Kevin Franklin Kim
058d2134e6
docs: update schema 2025-03-17 11:05:09 +01:00
Kevin Franklin Kim
e4a99eb2d3
chore: update dependabot 2025-03-17 11:04:28 +01:00
Kevin Franklin Kim
ac8f7298b7
feat: dynamic workspace id 2025-03-17 11:04:04 +01:00
Kevin Franklin Kim
7aa4f843e5
Merge pull request #103 from foomo/dependabot/go_modules/gomod-update-ec0bf6c2a5
chore(deps): bump the gomod-update group with 2 updates
2025-03-17 09:40:57 +01:00
Kevin Franklin Kim
e2f259bf5c
fix: switch dep 2025-03-17 09:38:40 +01:00
Kevin Franklin Kim
a92d456ed2
Merge branch 'main' of github.com:foomo/sesamy-cli into dependabot/go_modules/gomod-update-ec0bf6c2a5 2025-03-17 09:36:15 +01:00
Kevin Franklin Kim
339f91085d
Merge pull request #104 from foomo/feature/v0.17.1
v0.18.0
2025-03-17 09:35:32 +01:00
Kevin Franklin Kim
741c6bb44e
feat(cookiebot): add region settings 2025-03-17 09:30:37 +01:00
Kevin Franklin Kim
2983e5ee7d
feat(emarsys): add search param 2025-03-17 09:30:02 +01:00
Kevin Franklin Kim
233db21350
fix(emarsys): use test for initialization 2025-03-17 09:29:25 +01:00
dependabot[bot]
952ca528b8
chore(deps): bump the gomod-update group with 2 updates
Bumps the gomod-update group with 2 updates: [github.com/spf13/viper](https://github.com/spf13/viper) and [google.golang.org/api](https://github.com/googleapis/google-api-go-client).


Updates `github.com/spf13/viper` from 1.19.0 to 1.20.0
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.19.0...v1.20.0)

Updates `google.golang.org/api` from 0.225.0 to 0.226.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.225.0...v0.226.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-16 08:52:06 +00:00
Kevin Franklin Kim
3f1cf4f092
Merge pull request #102 from foomo/feature/sesamy-go-0.9.0
feat: sesamy go 0.9.0
2025-03-13 08:12:39 +01:00
Kevin Franklin Kim
7cdee0e36e
feat: sesamy go 0.9.0 2025-03-13 08:09:22 +01:00
Kevin Franklin Kim
d66e4ba2e9
Merge pull request #98 from foomo/dependabot/go_modules/gomod-update-dc0872d62f
chore(deps): bump google.golang.org/api from 0.222.0 to 0.224.0 in the gomod-update group across 1 directory
2025-03-11 17:09:20 +01:00
Kevin Franklin Kim
a096008783
Merge pull request #100 from foomo/fix/empty-load
fix: empty gocontemplate load
2025-03-11 17:08:30 +01:00
Kevin Franklin Kim
c86a7f01b1
docs: fix link 2025-03-11 17:04:34 +01:00
Kevin Franklin Kim
02c118c3c5
fix: empty load 2025-03-11 17:04:18 +01:00
Kevin Franklin Kim
90c63fa159
docs: update example 2025-03-11 15:07:49 +01:00
dependabot[bot]
fc4349ae11
chore(deps): bump google.golang.org/api
Bumps the gomod-update group with 1 update in the / directory: [google.golang.org/api](https://github.com/googleapis/google-api-go-client).


Updates `google.golang.org/api` from 0.222.0 to 0.224.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.222.0...v0.224.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-11 14:05:09 +00:00
Kevin Franklin Kim
3c045e165d
Merge pull request #99 from foomo/v0.16.0
V0.16.0
2025-03-11 15:04:01 +01:00
Kevin Franklin Kim
c17a587e7d
docs: update schema 2025-03-11 15:01:15 +01:00
Kevin Franklin Kim
2e6b01b08e
feat: bump gocontemplate 2025-03-11 14:56:53 +01:00
Kevin Franklin Kim
092b49487d
feat: go 1.24.1 & add directory 2025-03-11 14:55:49 +01:00
Kevin Franklin Kim
62465edf0d
chore: update dependabot 2025-03-11 14:55:08 +01:00
Kevin Franklin Kim
33f2863e8d
Merge pull request #97 from foomo/v0.15.1
fix: ensure order
2025-03-05 11:41:26 +01:00
Kevin Franklin Kim
3112bcc7f1
fix: ensure order 2025-03-05 11:38:34 +01:00
Kevin Franklin Kim
8cd029ee69
docs: update README 2025-03-05 10:11:36 +01:00
Kevin Franklin Kim
6f20c9bf96
Merge pull request #96 from foomo/v0.15.0
feat: add server container url
2025-03-05 10:08:33 +01:00
Kevin Franklin Kim
2321cd483b
feat: add server container url 2025-03-05 10:03:21 +01:00
Kevin Franklin Kim
09fa7bb1da
docs: update template 2025-02-28 09:21:36 +01:00
Kevin Franklin Kim
98399f961d
Merge pull request #94 from foomo/v0.14.1
fix(emarsys): always send cart
2025-02-28 09:21:11 +01:00
Kevin Franklin Kim
10a5197998
fix(emarsys): always send cart 2025-02-28 09:18:28 +01:00
Kevin Franklin Kim
837b8ae069
docs: update README 2025-02-28 08:05:59 +01:00
Kevin Franklin Kim
a15d637c0c
Merge pull request #93 from foomo/v0.14.0
refactor: use json md5 hash
2025-02-28 08:00:38 +01:00
Kevin Franklin Kim
f2d17d0e47
docs: add security policy and templates 2025-02-28 07:48:28 +01:00
Kevin Franklin Kim
1717f77f2f
refactor: use json md5 hash 2025-02-28 07:47:37 +01:00
111 changed files with 3964 additions and 1122 deletions

2
.dockerignore Normal file
View File

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

21
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,21 @@
# Contributing
If you want to submit a pull request to fix a bug or enhance an existing
feature, please first open an issue and link to that issue when you
submit your pull request.
If you have any questions about a possible submission, feel free to open
an issue too.
### Pull request process
1. Fork this repository
2. Create a branch in your fork to implement the changes. We recommend using
the issue number as part of your branch name, e.g. `1234-fixes`
3. Ensure that any documentation is updated with the changes that are required
by your fix.
4. Ensure that any samples are updated if the base image has been changed.
5. Submit the pull request. *Do not leave the pull request blank*. Explain exactly
what your changes are meant to do and provide simple steps on how to validate
your changes. Ensure that you reference the issue you created as well.
The pull request will be review before it is merged.

17
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@ -0,0 +1,17 @@
---
name: Bug Report
about: Report a bug you encountered
labels: bug
---
**What happened**:
**What you expected to happen**:
**How to reproduce it (as minimally and precisely as possible)**:
**Anything else we need to know?**:
**Environment**:
- Affected Version:
- OS (e.g: `cat /etc/os-release`):
- Others:

8
.github/ISSUE_TEMPLATE/enhancement.md vendored Normal file
View File

@ -0,0 +1,8 @@
---
name: Enhancement Request
about: Suggest an enhancement
labels: enhancement
---
**What would you like to be added**:
**Why is this needed**:

19
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,19 @@
### Type of Change
- [ ] New feature
- [ ] Bug fix
- [ ] Documentation update
- [ ] Refactoring
- [ ] Hotfix
- [ ] Security patch
### Description
_[Provide a detailed explanation of the changes you have made. Include the reasons behind these changes and any relevant context. Link any related issues.]_
### Related Issues
_[If this pull request addresses an issue, please link to it here (e.g., Fixes #123).]_
### Checklist
- [ ] My code adheres to the coding and style guidelines of the project.
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have made corresponding changes to the documentation.

45
.github/SECURITY.md vendored Normal file
View File

@ -0,0 +1,45 @@
# Security Guidelines
## How security is managed on this project
The foomo team and community take security seriously and wants to ensure that
we maintain a secure environment and provide secure solutions for the open
source community. To help us achieve these goals, please note the
following before using this software:
- Review the software license to understand the contributor's obligations in
terms of warranties and suitability for purpose
- For any questions or concerns about security, you can
[create an issue][new-issue] or [report a vulnerability][new-sec-issue]
- We request that you work with our security team and opt for
responsible disclosure using the guidelines below
- All security related issues and pull requests you make should be tagged with
"security" for easy identification
- Please monitor this repository and update your environment in a timely manner
as we release patches and updates
## Responsibly Disclosing Security Bugs
If you find a security bug in this repository, please work with contributors
following responsible disclosure principles and these guidelines:
- Do not submit a normal issue or pull request in our public repository, instead
[report it directly][new-sec-issue].
- We will review your submission and may follow up for additional details
- If you have a patch, we will review it and approve it privately; once approved
for release you can submit it as a pull request publicly in the repository (we
give credit where credit is due)
- We will keep you informed during our investigation, feel free to check in for
a status update
- We will release the fix and publicly disclose the issue as soon as possible,
but want to ensure we due properly due diligence before releasing
- Please do not publicly blog or post about the security issue until after we
have updated the public repo so that other downstream users have an opportunity
to patch
## Contact / Misc
If you have any questions, please reach out directly by [creating an issue][new-issue].
[new-issue]: https://github.com/foomo/sesamy-cli/issues/new/choose
[new-sec-issue]: https://github.com/foomo/sesamy-cli/security/advisories/new

View File

@ -1,26 +1,33 @@
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: 'github-actions'
open-pull-requests-limit: 1
directory: '/'
schedule:
day: 'sunday'
interval: 'weekly'
groups:
github-actions:
patterns:
- '*'
patterns: ['*']
- package-ecosystem: 'gomod'
open-pull-requests-limit: 1
directory: '/'
schedule:
day: 'sunday'
interval: 'weekly'
groups:
gomod-security:
patterns: ['*']
applies-to: security-updates
update-types: ['minor', 'patch']
patterns: ['*']
gomod-update:
applies-to: version-updates
update-types: ['minor', 'patch']
patterns: ['*']
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]

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@v6
- 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,108 +1,13 @@
# yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json
# https://golangci-lint.run/usage/configuration/
version: "2"
run:
go: 1.24.0
go: 1.24.3
build-tags: [safe]
modules-download-mode: readonly
issues:
exclude-dirs:
- 'bin'
- 'tmp'
exclude-rules:
- path: _test\.go
linters:
- forbidigo
- forcetypeassert
linters-settings:
# https://golangci-lint.run/usage/linters/#misspell
misspell:
mode: restricted
# https://golangci-lint.run/usage/linters/#asasalint
asasalint:
ignore-test: true
# https://golangci-lint.run/usage/linters/#exhaustive
exhaustive:
default-signifies-exhaustive: true
# https://golangci-lint.run/usage/linters/#predeclared
predeclared:
ignore: "new,error"
# https://golangci-lint.run/usage/linters/#gocritic
gocritic:
disabled-checks:
- ifElseChain
- commentFormatting
# https://golangci-lint.run/usage/linters/#testifylint
testifylint:
disable:
- float-compare
# https://golangci-lint.run/usage/linters/#gosec
gosec:
confidence: medium
# https://golangci-lint.run/usage/linters/#importas
importas:
no-unaliased: true
# https://golangci-lint.run/usage/linters/#gomoddirectives
gomoddirectives:
replace-local: true
# https://golangci-lint.run/usage/linters/#revive
revive:
ignore-generated-header: true
enable-all-rules: true
rules:
- name: line-length-limit
disabled: true
- name: cognitive-complexity
disabled: true
- name: unused-parameter
disabled: true
- name: add-constant
disabled: true
- name: cyclomatic
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: flag-parameter
disabled: true
- name: unused-receiver
disabled: true
- name: argument-limit
disabled: true
- name: max-control-nesting
disabled: true
- name: comment-spacings
disabled: true
- name: max-public-structs
disabled: true
- name: struct-tag
arguments:
- "json,inline"
- "yaml,squash"
- name: unhandled-error
arguments:
- "fmt.Println"
- "viper.BindPFlag"
- "strings.Builder.WriteString"
# TODO remove
- name: deep-exit
disabled: true
- name: if-return
disabled: true
- name: empty-block
disabled: true
- name: indent-error-flow
disabled: true
linters:
disable-all: true
default: none
enable:
## Default linters
- errcheck # errcheck is a program for checking for unchecked errors in Go code. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
- gosimple # (megacheck) Linter for Go source code that specializes in simplifying code [fast: false, auto-fix: false]
- govet # (vet, vetshadow) Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [fast: false, auto-fix: false]
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
- staticcheck # (megacheck) It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. [fast: false, auto-fix: false]
@ -131,9 +36,7 @@ linters:
- gochecksumtype # Run exhaustiveness checks on Go "sum types" [fast: false, auto-fix: false]
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
- gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: true]
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: true]
- goimports # Check import statements are formatted according to the 'goimport' command. Reformat imports in autofix mode. [fast: true, auto-fix: true]
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
- goprintffuncname # Checks that printf-like functions are named with `f` at the end. [fast: true, auto-fix: false]
@ -166,7 +69,6 @@ linters:
- rowserrcheck # checks whether Rows.Err of rows is checked successfully [fast: false, auto-fix: false]
- spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
- sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. [fast: false, auto-fix: false]
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
- testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false]
- testifylint # Checks usage of github.com/stretchr/testify. [fast: false, auto-fix: false]
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
@ -188,7 +90,6 @@ linters:
#- err113 # Go linter to check the errors handling expressions [fast: false, auto-fix: false]
#- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
#- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
#- ginkgolinter # enforces standards of using ginkgo and gomega [fast: false, auto-fix: false]
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false]
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
@ -196,7 +97,6 @@ linters:
#- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
#- godot # Check if comments end in a period [fast: true, auto-fix: true]
#- godox # Tool for detection of comment keywords [fast: true, auto-fix: false]
#- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
#- interfacebloat # A linter that checks the number of methods inside an interface. [fast: true, auto-fix: false]
#- intrange # (go >= 1.22) intrange is a linter to find places where for loops could make use of an integer range. [fast: true, auto-fix: false]
#- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
@ -216,3 +116,106 @@ linters:
#- wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
#- wsl # add or remove empty lines [fast: true, auto-fix: false]
#- zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg` [fast: false, auto-fix: false]
settings:
exhaustive:
default-signifies-exhaustive: true
gocritic:
disabled-checks:
- ifElseChain
- commentFormatting
gomoddirectives:
replace-local: true
gosec:
confidence: medium
importas:
no-unaliased: true
misspell:
mode: restricted
predeclared:
ignore:
- new
- error
revive:
enable-all-rules: true
rules:
- name: line-length-limit
disabled: true
- name: cognitive-complexity
disabled: true
- name: unused-parameter
disabled: true
- name: add-constant
disabled: true
- name: cyclomatic
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: flag-parameter
disabled: true
- name: unused-receiver
disabled: true
- name: argument-limit
disabled: true
- name: max-control-nesting
disabled: true
- name: comment-spacings
disabled: true
- name: max-public-structs
disabled: true
- name: struct-tag
arguments:
- json,inline
- yaml,squash
- name: unhandled-error
arguments:
- fmt.Println
- viper.BindPFlag
- strings.Builder.WriteString
- name: deep-exit
disabled: true
- name: if-return
disabled: true
- name: empty-block
disabled: true
- name: indent-error-flow
disabled: true
- name: var-naming
disabled: true
testifylint:
disable:
- float-compare
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- forbidigo
- forcetypeassert
path: _test\.go
- linters:
- asasalint
path: (.+)_test\.go
paths:
- bin
- tmp
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- bin
- tmp
- third_party$
- builtin$
- examples$

View File

@ -16,6 +16,7 @@ builds:
- '7'
flags:
- -trimpath
- -tags=safe
ldflags:
- -s -w -X github.com/foomo/sesamy-cli/cmd.version={{.Version}}
@ -23,10 +24,10 @@ release:
prerelease: auto
archives:
- format: tar.gz
- formats: [ tar.gz ]
format_overrides:
- goos: windows
format: zip
formats: [ zip ]
changelog:
use: github-native
@ -38,3 +39,76 @@ brews:
caveats: "sesamy --help"
homepage: "https://github.com/foomo/sesamy-cli"
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,6 +1,6 @@
hooks:
pre-commit:
- golangci-lint run --fast
- golangci-lint run --fast-only
- husky lint-staged
commit-msg:
# only execute if not in a merge
@ -8,7 +8,7 @@ hooks:
lint-staged:
'*.go':
- goimports -l -w
- golangci-lint fmt
lint-commit:
types: '^(feat|fix|build|chore|docs|perf|refactor|revert|style|test|wip)$'

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,9 +1,6 @@
.DEFAULT_GOAL:=help
.DEFAULT_GOAL := help
-include .makerc
# --- Config -----------------------------------------------------------------
export PATH := bin:$(PATH)
PATH:=bin:$(PATH)
# --- Targets -----------------------------------------------------------------
@ -20,12 +17,7 @@ export PATH := bin:$(PATH)
fi
@git config core.hooksPath .husky
## === Tasks ===
.PHONY: brew
## Install project binaries
brew:
@ownbrew install
### Tasks
.PHONY: doc
## Open go docs
@ -36,7 +28,7 @@ doc:
.PHONY: test
## Run tests
test:
@GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -race -json ./... | gotestfmt
@GO_TEST_TAGS=-skip go test -tags=safe -coverprofile=coverage.out -race -json -v ./... 2>&1 | tee /tmp/gotest.log | gotestfmt
.PHONY: lint
## Run linter
@ -62,48 +54,41 @@ outdated:
## Build binary
build:
@mkdir -p bin
@go build -o bin/sesamy main.go
@echo "building: bin/sesamy"
@go build -tags=safe -o bin/sesamy main.go
.PHONY: install
## Install binary
install:
@go build -o ${GOPATH}/bin/sesamy main.go
@echo "installing: ${GOPATH}/bin/sesamy"
@go build -tags=safe -o ${GOPATH}/bin/sesamy main.go
.PHONY: install.debug
## Install debug binary
install.debug:
@go build -gcflags "all=-N -l" -o ${GOPATH}/bin/sesamy main.go
@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)

131
README.md
View File

@ -33,7 +33,9 @@ Available Commands:
completion Generate the autocompletion script for the specified shell
config Print config
help Help about any command
tagmanager Provision Google Tag Manager containers
list List Google Tag Manager containers
provision Provision Google Tag Manager containers
tags Print out all available tags
typescript Generate typescript events
version Print version
@ -51,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
@ -78,16 +80,20 @@ googleTagManager:
tagId: GTM-57BHX34G
# The container id
containerId: '175355532'
# The workspace id that should be used by the api
# (Optional) The workspace id that should be used by the api
workspaceId: '23'
# (Optional) The workspace name that should be used by the api
workspace: 'Default Workspace'
# Server container settings
serverContainer:
# The container tag id
tagId: GTM-5NWPR4QW
# The container id
containerId: '175348980'
# The workspace id that should be used by the api
# (Optional) The workspace id that should be used by the api
workspaceId: '10'
# (Optional) The workspace name that should be used by the api
workspace: 'Default Workspace'
# Web container variables
webContainerVariables:
dataLayer:
@ -113,10 +119,14 @@ googleTag:
tagId: G-PZ5ELRCR31
# Whether a page_view should be sent on initial load
sendPageView: true
# Optional custom server container url
serverContainerUrl: ''
# TypeScript settings
typeScript:
# Target directory for generate files
outputPath: path/to/target
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -198,6 +208,8 @@ googleAnalytics:
ecommerceItems: true
# Google Tag Manager web container settings
webContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -206,6 +218,8 @@ googleAnalytics:
- SelectItem
# Google Tag Manager server container settings
serverContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -235,10 +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
@ -256,6 +280,8 @@ conversionLinker:
enabled: true
# Consent mode name
mode: ad_storage
# Accept incoming linker parameters
enableLinkerParams: true
# --- Umami settings
umami:
@ -275,6 +301,8 @@ umami:
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -293,6 +321,8 @@ criteo:
applicationId: com.foomo
# Google Tag Manager server container settings
serverContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -324,6 +354,8 @@ facebook:
# Consent mode name
mode: ad_storage
serverContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -352,6 +384,8 @@ emarsys:
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -376,6 +410,8 @@ tracify:
mode: analytics_storage
# Google Tag Manager server container settings
serverContainer:
# Path to the go.mod file
directory: .
# Contemplate package config for generated events
packages:
- path: github.com/foomo/sesamy-go/pkg/event
@ -407,6 +443,87 @@ cookiebot:
urlPassthrough: false
# Enable advertiser consent mode
advertiserConsentModeEnabled: false
# Default Consent state
regionSettings:
# Region (leave blank to apply globally)
- region: ''
# Default consent for functionality_storage and personalization_storage
preferences: denied
# Default consent for analytics_storage
statistics: denied
# Default consent for ad_storage
marketing: denied
# Default consent ad_user_data
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
@ -415,7 +532,7 @@ You might need to increase your Google Tag Manager API quotas, since they are li
## How to Contribute
Make a pull request...
Please refer to the [CONTRIBUTING](.github/CONTRIBUTING.md) details and follow the [CODE_OF_CONDUCT](.github/CODE_OF_CONDUCT.md) and [SECURITY](.github/SECURITY.md) guidelines.
## License

View File

@ -1,34 +1,49 @@
package cmd
import (
"bytes"
"encoding/json"
"fmt"
"log/slog"
"os"
"github.com/alecthomas/chroma/quick"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/itchyny/json2yaml"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func NewConfig(root *cobra.Command) {
func NewConfig(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "config",
Short: "Print config",
RunE: func(cmd *cobra.Command, args []string) error {
l := pkgcmd.Logger()
cfg, err := pkgcmd.ReadConfig(l, cmd)
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
out, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
return errors.Wrap(err, "failed to marshal config")
}
fmt.Println(string(out))
return nil
var buf bytes.Buffer
if err := json2yaml.Convert(&buf, bytes.NewBuffer(out)); err != nil {
return errors.Wrap(err, "failed to convert config")
}
return quick.Highlight(os.Stdout, buf.String(), "yaml", "terminal", "monokai")
},
}
root.AddCommand(cmd)
flags := cmd.Flags()
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))
return cmd
}

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

@ -1,20 +1,23 @@
package cmd
import (
"log/slog"
"github.com/foomo/sesamy-cli/cmd/list"
"github.com/spf13/cobra"
)
// NewList represents the list command
func NewList(root *cobra.Command) *cobra.Command {
func NewList(l *slog.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List Google Tag Manager containers",
}
list.NewServer(cmd)
list.NewWeb(cmd)
root.AddCommand(cmd)
cmd.AddCommand(
list.NewWeb(l),
list.NewServer(l),
)
return cmd
}

View File

@ -2,71 +2,69 @@ package list
import (
"bytes"
"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(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 "status":
return dump(tm.Service().Accounts.Containers.Workspaces.GetStatus(tm.WorkspacePath()).Do())
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 "clients":
return dump(tm.Service().Accounts.Containers.Workspaces.Clients.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Clients.List(tm.WorkspacePath()).Context(ctx).Do())
case "tags":
return dump(tm.Service().Accounts.Containers.Workspaces.Tags.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Tags.List(tm.WorkspacePath()).Context(ctx).Do())
case "built-in-variables":
return dump(tm.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(tm.WorkspacePath()).Context(ctx).Do())
case "folders":
return dump(tm.Service().Accounts.Containers.Workspaces.Folders.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Folders.List(tm.WorkspacePath()).Context(ctx).Do())
case "variables":
return dump(tm.Service().Accounts.Containers.Workspaces.Variables.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Variables.List(tm.WorkspacePath()).Context(ctx).Do())
case "templates":
return dump(tm.Service().Accounts.Containers.Workspaces.Templates.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Templates.List(tm.WorkspacePath()).Context(ctx).Do())
case "templates-data":
r, err := tm.Service().Accounts.Containers.Workspaces.Templates.List(tm.WorkspacePath()).Do()
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()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.GtagConfig.List(tm.WorkspacePath()).Context(ctx).Do())
case "triggers":
return dump(tm.Service().Accounts.Containers.Workspaces.Triggers.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Triggers.List(tm.WorkspacePath()).Context(ctx).Do())
case "transformations":
return dump(tm.Service().Accounts.Containers.Workspaces.Transformations.List(tm.WorkspacePath()).Do())
return dump(tm.Service().Accounts.Containers.Workspaces.Transformations.List(tm.WorkspacePath()).Context(ctx).Do())
case "zones":
return dump(tm.Service().Accounts.Containers.Workspaces.Zones.List(tm.WorkspacePath()).Do())
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,13 +1,20 @@
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"
)
// NewServer represents the server command
func NewServer(root *cobra.Command) {
func NewServer(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "server",
Short: "List Google Tag Manager Server Container",
@ -15,6 +22,7 @@ func NewServer(root *cobra.Command) {
ValidArgs: []cobra.Completion{
"built-in-variables",
"clients",
"environments",
"folders",
"gtag-config",
"status",
@ -24,14 +32,14 @@ func NewServer(root *cobra.Command) {
"transformations",
"triggers",
"variables",
"workspaces",
"zones",
},
RunE: func(cmd *cobra.Command, args []string) error {
resource := args[0]
l := pkgcmd.Logger()
l.Info("☕ Listing Server Container resources: " + resource)
cfg, err := pkgcmd.ReadConfig(l, cmd)
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
@ -48,9 +56,30 @@ func NewServer(root *cobra.Command) {
return err
}
return list(l, tm, resource)
if err := tm.EnsureWorkspaceID(cmd.Context()); err != nil {
return err
}
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
},
}
root.AddCommand(cmd)
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

@ -1,19 +1,27 @@
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"
)
// NewWeb represents the web command
func NewWeb(root *cobra.Command) {
func NewWeb(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "web",
Short: "List Google Tag Manager Web Container",
Args: cobra.OnlyValidArgs,
ValidArgs: []cobra.Completion{
"built-in-variables",
"environments",
"folders",
"gtag-config",
"status",
@ -23,14 +31,14 @@ func NewWeb(root *cobra.Command) {
"transformations",
"triggers",
"variables",
"workspaces",
"zones",
},
RunE: func(cmd *cobra.Command, args []string) error {
resource := args[0]
l := pkgcmd.Logger()
l.Info("☕ Listing Web Container resources: " + resource)
cfg, err := pkgcmd.ReadConfig(l, cmd)
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
@ -47,9 +55,30 @@ func NewWeb(root *cobra.Command) {
return err
}
return list(l, tm, resource)
if err := tm.EnsureWorkspaceID(cmd.Context()); err != nil {
return err
}
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
},
}
root.AddCommand(cmd)
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
}

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

@ -1,20 +1,23 @@
package cmd
import (
"log/slog"
"github.com/foomo/sesamy-cli/cmd/provision"
"github.com/spf13/cobra"
)
// NewProvision represents the provision command
func NewProvision(root *cobra.Command) *cobra.Command {
func NewProvision(l *slog.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "provision",
Short: "Provision Google Tag Manager containers",
}
provision.NewServer(cmd)
provision.NewWeb(cmd)
root.AddCommand(cmd)
cmd.AddCommand(
provision.NewServer(l),
provision.NewWeb(l),
)
return cmd
}

View File

@ -1,6 +1,8 @@
package provision
import (
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
conversionlinkerprovider "github.com/foomo/sesamy-cli/pkg/provider/conversionlinker"
criteoprovider "github.com/foomo/sesamy-cli/pkg/provider/criteo"
@ -11,9 +13,13 @@ 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"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/pkg/errors"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
@ -21,12 +27,14 @@ import (
)
// NewServer represents the server command
func NewServer(root *cobra.Command) {
func NewServer(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "server",
Short: "Provision Google Tag Manager Server Container",
RunE: func(cmd *cobra.Command, args []string) error {
l := pkgcmd.Logger()
l := pkgcmd.NewLogger()
l.Info("☕ Provisioning Server Container")
tags, err := cmd.Flags().GetStringSlice("tags")
@ -34,7 +42,7 @@ func NewServer(root *cobra.Command) {
return errors.Wrap(err, "error reading tags flag")
}
cfg, err := pkgcmd.ReadConfig(l, cmd)
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
@ -51,81 +59,99 @@ func NewServer(root *cobra.Command) {
return err
}
if pkgcmd.Tag(googletagprovider.Tag, tags) {
if err := googletagprovider.Server(tm, cfg.GoogleTag); err != nil {
if err := tm.EnsureWorkspaceID(cmd.Context()); err != nil {
return err
}
if utils.Tag(googletagprovider.Tag, tags) {
if err := googletagprovider.Server(cmd.Context(), tm, cfg.GoogleTag); err != nil {
return errors.Wrap(err, "failed to provision google tag provider")
}
}
if pkgcmd.Tag(googletagmanagerprovider.Tag, tags) {
if err := googletagmanagerprovider.Server(tm, cfg.GoogleTagManager, cfg.EnableGeoResolution); err != nil {
if utils.Tag(googletagmanagerprovider.Tag, tags) {
if err := googletagmanagerprovider.Server(cmd.Context(), tm, cfg.GoogleTagManager, cfg.EnableGeoResolution); err != nil {
return errors.Wrap(err, "failed to provision google tag manager")
}
}
if cfg.GoogleAnalytics.Enabled && pkgcmd.Tag(googleanalyticsprovider.Tag, tags) {
if cfg.GoogleAnalytics.Enabled && utils.Tag(googleanalyticsprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", googleanalyticsprovider.Name, "tag", googleanalyticsprovider.Tag)
if err := googleanalyticsprovider.Server(tm, cfg.GoogleAnalytics, cfg.RedactVisitorIP, cfg.EnableGeoResolution); err != nil {
if err := googleanalyticsprovider.Server(cmd.Context(), tm, cfg.GoogleAnalytics, cfg.RedactVisitorIP, cfg.EnableGeoResolution); err != nil {
return errors.Wrap(err, "failed to provision google analytics")
}
}
if cfg.ConversionLinker.Enabled && pkgcmd.Tag(conversionlinkerprovider.Tag, tags) {
if cfg.ConversionLinker.Enabled && utils.Tag(conversionlinkerprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", conversionlinkerprovider.Name, "tag", conversionlinkerprovider.Tag)
if err := conversionlinkerprovider.Server(tm, cfg.ConversionLinker); err != nil {
if err := conversionlinkerprovider.Server(cmd.Context(), tm, cfg.ConversionLinker); err != nil {
return errors.Wrap(err, "failed to provision conversion linker")
}
}
if cfg.Umami.Enabled && pkgcmd.Tag(umamiprovider.Tag, tags) {
if cfg.Umami.Enabled && utils.Tag(umamiprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", umamiprovider.Name, "tag", umamiprovider.Tag)
if err := umamiprovider.Server(tm, cfg.Umami); err != nil {
if err := umamiprovider.Server(cmd.Context(), tm, cfg.Umami); err != nil {
return errors.Wrap(err, "failed to provision umami")
}
}
if cfg.Facebook.Enabled && pkgcmd.Tag(facebookprovider.Tag, tags) {
if cfg.Facebook.Enabled && utils.Tag(facebookprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", facebookprovider.Name, "tag", facebookprovider.Tag)
if err := facebookprovider.Server(l, tm, cfg.Facebook); err != nil {
if err := facebookprovider.Server(cmd.Context(), l, tm, cfg.Facebook); err != nil {
return errors.Wrap(err, "failed to provision facebook")
}
}
if cfg.GoogleAds.Enabled && pkgcmd.Tag(googleadsprovider.Tag, tags) {
if cfg.GoogleAds.Enabled && utils.Tag(googleadsprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", googleadsprovider.Name, "tag", googleadsprovider.Tag)
if err := googleadsprovider.Server(l, tm, cfg.GoogleAds); err != nil {
if err := googleadsprovider.Server(cmd.Context(), l, tm, cfg.GoogleAds); err != nil {
return errors.Wrap(err, "failed to provision google ads")
}
}
if cfg.Emarsys.Enabled && pkgcmd.Tag(emarsysprovider.Tag, tags) {
if cfg.Emarsys.Enabled && utils.Tag(emarsysprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", emarsysprovider.Name, "tag", emarsysprovider.Tag)
if err := emarsysprovider.Server(l, tm, cfg.Emarsys); err != nil {
if err := emarsysprovider.Server(cmd.Context(), l, tm, cfg.Emarsys); err != nil {
return errors.Wrap(err, "failed to provision emarsys")
}
}
if cfg.Tracify.Enabled && pkgcmd.Tag(tracifyprovider.Tag, tags) {
if cfg.Tracify.Enabled && utils.Tag(tracifyprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", tracifyprovider.Name, "tag", tracifyprovider.Tag)
if err := tracifyprovider.Server(l, tm, cfg.Tracify); err != nil {
if err := tracifyprovider.Server(cmd.Context(), l, tm, cfg.Tracify); err != nil {
return errors.Wrap(err, "failed to provision tracify")
}
}
if cfg.Criteo.Enabled && pkgcmd.Tag(criteoprovider.Tag, tags) {
if cfg.Criteo.Enabled && utils.Tag(criteoprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", criteoprovider.Name, "tag", criteoprovider.Tag)
if err := criteoprovider.Server(l, tm, cfg.Criteo); err != nil {
if err := criteoprovider.Server(cmd.Context(), l, tm, cfg.Criteo); err != nil {
return errors.Wrap(err, "failed to provision criteo")
}
}
if cfg.MicrosoftAds.Enabled && pkgcmd.Tag(microsoftadsprovider.Tag, tags) {
if cfg.MicrosoftAds.Enabled && utils.Tag(microsoftadsprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", microsoftadsprovider.Name, "tag", microsoftadsprovider.Tag)
if err := microsoftadsprovider.Server(l, tm, cfg.MicrosoftAds); err != nil {
if err := microsoftadsprovider.Server(cmd.Context(), l, tm, cfg.MicrosoftAds); err != nil {
return errors.Wrap(err, "failed to provision microsoftads")
}
}
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)",
@ -139,7 +165,8 @@ func NewServer(root *cobra.Command) {
}
tree.Children = append(tree.Children, child)
}
if err := pterm.DefaultTree.WithRoot(tree).Render(); err != nil {
if err := pterm.DefaultTree.WithRoot(tree).WithWriter(ptermx.NewWriter(pterm.Warning)).Render(); err != nil {
l.Warn("failed to render missed resources", "error", err)
}
}
@ -148,8 +175,13 @@ func NewServer(root *cobra.Command) {
},
}
cmd.Flags().StringSlice("tags", nil, "list of tags to provision")
_ = viper.BindPFlag("tags", cmd.Flags().Lookup("tags"))
flags := cmd.Flags()
root.AddCommand(cmd)
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))
flags.StringSlice("tags", nil, "list of tags to provision")
_ = c.BindPFlag("tags", flags.Lookup("tags"))
return cmd
}

View File

@ -1,6 +1,8 @@
package provision
import (
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
cookiebotprovider "github.com/foomo/sesamy-cli/pkg/provider/cookiebot"
criteoprovider "github.com/foomo/sesamy-cli/pkg/provider/criteo"
@ -9,7 +11,9 @@ import (
googletagprovider "github.com/foomo/sesamy-cli/pkg/provider/googletag"
googletagmanagerprovider "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
hotjarprovider "github.com/foomo/sesamy-cli/pkg/provider/hotjar"
ptermx "github.com/foomo/sesamy-cli/pkg/pterm"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/foomo/sesamy-cli/pkg/utils"
"github.com/pkg/errors"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
@ -17,12 +21,14 @@ import (
)
// NewWeb represents the web command
func NewWeb(root *cobra.Command) {
func NewWeb(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "web",
Short: "Provision Google Tag Manager Web Container",
RunE: func(cmd *cobra.Command, args []string) error {
l := pkgcmd.Logger()
l := pkgcmd.NewLogger()
l.Info("☕ Provisioning Web Container")
tags, err := cmd.Flags().GetStringSlice("tags")
@ -30,7 +36,7 @@ func NewWeb(root *cobra.Command) {
return errors.Wrap(err, "error reading tags flag")
}
cfg, err := pkgcmd.ReadConfig(l, cmd)
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
@ -47,50 +53,54 @@ func NewWeb(root *cobra.Command) {
return err
}
if pkgcmd.Tag(googletagprovider.Tag, tags) {
if err := tm.EnsureWorkspaceID(cmd.Context()); err != nil {
return err
}
if utils.Tag(googletagprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", googletagprovider.Name, "tag", googletagprovider.Tag)
if err := googletagprovider.Web(tm, cfg.GoogleTag); err != nil {
if err := googletagprovider.Web(cmd.Context(), tm, cfg.GoogleTag); err != nil {
return errors.Wrap(err, "failed to provision google tag provider")
}
}
if pkgcmd.Tag(googletagmanagerprovider.Tag, tags) {
if err := googletagmanagerprovider.Web(tm, cfg.GoogleTagManager); err != nil {
if utils.Tag(googletagmanagerprovider.Tag, tags) {
if err := googletagmanagerprovider.Web(cmd.Context(), tm, cfg.GoogleTagManager); err != nil {
return errors.Wrap(err, "failed to provision google tag manager")
}
}
if cfg.GoogleAnalytics.Enabled && pkgcmd.Tag(googleanaylticsprovider.Tag, tags) {
if cfg.GoogleAnalytics.Enabled && utils.Tag(googleanaylticsprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", googleanaylticsprovider.Name, "tag", googleanaylticsprovider.Tag)
if err := googleanaylticsprovider.Web(tm, cfg.GoogleAnalytics); err != nil {
if err := googleanaylticsprovider.Web(cmd.Context(), tm, cfg.GoogleAnalytics); err != nil {
return errors.Wrap(err, "failed to provision google analytics provider")
}
}
if cfg.Emarsys.Enabled && pkgcmd.Tag(emarsysprovider.Tag, tags) {
if cfg.Emarsys.Enabled && utils.Tag(emarsysprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", emarsysprovider.Name, "tag", emarsysprovider.Tag)
if err := emarsysprovider.Web(tm, cfg.Emarsys); err != nil {
if err := emarsysprovider.Web(cmd.Context(), tm, cfg.Emarsys); err != nil {
return errors.Wrap(err, "failed to provision emarsys provider")
}
}
if cfg.Hotjar.Enabled && pkgcmd.Tag(hotjarprovider.Tag, tags) {
if cfg.Hotjar.Enabled && utils.Tag(hotjarprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", hotjarprovider.Name, "tag", hotjarprovider.Tag)
if err := hotjarprovider.Web(tm, cfg.Hotjar); err != nil {
if err := hotjarprovider.Web(cmd.Context(), tm, cfg.Hotjar); err != nil {
return errors.Wrap(err, "failed to provision hotjar provider")
}
}
if cfg.Criteo.Enabled && pkgcmd.Tag(criteoprovider.Tag, tags) {
if cfg.Criteo.Enabled && utils.Tag(criteoprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", criteoprovider.Name, "tag", criteoprovider.Tag)
if err := criteoprovider.Web(l, tm, cfg.Criteo); err != nil {
if err := criteoprovider.Web(cmd.Context(), l, tm, cfg.Criteo); err != nil {
return errors.Wrap(err, "failed to provision criteo provider")
}
}
if cfg.Cookiebot.Enabled && pkgcmd.Tag(cookiebotprovider.Tag, tags) {
if cfg.Cookiebot.Enabled && utils.Tag(cookiebotprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", cookiebotprovider.Name, "tag", cookiebotprovider.Tag)
if err := cookiebotprovider.Web(tm, cfg.Cookiebot); err != nil {
if err := cookiebotprovider.Web(cmd.Context(), tm, cfg.Cookiebot); err != nil {
return errors.Wrap(err, "failed to provision cookiebot provider")
}
}
@ -108,7 +118,7 @@ func NewWeb(root *cobra.Command) {
}
tree.Children = append(tree.Children, child)
}
if err := pterm.DefaultTree.WithRoot(tree).Render(); err != nil {
if err := pterm.DefaultTree.WithRoot(tree).WithWriter(ptermx.NewWriter(pterm.Warning)).Render(); err != nil {
l.Warn("failed to render missed resources", "error", err)
}
}
@ -117,8 +127,13 @@ func NewWeb(root *cobra.Command) {
},
}
cmd.Flags().StringSlice("tags", nil, "list of tags to provision")
_ = viper.BindPFlag("tags", cmd.Flags().Lookup("tags"))
flags := cmd.Flags()
root.AddCommand(cmd)
flags.StringSliceP("config", "c", []string{"sesamy.yaml"}, "config files (default is sesamy.yaml)")
_ = c.BindPFlag("config", flags.Lookup("config"))
flags.StringSlice("tags", nil, "list of tags to provision")
_ = c.BindPFlag("tags", flags.Lookup("tags"))
return cmd
}

View File

@ -1,44 +1,31 @@
package cmd
import (
"os"
"log/slog"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var root *cobra.Command
func init() {
root = NewRoot()
NewConfig(root)
NewVersion(root)
NewTags(root)
NewList(root)
NewProvision(root)
NewTypeScript(root)
cobra.OnInitialize(pkgcmd.InitConfig)
}
// NewRoot represents the base command when called without any subcommands
func NewRoot() *cobra.Command {
cmd := &cobra.Command{
Use: "sesamy",
Short: "Server Side Tag Management System",
}
cmd.PersistentFlags().BoolP("verbose", "v", false, "output debug information")
_ = viper.BindPFlag("verbose", cmd.PersistentFlags().Lookup("verbose"))
func NewRoot(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "sesamy",
Short: "Server Side Tag Management System",
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
pterm.PrintDebugMessages = viper.GetBool("verbose")
},
}
flags := cmd.PersistentFlags()
flags.BoolP("verbose", "v", false, "output debug information")
_ = c.BindPFlag("verbose", flags.Lookup("verbose"))
cmd.PersistentFlags().StringP("config", "c", "sesamy.yaml", "config file (default is sesamy.yaml)")
_ = viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config"))
return cmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := root.Execute(); err != nil {
os.Exit(1)
}
}

View File

@ -1,6 +1,10 @@
package cmd
import (
"log/slog"
"maps"
"slices"
"github.com/foomo/sesamy-cli/pkg/provider/conversionlinker"
"github.com/foomo/sesamy-cli/pkg/provider/cookiebot"
"github.com/foomo/sesamy-cli/pkg/provider/criteo"
@ -13,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"
@ -20,35 +26,37 @@ import (
)
// NewTags represents the tags command
func NewTags(root *cobra.Command) *cobra.Command {
func NewTags(l *slog.Logger) *cobra.Command {
cmd := &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()
},
}
root.AddCommand(cmd)
return cmd
}

View File

@ -1,6 +1,7 @@
package cmd
import (
"log/slog"
"maps"
"os"
"path"
@ -12,22 +13,25 @@ import (
"github.com/foomo/sesamy-cli/pkg/typescript/generator"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// NewTypeScript represents the typescript command
func NewTypeScript(root *cobra.Command) *cobra.Command {
func NewTypeScript(l *slog.Logger) *cobra.Command {
c := viper.New()
cmd := &cobra.Command{
Use: "typescript",
Short: "Generate typescript events",
RunE: func(cmd *cobra.Command, args []string) error {
l := pkgcmd.Logger()
l := pkgcmd.NewLogger()
cfg, err := pkgcmd.ReadConfig(l, cmd)
cfg, err := pkgcmd.ReadConfig(l, c, cmd)
if err != nil {
return err
}
ctpl, err := contemplate.Load(&cfg.GoogleTag.TypeScript.Config)
ctpl, err := contemplate.Load(cmd.Context(), &cfg.GoogleTag.TypeScript.Config)
if err != nil {
return err
}
@ -57,7 +61,10 @@ func NewTypeScript(root *cobra.Command) *cobra.Command {
},
}
root.AddCommand(cmd)
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

@ -2,13 +2,14 @@ package cmd
import (
"fmt"
"log/slog"
"github.com/spf13/cobra"
)
var version = "latest"
func NewVersion(root *cobra.Command) {
func NewVersion(l *slog.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print version",
@ -17,5 +18,5 @@ func NewVersion(root *cobra.Command) {
},
}
root.AddCommand(cmd)
return cmd
}

108
go.mod
View File

@ -1,87 +1,95 @@
module github.com/foomo/sesamy-cli
go 1.24.0
go 1.25.1
require (
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4
github.com/alecthomas/chroma v0.10.0
github.com/fatih/structtag v1.2.0
github.com/foomo/go v0.0.3
github.com/foomo/gocontemplate v0.1.4
github.com/foomo/sesamy-go v0.8.1
github.com/foomo/gocontemplate v0.2.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/mitchellh/hashstructure/v2 v2.0.2
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4
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.19.0
github.com/stoewer/go-strcase v1.3.0
github.com/stretchr/testify v1.10.0
github.com/wissance/stringFormatter v1.3.0
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
google.golang.org/api v0.222.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.14.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
cloud.google.com/go/compute/metadata v0.6.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.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.4.0 // 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.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.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // 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.2 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // 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.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/sagikazarmark/slog-shim v0.1.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.58.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.26.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/ini.v1 v1.67.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.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
)

235
go.sum
View File

@ -6,12 +6,16 @@ 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.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
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/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.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=
github.com/Code-Hex/go-wordwrap v1.0.0/go.mod h1:/SsbgkY2Q0aPQRyvXcyQwWYTQOIwSORKe6MPjRVGIWU=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
@ -29,54 +33,60 @@ 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=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foomo/go v0.0.3 h1:5pGzcPC78dImuBTT7nsZZnH+GIQUylbCtMkFEH26uZk=
github.com/foomo/go v0.0.3/go.mod h1:x6g64wiQusqaFElnh5rlk9unCgLKmfUWy0YFLejJxio=
github.com/foomo/gocontemplate v0.1.4 h1:MYSsoltno9pNMU5NwALd7PNSQ1XjN1tpqMGWAhCUETc=
github.com/foomo/gocontemplate v0.1.4/go.mod h1:BH8eODDwlqWhasSl86avbtMN3Zfgt4pWwr8/PBPM5v0=
github.com/foomo/gocontemplate v0.2.0 h1:YbgNUwZ9mK9Wj05cGVTPamCpMuxlaL4qegkbZv2eqOY=
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.8.1 h1:5m1ySZB5gW9Dymz8MFggU8DXs7F1AAcV6WW2ieX9DPI=
github.com/foomo/sesamy-go v0.8.1/go.mod h1:9TlGLPABYmjt/louonKy4Na4nmx9RHq7N9KyZ6Sy7Xw=
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.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
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.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
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/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.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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
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=
@ -90,6 +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.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=
@ -99,19 +119,22 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY=
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
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/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.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=
@ -124,34 +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/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
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=
@ -161,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.3.0 h1:BVl9tEvS/JgE2YCE3voaBDe3F4SB8sGrH2E3VUzX3I0=
github.com/wissance/stringFormatter v1.3.0/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=
@ -175,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.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
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.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
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.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
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.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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=
@ -223,46 +247,49 @@ 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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
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.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
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=
google.golang.org/api v0.222.0 h1:Aiewy7BKLCuq6cUCeOUrsAlzjXPqBkEeQ/iwGHVQa/4=
google.golang.org/api v0.222.0/go.mod h1:efZia3nXpWELrwMlN5vyQrD4GmJN1Vw0x68Et3r+a9c=
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-20250212204824-5a70512c5d8b h1:FQtJ1MxbXoIIrZHZ33M+w5+dAP9o86rgpjoKr/ZmT7k=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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=
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=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

50
main.go
View File

@ -1,7 +1,53 @@
package main
import "github.com/foomo/sesamy-cli/cmd"
import (
"fmt"
"os"
"runtime/debug"
"strings"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
"github.com/foomo/sesamy-cli/cmd"
cmdx "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/pkg/errors"
)
func main() {
cmd.Execute()
l := cmdx.NewLogger()
root := cmd.NewRoot(l)
root.AddCommand(
cmd.NewConfig(l),
cmd.NewList(l),
cmd.NewDiff(l),
cmd.NewOpen(l),
cmd.NewProvision(l),
cmd.NewTags(l),
cmd.NewTypeScript(l),
cmd.NewVersion(l),
)
say := func(msg string) string {
if say, cerr := cowsay.Say(msg, cowsay.BallonWidth(80)); cerr == nil {
msg = say
}
return msg
}
code := 0
defer func() {
if r := recover(); r != nil {
l.Error(say("It's time to panic"))
l.Error(fmt.Sprintf("%v", r))
l.Error(string(debug.Stack()))
code = 1
}
os.Exit(code)
}()
if err := root.Execute(); err != nil {
l.Error(say(strings.Split(errors.Cause(err).Error(), ":")[0]))
l.Error(err.Error())
code = 1
}
}

View File

@ -1,57 +1,47 @@
package cmd
import (
"bytes"
"io"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/mitchellh/mapstructure"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/rawbytes"
"github.com/knadh/koanf/v2"
"github.com/pkg/errors"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// InitConfig reads in config file and ENV variables if set.
func InitConfig() {
filename := viper.GetString("config")
func ReadConfig(l *slog.Logger, c *viper.Viper, cmd *cobra.Command) (*config.Config, error) {
k := koanf.NewWithConf(koanf.Conf{
Delim: "/",
})
viper.SetConfigType("yaml")
if filename == "-" {
// do nothing
} else if filename != "" {
// Use config file from the flag.
viper.SetConfigFile(filename)
} else {
// Search config in home directory with name ".sesamy" (without extension).
viper.AddConfigPath(".")
viper.SetConfigName("sesamy")
}
}
func ReadConfig(l *slog.Logger, cmd *cobra.Command) (*config.Config, error) {
filename := viper.GetString("config")
if filename == "-" {
l.Debug("using config from stdin")
b, err := io.ReadAll(cmd.InOrStdin())
if err != nil {
return nil, err
for _, filename := range c.GetStringSlice("config") {
var p koanf.Provider
if filename == "-" {
pterm.Debug.Println("reading config from stdin")
if b, err := io.ReadAll(cmd.InOrStdin()); err != nil {
return nil, err
} else {
p = rawbytes.Provider(b)
}
} else {
pterm.Debug.Println("reading config from filename: " + filename)
p = file.Provider(filename)
}
if err := viper.ReadConfig(bytes.NewBuffer(b)); err != nil {
return nil, err
}
} else {
l.Debug("using config file", "filename", viper.ConfigFileUsed())
if err := viper.ReadInConfig(); err != nil {
return nil, err
if err := k.Load(p, yaml.Parser()); err != nil {
return nil, errors.Wrap(err, "error loading config file: "+filename)
}
}
// l.Debug("config", l.ArgsFromMap(viper.AllSettings()))
var cfg *config.Config
if err := viper.Unmarshal(&cfg, func(decoderConfig *mapstructure.DecoderConfig) {
decoderConfig.TagName = "yaml"
pterm.Debug.Println("unmarshalling config")
if err := k.UnmarshalWithConf("", &cfg, koanf.UnmarshalConf{
Tag: "yaml",
}); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal config")
}

View File

@ -2,22 +2,36 @@ package cmd
import (
"log/slog"
"os"
ptermx "github.com/foomo/sesamy-cli/pkg/pterm"
"github.com/pterm/pterm"
"github.com/spf13/viper"
)
func Logger() *slog.Logger {
verbose := viper.GetBool("verbose")
func init() {
pterm.Info.Prefix.Text = "⎈"
pterm.Info.Scope.Style = &pterm.ThemeDefault.DebugMessageStyle
pterm.Debug.Prefix.Text = "⛏︎"
pterm.Debug.Scope.Style = &pterm.ThemeDefault.DebugMessageStyle
pterm.Fatal.Prefix.Text = "⛔︎"
pterm.Fatal.Scope.Style = &pterm.ThemeDefault.DebugMessageStyle
pterm.Error.Prefix.Text = "⛌"
pterm.Error.Scope.Style = &pterm.ThemeDefault.DebugMessageStyle
pterm.Warning.Prefix.Text = "⚠"
pterm.Warning.Scope.Style = &pterm.ThemeDefault.DebugMessageStyle
pterm.Success.Prefix.Text = "✓"
pterm.Success.Scope.Style = &pterm.ThemeDefault.DebugMessageStyle
plogger := pterm.DefaultLogger.WithTime(false)
if verbose {
plogger = plogger.WithLevel(pterm.LogLevelTrace).WithCaller(true)
if scope := os.Getenv("SESAMY_SCOPE"); scope != "" {
pterm.Info.Scope.Text = scope
pterm.Debug.Scope.Text = scope
pterm.Fatal.Scope.Text = scope
pterm.Error.Scope.Text = scope
pterm.Warning.Scope.Text = scope
pterm.Success.Scope.Text = scope
}
// Create a new slog handler with the default PTerm logger
handler := pterm.NewSlogHandler(plogger)
// Create a new slog logger with the handler
return slog.New(handler)
}
func NewLogger() *slog.Logger {
return slog.New(ptermx.NewSlogHandler())
}

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

@ -2,10 +2,17 @@ package config
type Cookiebot struct {
// Enable provider
Enabled bool `json:"enabled" yaml:"enabled"`
TemplateName string `json:"templateName" yaml:"templateName"`
CookiebotID string `json:"cookiebotId" yaml:"cookiebotId"`
CDNRegion string `json:"cdnRegion" yaml:"cdnRegion"`
URLPassthrough bool `json:"urlPassthrough" yaml:"urlPassthrough"`
AdvertiserConsentModeEnabled bool `json:"advertiserConsentModeEnabled" yaml:"advertiserConsentModeEnabled"`
Enabled bool `json:"enabled" yaml:"enabled"`
// Name of the manually installed Cookiebot CMP tag template
TemplateName string `json:"templateName" yaml:"templateName"`
// Create an account on Cookiebot.com and copy 'Domain Group ID' from the tab 'Your Scripts' in Cookiebot
CookiebotID string `json:"cookiebotId" yaml:"cookiebotId"`
// Select which CDN region Cookiebot uses
CDNRegion string `json:"cdnRegion" yaml:"cdnRegion"`
// When using URL passthrough, a few query parameters may be appended to links as users navigate through pages on your website
URLPassthrough bool `json:"urlPassthrough" yaml:"urlPassthrough"`
// If enabled, Google will deduce ad_storage, ad_user_data and ad_personalization data from the TC string.
AdvertiserConsentModeEnabled bool `json:"advertiserConsentModeEnabled" yaml:"advertiserConsentModeEnabled"`
// Default Consent state
RegionSettings []CookiebotRegionSetting `json:"regionSettings" yaml:"regionSettings"`
}

View File

@ -0,0 +1,16 @@
package config
type CookiebotRegionSetting struct {
// Region (leave blank to apply globally)
Region string `json:"region" yaml:"region"`
// Default consent for functionality_storage and personalization_storage
Preferences string `json:"preferences" yaml:"preferences"`
// Default consent for analytics_storage
Statistics string `json:"statistics" yaml:"statistics"`
// Default consent for ad_storage
Marketing string `json:"marketing" yaml:"marketing"`
// Default consent ad_user_data
AdUserData string `json:"adUserData" yaml:"adUserData"`
// Default consent ad_personalization
AdPersonalization string `json:"adPersonalization" yaml:"adPersonalization"`
}

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,10 +7,16 @@ 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
GoogleGTagJSOverride GoogleAnalyticsGTagJSOverride `json:"googleGTagJSOverride" yaml:"googleGTagJSOverride"`
// Enable mpv2 user data transformation (experimental)
EnableMPv2UserDataTransformation bool `json:"enableMPv2UserDataTransformation" yaml:"enableMPv2UserDataTransformation"`
// Google Tag Manager web container settings
WebContainer contemplate.Config `json:"webContainer" yaml:"webContainer"`
// Google Tag Manager server container settings

View File

@ -5,6 +5,8 @@ type GoogleTag struct {
TagID string `json:"tagId" yaml:"tagId"`
// Whether a page_view should be sent on initial load
SendPageView bool `json:"sendPageView" yaml:"sendPageView"`
// Optional custom server container url
ServerContainerURL string `json:"serverContainerUrl" yaml:"serverContainerUrl"`
// Data layer variables to be added to the event settings
DataLayerVariables map[string]string `json:"dataLayerVariables" yaml:"dataLayerVariables"`
// TypeScript settings

View File

@ -5,6 +5,8 @@ type GoogleTagManagerContainer struct {
TagID string `json:"tagId" yaml:"tagId"`
// The container id
ContainerID string `json:"containerId" yaml:"containerId"`
// The workspace id that should be used by the api
// (Optional) The workspace id that should be used by the api
WorkspaceID string `json:"workspaceId" yaml:"workspaceId"`
// (Optional) The workspace name that should be used by the api
Workspace string `json:"workspace" yaml:"workspace"`
}

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

@ -1,6 +1,8 @@
package conversionlinker
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
containertag "github.com/foomo/sesamy-cli/pkg/provider/conversionlinker/server/tag"
"github.com/foomo/sesamy-cli/pkg/provider/conversionlinker/server/trigger"
@ -10,33 +12,30 @@ import (
"github.com/pkg/errors"
)
func Server(tm *tagmanager.TagManager, cfg config.ConversionLinker) error {
{
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.ConversionLinker) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
var eventTriggerOpts []trigger.ConversionLinkerEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.ConversionLinkerEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewConversionLinkerEvent(NameConversionLinkerTrigger, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewConversionLinkerEvent(NameConversionLinkerTrigger, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+NameConversionLinkerTrigger)
}
if _, err := tm.UpsertTag(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

@ -1,28 +1,27 @@
package cookiebot
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/provider/cookiebot/web/tag"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/pkg/errors"
)
func Web(tm *tagmanager.TagManager, cfg config.Cookiebot) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.Cookiebot) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // create event tags
temmplate, err := tm.LookupTemplate(cfg.TemplateName)
temmplate, err := tm.LookupTemplate(ctx, cfg.TemplateName)
if err != nil {
return errors.Wrapf(err, "Failed to lookup `%s`, please install the `%s` gallery tag template first (%s)", cfg.TemplateName, "Cookiebot CMP", "https://tagmanager.google.com/gallery/#/owners/cybotcorp/templates/gtm-templates-cookiebot-cmp")
}
if _, err := tm.UpsertTag(tag.NewCookiebotInitialization(NameCookiebotTag, cfg, temmplate)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, tag.NewCookiebotInitialization(NameCookiebotTag, cfg, temmplate)); err != nil {
return err
}
}

View File

@ -10,62 +10,109 @@ import (
)
func NewCookiebotInitialization(name string, cfg config.Cookiebot, template *tagmanager.CustomTemplate) *tagmanager.Tag {
parameter := []*tagmanager.Parameter{
{
Key: "adsDataRedaction",
Type: "template",
Value: "dynamic",
},
{
Key: "addGeoRegion",
Type: "boolean",
Value: "false",
},
{
Key: "serial",
Type: "template",
Value: cfg.CookiebotID,
},
{
Key: "iabFramework",
Type: "boolean",
Value: "false",
},
{
Key: "cdnRegion",
Type: "template",
Value: cfg.CDNRegion,
},
{
Key: "advertiserConsentModeEnabled",
Type: "boolean",
Value: "true",
},
{
Key: "language",
Type: "template",
Value: "auto",
},
{
Key: "urlPassthrough",
Type: "boolean",
Value: strconv.FormatBool(cfg.URLPassthrough),
},
{
Key: "consentModeEnabled",
Type: "boolean",
Value: "true",
},
{
Key: "waitForUpdate",
Type: "template",
Value: "2000",
},
}
if len(cfg.RegionSettings) > 0 {
param := &tagmanager.Parameter{
Key: "regionSettings",
Type: "list",
}
for _, setting := range cfg.RegionSettings {
param.List = append(param.List, &tagmanager.Parameter{
Map: []*tagmanager.Parameter{
{
Key: "region",
Type: "template",
Value: setting.Region,
},
{
Key: "defaultConsentPreferences",
Type: "template",
Value: setting.Preferences,
},
{
Key: "defaultConsentStatistics",
Type: "template",
Value: setting.Statistics,
},
{
Key: "defaultConsentMarketing",
Type: "template",
Value: setting.Marketing,
},
{
Key: "defaultConsentMarketingAdUserData",
Type: "template",
Value: setting.AdUserData,
},
{
Key: "defaultConsentMarketingAdPersonalization",
Type: "template",
Value: setting.AdPersonalization,
},
},
Type: "map",
})
}
parameter = append(parameter, param)
}
return &tagmanager.Tag{
Name: name,
FiringTriggerId: []string{trigger.IDConsentInitializtion},
TagFiringOption: "oncePerEvent",
Parameter: []*tagmanager.Parameter{
{
Key: "adsDataRedaction",
Type: "template",
Value: "dynamic",
},
{
Key: "addGeoRegion",
Type: "boolean",
Value: "false",
},
{
Key: "serial",
Type: "template",
Value: cfg.CookiebotID,
},
{
Key: "iabFramework",
Type: "boolean",
Value: "false",
},
{
Key: "cdnRegion",
Type: "template",
Value: cfg.CDNRegion,
},
{
Key: "advertiserConsentModeEnabled",
Type: "boolean",
Value: "true",
},
{
Key: "language",
Type: "template",
Value: "auto",
},
{
Key: "urlPassthrough",
Type: "boolean",
Value: strconv.FormatBool(cfg.URLPassthrough),
},
{
Key: "consentModeEnabled",
Type: "boolean",
Value: "true",
},
{
Key: "waitForUpdate",
Type: "template",
Value: "2000",
},
},
Type: utils.TemplateType(template),
Parameter: parameter,
Type: utils.TemplateType(template),
}
}

View File

@ -1,6 +1,7 @@
package criteo
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
@ -14,40 +15,37 @@ import (
"github.com/pkg/errors"
)
func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Criteo) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Criteo) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
template, err := tm.LookupTemplate(NameCriteoEventsAPITemplate)
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
}
{ // create tags
callerID, err := tm.UpsertVariable(commonvariable.NewConstant(NameCallerID, cfg.CallerID))
callerID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameCallerID, cfg.CallerID))
if err != nil {
return err
}
partnerID, err := tm.UpsertVariable(commonvariable.NewConstant(NamePartnerID, cfg.PartnerID))
partnerID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NamePartnerID, cfg.PartnerID))
if err != nil {
return err
}
applicationID, err := tm.UpsertVariable(commonvariable.NewConstant(NameApplicationID, cfg.ApplicationID))
applicationID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameApplicationID, cfg.ApplicationID))
if err != nil {
return err
}
eventParameters, err := utils.LoadEventParams(cfg.ServerContainer)
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer)
if err != nil {
return err
}
@ -55,22 +53,22 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Criteo) error
for event := range eventParameters {
var eventTriggerOpts []trigger.CriteoEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.CriteoEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewCriteoEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewCriteoEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(servertagx.NewEventsAPITag(event, callerID, partnerID, applicationID, template, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewEventsAPITag(event, callerID, partnerID, applicationID, template, eventTrigger)); err != nil {
return err
}
}

View File

@ -1,6 +1,7 @@
package criteo
import (
"context"
"errors"
"log/slog"
@ -10,16 +11,13 @@ import (
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
)
func Web(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Criteo) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Criteo) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
template, err := tm.LookupTemplate(NameCriteoUserIdentificationTemplate)
template, err := tm.LookupTemplate(ctx, NameCriteoUserIdentificationTemplate)
if err != nil {
if errors.Is(err, tagmanager.ErrNotFound) {
l.Warn("Please install the 'Criteo User Identification' template manually first")
@ -28,17 +26,17 @@ func Web(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Criteo) error {
}
{ // setup criteo
callerID, err := tm.UpsertVariable(commonvariable.NewConstant(NameCallerID, cfg.CallerID))
callerID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameCallerID, cfg.CallerID))
if err != nil {
return err
}
partnerID, err := tm.UpsertVariable(commonvariable.NewConstant(NamePartnerID, cfg.PartnerID))
partnerID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NamePartnerID, cfg.PartnerID))
if err != nil {
return err
}
if _, err = tm.UpsertTag(client.NewUserIdentification(NameCriteoUserIdentificationTag, callerID, partnerID, template)); err != nil {
if _, err = tm.UpsertTag(ctx, folder, client.NewUserIdentification(NameCriteoUserIdentificationTag, callerID, partnerID, template)); err != nil {
return err
}
}

View File

@ -1,6 +1,7 @@
package emarsys
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
@ -16,38 +17,35 @@ import (
"github.com/pkg/errors"
)
func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Emarsys) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Emarsys) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // conversion
merchantID, err := tm.UpsertVariable(commonvariable.NewConstant(NameMerchantIDConstant, cfg.MerchantID))
merchantID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameMerchantIDConstant, cfg.MerchantID))
if err != nil {
return err
}
tagTemplate, err := tm.UpsertCustomTemplate(template.NewEmarsysWebExtendTag(NameServerEmarsysWebExtendTagTemplate))
tagTemplate, err := tm.UpsertCustomTemplate(ctx, template.NewEmarsysWebExtendTag(NameServerEmarsysWebExtendTagTemplate))
if err != nil {
return err
}
clientTemplate, err := tm.UpsertCustomTemplate(template.NewEmarsysInitializationClient(NameServerEmarsysInitalizationClientTemplate))
clientTemplate, err := tm.UpsertCustomTemplate(ctx, template.NewEmarsysInitializationClient(NameServerEmarsysInitalizationClientTemplate))
if err != nil {
return err
}
_, err = tm.UpsertClient(serverclientx.NewEmarsys(NameServerEmarsysClient, cfg, clientTemplate))
_, err = tm.UpsertClient(ctx, folder, serverclientx.NewEmarsys(NameServerEmarsysClient, cfg, clientTemplate))
if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.ServerContainer)
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer)
if err != nil {
return err
}
@ -55,22 +53,22 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Emarsys) error
for event := range eventParameters {
var eventTriggerOpts []trigger.EmarsysEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.EmarsysEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewEmarsysEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewEmarsysEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(servertagx.NewEmarsys(event, merchantID, cfg.TestMode, cfg.DebugMode, tagTemplate, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewEmarsys(event, merchantID, cfg.TestMode, cfg.DebugMode, tagTemplate, eventTrigger)); err != nil {
return err
}
}

View File

@ -79,7 +79,7 @@ const headers = {
'user-agent': getRequestHeader('user-agent'),
};
let query = ['xp=1', 'cp=1'];
let query = ['xp=1', 'cp=1', 'test=true'];
if (sessionId) query.push('s='+encodeUriComponent(sessionId));
if (visitorId) query.push('vi='+encodeUriComponent(visitorId));
if (pageViewId) query.push('pv='+encodeUriComponent(pageViewId));

View File

@ -81,6 +81,7 @@ ___SANDBOXED_JS_FOR_SERVER___
const Math = require('Math');
const JSON = require('JSON');
const parseUrl = require('parseUrl');
const setCookie = require('setCookie');
const sendHttpGet = require('sendHttpGet');
const setResponseBody = require('setResponseBody');
@ -91,6 +92,7 @@ const getRequestHeader = require('getRequestHeader');
const getCookieValues = require('getCookieValues');
const generateRandom = require('generateRandom');
const logToConsole = require('logToConsole');
const createRegex = require('createRegex');
// --- Config ---
@ -111,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) => {
@ -142,6 +144,7 @@ function mapEventData() {
referrer: eventData.page_referrer || null,
orderId: null,
order: null,
search: null,
category: null,
view: null,
cart: null,
@ -150,25 +153,31 @@ function mapEventData() {
switch (eventData.event_name) {
case 'page_view': {
mappedData.cart = serializeItems(eventData.items || []);
mappedData.search = ((parseUrl(eventData.page_location) || {}).searchParams || {}).q || null;
break;
}
case 'view_item': {
mappedData.cart = serializeItems(eventData.items || []);
mappedData.view = serializeItem(eventData.items[0] || {}, false);
break;
}
case 'view_item_list': {
mappedData.category = eventData.item_list_id;
const prefix = createRegex('^/', 'i');
const seperator = createRegex('/', 'i');
mappedData.cart = serializeItems(eventData.items || []);
mappedData.category = eventData.item_list_id.replace(prefix,"").replace(seperator, " > ");
break;
}
case 'purchase': {
mappedData.cart = [];
mappedData.orderId = eventData.transaction_id;
mappedData.order = serializeItems(eventData.items || []);
if (eventData.tax) {
mappedData.order[0].price += eventData.tax;
}
if (eventData.shipping) {
mappedData.order[0].price += eventData.shipping;
}
if (eventData.tax) {
mappedData.order[0].price += eventData.tax;
}
if (eventData.shipping) {
mappedData.order[0].price += eventData.shipping;
}
break;
}
}
@ -208,8 +217,7 @@ function serializeData(mappedData) {
}
if (mappedData.email) {
slist.push("eh=" + encodeUriComponent(mappedData.email));
}
if (mappedData.customerId) {
} else if (mappedData.customerId) {
slist.push("ci=" + encodeUriComponent(mappedData.customerId));
}
if (mappedData.sessionId) {
@ -234,6 +242,9 @@ function serializeData(mappedData) {
slist.push("ca=" + encodeUriComponent(mappedData.cart));
slist.push("cv=1");
}
if (mappedData.search) {
slist.push("q=" + encodeUriComponent(mappedData.search));
}
if (mappedData.referrer) {
slist.push("prev_url=" + encodeUriComponent(mappedData.referrer));
}

View File

@ -1,6 +1,8 @@
package emarsys
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/provider/emarsys/web/tag"
"github.com/foomo/sesamy-cli/pkg/provider/emarsys/web/template"
@ -12,55 +14,52 @@ import (
"github.com/pkg/errors"
)
func Web(tm *tagmanager.TagManager, cfg config.Emarsys) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.Emarsys) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // create initialization tag
tagTemplate, err := tm.UpsertCustomTemplate(template.NewEmarsysInitializationTag(NameWebEmarsysInitalizationTagTemplate))
tagTemplate, err := tm.UpsertCustomTemplate(ctx, template.NewEmarsysInitializationTag(NameWebEmarsysInitalizationTagTemplate))
if err != nil {
return err
}
if _, err = tm.UpsertTag(tag.NewEmarsysInitialization(NameWebEmarsysInitalizationTag, tagTemplate)); err != nil {
if _, err = tm.UpsertTag(ctx, folder, tag.NewEmarsysInitialization(NameWebEmarsysInitalizationTag, tagTemplate)); err != nil {
return err
}
}
if _, err := googletag.CreateWebDatalayerVariables(tm, map[string]string{
if _, err := googletag.CreateWebDatalayerVariables(ctx, tm, map[string]string{
"emarsys_page_view_id": "emarsys.page_view_id",
}); err != nil {
return err
}
{ // create event tags
tagID, err := tm.LookupVariable(googletag.NameGoogleTagID)
tagID, err := tm.LookupVariable(ctx, googletag.NameGoogleTagID)
if err != nil {
return err
}
eventParameters, err := googletag.CreateWebEventTriggers(tm, cfg.WebContainer)
eventParameters, err := googletag.CreateWebEventTriggers(ctx, tm, cfg.WebContainer)
if err != nil {
return err
}
for event := range eventParameters {
eventTrigger, err := tm.LookupTrigger(commontrigger.EventName(event))
eventTrigger, err := tm.LookupTrigger(ctx, commontrigger.EventName(event))
if err != nil {
return errors.Wrap(err, "failed to lookup event trigger: "+event)
}
eventSettings, err := tm.LookupVariable(commonvariable.GoogleTagEventSettingsName(event))
eventSettings, err := tm.LookupVariable(ctx, commonvariable.GoogleTagEventSettingsName(event))
if err != nil {
return errors.Wrap(err, "failed to lookup google tag event setting: "+event)
}
if _, err := tm.UpsertTag(containertag.NewGoogleAnalyticsEvent(event, tagID, eventSettings, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, containertag.NewGoogleAnalyticsEvent(event, tagID, eventSettings, eventTrigger)); err != nil {
return err
}
}

View File

@ -1,6 +1,7 @@
package facebook
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
@ -12,42 +13,44 @@ 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(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Facebook) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Facebook) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
pixelID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NamePixelIDConstant, cfg.PixelID))
if err != nil {
return err
}
apiAccessToken, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameAPIAcessTokenConstant, cfg.APIAccessToken))
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
} else {
tm.SetFolderName(folder.Name)
}
}
pixelID, err := tm.UpsertVariable(commonvariable.NewConstant(NamePixelIDConstant, cfg.PixelID))
if err != nil {
template, err := tm.LookupTemplate(ctx, NameConversionsAPITagTemplate)
if errors.Is(err, tagmanager.ErrNotFound) {
l.Warn("Please install the 'Conversions API Tag' by 'facebookincubator' template manually first")
return err
}
apiAccessToken, err := tm.UpsertVariable(commonvariable.NewConstant(NameAPIAcessTokenConstant, cfg.APIAccessToken))
if err != nil {
return err
}
testEventToken, err := tm.UpsertVariable(commonvariable.NewConstant(NameTestEventTokenConstant, cfg.TestEventToken))
if err != nil {
return err
}
template, err := tm.LookupTemplate(NameConversionsAPITagTemplate)
if err != nil {
if errors.Is(err, tagmanager.ErrNotFound) {
l.Warn("Please install the 'Conversion API' template manually first")
}
} else if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.ServerContainer.Config)
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer.Config)
if err != nil {
return err
}
@ -55,22 +58,22 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Facebook) erro
for event := range eventParameters {
var eventTriggerOpts []trigger.FacebookEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.FacebookEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewFacebookEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewFacebookEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(servertagx.NewConversionsAPITag(event, pixelID, apiAccessToken, testEventToken, cfg.ServerContainer.Setting(event), template, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewConversionsAPITag(event, pixelID, apiAccessToken, testEventToken, cfg.ServerContainer.Setting(event), template, eventTrigger)); err != nil {
return err
}
}

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

@ -1,6 +1,7 @@
package googleads
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
@ -8,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"
@ -15,34 +17,36 @@ import (
"github.com/pkg/errors"
)
func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.GoogleAds) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.GoogleAds) error {
folder, err := Folder(ctx, tm)
if err != nil {
return err
}
conversionID, err := tm.UpsertVariable(commonvariable.NewConstant(NameConversionIDConstant, cfg.ConversionID))
gtmFolder, err := googletagmanager.Folder(ctx, tm)
if err != nil {
return err
}
conversionID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameConversionIDConstant, cfg.ConversionID))
if err != nil {
return err
}
// conversion
if cfg.Conversion.Enabled {
value, err := tm.UpsertVariable(variable.NewEventData("value"))
value, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData("value"))
if err != nil {
return err
}
currency, err := tm.UpsertVariable(variable.NewEventData("currency"))
currency, err := tm.UpsertVariable(ctx, gtmFolder, variable.NewEventData("currency"))
if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.Conversion.ServerContainer.Config)
eventParameters, err := utils.LoadEventParams(ctx, cfg.Conversion.ServerContainer.Config)
if err != nil {
return err
}
@ -50,23 +54,25 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.GoogleAds) err
for event := range eventParameters {
var eventTriggerOpts []trigger.GoogleAdsEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.GoogleAdsEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewGoogleAdsEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewGoogleAdsEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(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
}
}
}
}
@ -75,22 +81,22 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.GoogleAds) err
if cfg.Remarketing.Enabled {
var eventTriggerOpts []trigger.GoogleAdsRemarketingEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.GoogleAdsRemarketingEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewGoogleAdsRemarketingEvent(NameGoogleAdsRemarketingTrigger, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewGoogleAdsRemarketingEvent(NameGoogleAdsRemarketingTrigger, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+NameGoogleAdsRemarketingTrigger)
}
if _, err := tm.UpsertTag(servertagx.NewGoogleAdsRemarketing(NameGoogleAdsRemarketingTag, conversionID, cfg.Remarketing, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewGoogleAdsRemarketing(NameGoogleAdsRemarketingTag, conversionID, cfg.Remarketing, 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

@ -1,6 +1,8 @@
package googleanalytics
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
googleanalyticsclient "github.com/foomo/sesamy-cli/pkg/provider/googleanalytics/server/client"
containertag "github.com/foomo/sesamy-cli/pkg/provider/googleanalytics/server/tag"
@ -20,77 +22,76 @@ import (
api "google.golang.org/api/tagmanager/v2"
)
func Server(tm *tagmanager.TagManager, cfg config.GoogleAnalytics, redactVisitorIP, enableGeoResolution bool) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleAnalytics, redactVisitorIP, enableGeoResolution bool) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // create clients
{
measurementID, err := tm.LookupVariable(googletag.NameGoogleTagMeasurementID)
measurementID, err := tm.LookupVariable(ctx, googletag.NameGoogleTagMeasurementID)
if err != nil {
return err
}
visitorRegion, err := tm.LookupVariable(googletagmanager.NameGoogleTagManagerVisitorRegion)
visitorRegion, err := tm.LookupVariable(ctx, googletagmanager.NameGoogleTagManagerVisitorRegion)
if err != nil {
return err
}
client, err := tm.UpsertClient(googleanalyticsclient.NewGoogleAnalyticsGA4(NameGoogleAnalyticsGA4Client, enableGeoResolution, visitorRegion, measurementID))
client, err := tm.UpsertClient(ctx, folder, googleanalyticsclient.NewGoogleAnalyticsGA4(NameGoogleAnalyticsGA4Client, enableGeoResolution, visitorRegion, measurementID))
if err != nil {
return err
}
if _, err = tm.UpsertTrigger(servertrigger.NewClient(NameGoogleAnalyticsGA4ClientTrigger, client)); err != nil {
if _, err = tm.UpsertTrigger(ctx, folder, servertrigger.NewClient(NameGoogleAnalyticsGA4ClientTrigger, client)); err != nil {
return err
}
}
{
client, err := tm.UpsertClient(googleanalyticsclient.NewMeasurementProtocolGA4(NameMeasurementProtocolGA4Client))
client, err := tm.UpsertClient(ctx, folder, googleanalyticsclient.NewMeasurementProtocolGA4(NameMeasurementProtocolGA4Client))
if err != nil {
return err
}
if _, err = tm.UpsertTrigger(servertrigger.NewClient(NameMeasurementProtocolGA4ClientTrigger, client)); err != nil {
if _, err = tm.UpsertTrigger(ctx, folder, servertrigger.NewClient(NameMeasurementProtocolGA4ClientTrigger, client)); err != nil {
return err
}
userDataTemplate, err := tm.UpsertCustomTemplate(servertemplate.NewJSONRequestValue(NameJSONRequestValueTemplate))
if err != nil {
return err
}
if cfg.EnableMPv2UserDataTransformation {
userDataTemplate, err := tm.UpsertCustomTemplate(ctx, servertemplate.NewJSONRequestValue(NameJSONRequestValueTemplate))
if err != nil {
return err
}
userDataVariable, err := tm.UpsertVariable(servervariable.NewMPv2Data("user_data", userDataTemplate))
if err != nil {
return err
}
userDataVariable, err := tm.UpsertVariable(ctx, folder, servervariable.NewMPv2Data("user_data", userDataTemplate))
if err != nil {
return err
}
debugModeVariable, err := tm.UpsertVariable(servervariable.NewMPv2Data("debug_mode", userDataTemplate))
if err != nil {
return err
}
debugModeVariable, err := tm.UpsertVariable(ctx, folder, servervariable.NewMPv2Data("debug_mode", userDataTemplate))
if err != nil {
return err
}
_, err = tm.UpsertTransformation(servertransformation.NewMPv2UserData(NameMPv2UserDataTransformation, map[string]*api.Variable{
"user_data": userDataVariable,
"debug_mode": debugModeVariable,
}, client))
if err != nil {
return err
_, err = tm.UpsertTransformation(ctx, folder, servertransformation.NewMPv2UserData(NameMPv2UserDataTransformation, map[string]*api.Variable{
"user_data": userDataVariable,
"debug_mode": debugModeVariable,
}, client))
if err != nil {
return err
}
}
}
if cfg.GoogleGTagJSOverride.Enabled {
template, err := tm.UpsertCustomTemplate(googleanalyticstemplate.NewGoogleGTagClient(NameGoogleGTagClientTemplate))
template, err := tm.UpsertCustomTemplate(ctx, googleanalyticstemplate.NewGoogleGTagClient(NameGoogleGTagClientTemplate))
if err != nil {
return err
}
_, err = tm.UpsertClient(googleanalyticsclient.NewGoogleGTag(NameGoogleGTagClient, cfg.GoogleGTagJSOverride, template))
_, err = tm.UpsertClient(ctx, folder, googleanalyticsclient.NewGoogleGTag(NameGoogleGTagClient, cfg.GoogleGTagJSOverride, template))
if err != nil {
return err
}
@ -98,7 +99,7 @@ func Server(tm *tagmanager.TagManager, cfg config.GoogleAnalytics, redactVisitor
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.ServerContainer)
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer)
if err != nil {
return err
}
@ -106,22 +107,22 @@ func Server(tm *tagmanager.TagManager, cfg config.GoogleAnalytics, redactVisitor
for event := range eventParameters {
var eventTriggerOpts []trigger.GoogleAnalyticsEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.GoogleAnalyticsEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewGoogleAnalyticsEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewGoogleAnalyticsEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(containertag.NewGoogleAnalytics(event, redactVisitorIP, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, containertag.NewGoogleAnalytics(event, redactVisitorIP, eventTrigger)); err != nil {
return errors.Wrap(err, "failed to upsert google analytics ga4 tag: "+event)
}
}

View File

@ -12,7 +12,7 @@ func NewGoogleAnalyticsGA4(name string, enableGeoResolution bool, visitorRegion,
Parameter: []*tagmanager.Parameter{
{
Key: "activateResponseCompression",
Type: "template",
Type: "boolean",
Value: "true",
},
{

View File

@ -1,6 +1,8 @@
package googleanalytics
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
containertag "github.com/foomo/sesamy-cli/pkg/provider/googleanalytics/web/tag"
"github.com/foomo/sesamy-cli/pkg/provider/googletag"
@ -10,38 +12,35 @@ import (
"github.com/pkg/errors"
)
func Web(tm *tagmanager.TagManager, cfg config.GoogleAnalytics) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleAnalytics) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // create event tags
tagID, err := tm.LookupVariable(googletag.NameGoogleTagID)
tagID, err := tm.LookupVariable(ctx, googletag.NameGoogleTagID)
if err != nil {
return err
}
eventParameters, err := googletag.CreateWebEventTriggers(tm, cfg.WebContainer)
eventParameters, err := googletag.CreateWebEventTriggers(ctx, tm, cfg.WebContainer)
if err != nil {
return err
}
for event := range eventParameters {
eventTrigger, err := tm.LookupTrigger(commontrigger.EventName(event))
eventTrigger, err := tm.LookupTrigger(ctx, commontrigger.EventName(event))
if err != nil {
return errors.Wrap(err, "failed to lookup event trigger: "+event)
}
eventSettings, err := tm.LookupVariable(commonvariable.GoogleTagEventSettingsName(event))
eventSettings, err := tm.LookupVariable(ctx, commonvariable.GoogleTagEventSettingsName(event))
if err != nil {
return errors.Wrap(err, "failed to lookup google tag event setting: "+event)
}
if _, err := tm.UpsertTag(containertag.NewGoogleAnalyticsEvent(event, tagID, eventSettings, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, containertag.NewGoogleAnalyticsEvent(event, tagID, eventSettings, eventTrigger)); err != nil {
return err
}
}

View File

@ -1,32 +1,28 @@
package googleconsent
import (
"context"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/template"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/variable"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
)
func ServerEnsure(tm *tagmanager.TagManager) error {
folderName := tm.FolderName()
defer tm.SetFolderName(folderName)
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func ServerEnsure(ctx context.Context, tm *tagmanager.TagManager) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // create clients
consentTemplate, err := tm.UpsertCustomTemplate(template.NewGoogleConsentModeCheck(NameGoogleConsentModeCheckVariableTemplate))
consentTemplate, err := tm.UpsertCustomTemplate(ctx, template.NewGoogleConsentModeCheck(NameGoogleConsentModeCheckVariableTemplate))
if err != nil {
return err
}
if _, err = tm.UpsertVariable(variable.NewGoogleConsentModeAdStorage(consentTemplate)); err != nil {
if _, err = tm.UpsertVariable(ctx, folder, variable.NewGoogleConsentModeAdStorage(consentTemplate)); err != nil {
return err
}
if _, err = tm.UpsertVariable(variable.NewGoogleConsentModeAnalyticsStorage(consentTemplate)); err != nil {
if _, err = tm.UpsertVariable(ctx, folder, variable.NewGoogleConsentModeAnalyticsStorage(consentTemplate)); err != nil {
return err
}
}

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

@ -1,22 +1,21 @@
package googletag
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
)
func Server(tm *tagmanager.TagManager, cfg config.GoogleTag) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag) error {
folder, err := Folder(ctx, tm)
if err != nil {
return err
}
{ // create constants
if _, err := tm.UpsertVariable(commonvariable.NewConstant(NameGoogleTagMeasurementID, cfg.TagID)); err != nil {
if _, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameGoogleTagMeasurementID, cfg.TagID)); err != nil {
return err
}
}

View File

@ -1,6 +1,8 @@
package googletag
import (
"context"
"github.com/foomo/gocontemplate/pkg/contemplate"
"github.com/foomo/sesamy-cli/pkg/config"
webtag "github.com/foomo/sesamy-cli/pkg/provider/googletag/web/tag"
@ -13,13 +15,10 @@ import (
api "google.golang.org/api/tagmanager/v2"
)
func Web(tm *tagmanager.TagManager, cfg config.GoogleTag) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag) error {
folder, err := Folder(ctx, tm)
if err != nil {
return err
}
{ // setup google tag
@ -29,26 +28,29 @@ func Web(tm *tagmanager.TagManager, cfg config.GoogleTag) error {
if !cfg.SendPageView {
configSettings["send_page_view"] = "false"
}
if cfg.ServerContainerURL != "" {
configSettings["server_container_url"] = cfg.ServerContainerURL
}
eventSettings := map[string]*api.Variable{}
for k, v := range cfg.DataLayerVariables {
dlv, err := tm.UpsertVariable(variable.NewDataLayer(v))
dlv, err := tm.UpsertVariable(ctx, folder, variable.NewDataLayer(v))
if err != nil {
return err
}
eventSettings[k] = dlv
}
tagID, err := tm.UpsertVariable(commonvariable.NewConstant(NameGoogleTagID, cfg.TagID))
tagID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameGoogleTagID, cfg.TagID))
if err != nil {
return err
}
settingsVariable, err := tm.UpsertVariable(containervariable.NewGoogleTagConfigurationSettings(NameGoogleTagSettings, configSettings))
settingsVariable, err := tm.UpsertVariable(ctx, folder, containervariable.NewGoogleTagConfigurationSettings(NameGoogleTagSettings, configSettings))
if err != nil {
return err
}
if _, err = tm.UpsertTag(webtag.NewGoogleTag(NameGoogleTag, tagID, settingsVariable, eventSettings)); err != nil {
if _, err = tm.UpsertTag(ctx, folder, webtag.NewGoogleTag(NameGoogleTag, tagID, settingsVariable, eventSettings)); err != nil {
return err
}
}
@ -56,27 +58,28 @@ func Web(tm *tagmanager.TagManager, cfg config.GoogleTag) error {
return nil
}
func CreateWebEventTriggers(tm *tagmanager.TagManager, cfg contemplate.Config) (map[string]map[string]string, error) {
previousFolderName := tm.FolderName()
tm.SetFolderName("Sesamy - " + Name)
defer tm.SetFolderName(previousFolderName)
func CreateWebEventTriggers(ctx context.Context, tm *tagmanager.TagManager, cfg contemplate.Config) (map[string]map[string]string, error) {
folder, err := tm.LookupFolder(ctx, "Sesamy - "+Name)
if err != nil {
return nil, err
}
eventParameters, err := utils.LoadEventParams(cfg)
eventParameters, err := utils.LoadEventParams(ctx, cfg)
if err != nil {
return nil, err
}
for event, parameters := range eventParameters {
if _, err = tm.UpsertTrigger(commontrigger.NewEvent(event)); err != nil {
if _, err = tm.UpsertTrigger(ctx, folder, commontrigger.NewEvent(event)); err != nil {
return nil, err
}
variables, err := CreateWebDatalayerVariables(tm, parameters)
variables, err := CreateWebDatalayerVariables(ctx, tm, parameters)
if err != nil {
return nil, err
}
if _, err := tm.UpsertVariable(containervariable.NewGoogleTagEventSettings(event, variables)); err != nil {
if _, err := tm.UpsertVariable(ctx, folder, containervariable.NewGoogleTagEventSettings(event, variables)); err != nil {
return nil, err
}
}
@ -84,14 +87,15 @@ func CreateWebEventTriggers(tm *tagmanager.TagManager, cfg contemplate.Config) (
return eventParameters, nil
}
func CreateWebDatalayerVariables(tm *tagmanager.TagManager, parameters map[string]string) (map[string]*api.Variable, error) {
previousFolderName := tm.FolderName()
tm.SetFolderName("Sesamy - " + Name)
defer tm.SetFolderName(previousFolderName)
var err error
func CreateWebDatalayerVariables(ctx context.Context, tm *tagmanager.TagManager, parameters map[string]string) (map[string]*api.Variable, error) {
folder, err := tm.LookupFolder(ctx, "Sesamy - "+Name)
if err != nil {
return nil, err
}
variables := make(map[string]*api.Variable, len(parameters))
for parameterName, parameterValue := range parameters {
if variables[parameterName], err = tm.UpsertVariable(variable.NewDataLayer(parameterValue)); err != nil {
if variables[parameterName], err = tm.UpsertVariable(ctx, folder, variable.NewDataLayer(parameterValue)); err != nil {
return nil, err
}
}

View File

@ -1,32 +1,30 @@
package variable
import (
"sort"
"maps"
"slices"
"google.golang.org/api/tagmanager/v2"
)
func NewGoogleTagConfigurationSettings(name string, variables map[string]string) *tagmanager.Variable {
parameters := make([]string, 0, len(variables))
for k := range variables {
parameters = append(parameters, k)
}
sort.Strings(parameters)
variableKeys := slices.AppendSeq(make([]string, 0, len(variables)), maps.Keys(variables))
slices.Sort(variableKeys)
list := make([]*tagmanager.Parameter, len(parameters))
for i, parameter := range parameters {
list := make([]*tagmanager.Parameter, len(variables))
for i, k := range variableKeys {
list[i] = &tagmanager.Parameter{
Type: "map",
Map: []*tagmanager.Parameter{
{
Key: "parameter",
Type: "template",
Value: parameter,
Value: k,
},
{
Key: "parameterValue",
Type: "template",
Value: variables[parameter],
Value: variables[k],
},
},
}

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

@ -1,6 +1,8 @@
package googletagmanager
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager/server/client"
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager/server/variable"
@ -9,40 +11,37 @@ import (
servervariable "github.com/foomo/sesamy-cli/pkg/tagmanager/server/variable"
)
func Server(tm *tagmanager.TagManager, cfg config.GoogleTagManager, enableGeoResolution bool) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTagManager, enableGeoResolution bool) error {
folder, err := Folder(ctx, tm)
if err != nil {
return err
}
{ // enable build in variables
if _, err := tm.EnableBuiltInVariable("clientName"); err != nil {
if _, err := tm.EnableBuiltInVariable(ctx, "clientName"); err != nil {
return err
}
}
{ // create client
visitorRegion, err := tm.UpsertVariable(variable.NewVisitorRegion(NameGoogleTagManagerVisitorRegion))
visitorRegion, err := tm.UpsertVariable(ctx, folder, variable.NewVisitorRegion(NameGoogleTagManagerVisitorRegion))
if err != nil {
return err
}
if _, err := tm.UpsertClient(client.NewGoogleTagManagerWebContainer(NameGoogleTagManagerWebContainerClient, cfg.WebContainer.TagID, enableGeoResolution, visitorRegion)); err != nil {
if _, err := tm.UpsertClient(ctx, folder, client.NewGoogleTagManagerWebContainer(NameGoogleTagManagerWebContainerClient, cfg.WebContainer.TagID, enableGeoResolution, visitorRegion)); err != nil {
return err
}
}
{ // create variables
for _, value := range cfg.ServerContaienrVariables.EventData {
if _, err := tm.UpsertVariable(servervariable.NewEventData(value)); err != nil {
if _, err := tm.UpsertVariable(ctx, folder, servervariable.NewEventData(value)); err != nil {
return err
}
}
for key, value := range cfg.ServerContaienrVariables.LookupTables {
if _, err := tm.UpsertVariable(commonvariable.NewLookupTable(key, value)); err != nil {
if _, err := tm.UpsertVariable(ctx, folder, commonvariable.NewLookupTable(key, value)); err != nil {
return err
}
}

View File

@ -1,29 +1,28 @@
package googletagmanager
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
"github.com/foomo/sesamy-cli/pkg/tagmanager/web/variable"
)
func Web(tm *tagmanager.TagManager, cfg config.GoogleTagManager) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTagManager) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // create variables
for _, value := range cfg.WebContaienrVariables.DataLayer {
if _, err := tm.UpsertVariable(variable.NewDataLayer(value)); err != nil {
if _, err := tm.UpsertVariable(ctx, folder, variable.NewDataLayer(value)); err != nil {
return err
}
}
for key, value := range cfg.WebContaienrVariables.LookupTables {
if _, err := tm.UpsertVariable(commonvariable.NewLookupTable(key, value)); err != nil {
if _, err := tm.UpsertVariable(ctx, folder, commonvariable.NewLookupTable(key, value)); err != nil {
return err
}
}

View File

@ -1,28 +1,27 @@
package hotjar
import (
"context"
"github.com/foomo/sesamy-cli/pkg/config"
client "github.com/foomo/sesamy-cli/pkg/provider/hotjar/web/tag"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
commonvariable "github.com/foomo/sesamy-cli/pkg/tagmanager/common/variable"
)
func Web(tm *tagmanager.TagManager, cfg config.Hotjar) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.Hotjar) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
{ // setup hotjar
siteID, err := tm.UpsertVariable(commonvariable.NewConstant(NameSiteID, cfg.SiteID))
siteID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameSiteID, cfg.SiteID))
if err != nil {
return err
}
if _, err = tm.UpsertTag(client.NewHotjar(NameHotjarTag, siteID)); err != nil {
if _, err = tm.UpsertTag(ctx, folder, client.NewHotjar(NameHotjarTag, siteID)); err != nil {
return err
}
}

View File

@ -1,6 +1,7 @@
package microsoftads
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
@ -15,28 +16,25 @@ import (
"github.com/pkg/errors"
)
func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.MicrosoftAds) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.MicrosoftAds) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
tagID, err := tm.UpsertVariable(commonvariable.NewConstant(NameTagIDConstant, cfg.TagID))
tagID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameTagIDConstant, cfg.TagID))
if err != nil {
return err
}
if cfg.Conversion.Enabled {
tagTemplate, err := tm.UpsertCustomTemplate(template.NewConversionTag(NameConversionsTagTemplate))
tagTemplate, err := tm.UpsertCustomTemplate(ctx, template.NewConversionTag(NameConversionsTagTemplate))
if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.Conversion.ServerContainer.Config)
eventParameters, err := utils.LoadEventParams(ctx, cfg.Conversion.ServerContainer.Config)
if err != nil {
return err
}
@ -44,22 +42,22 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.MicrosoftAds)
for event := range eventParameters {
var eventTriggerOpts []trigger.ConversionEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.ConversionEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewConversionEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewConversionEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(servertagx.NewConversion(event, tagID, tagTemplate, cfg.Conversion.ServerContainer.Setting(event), eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewConversion(event, tagID, tagTemplate, cfg.Conversion.ServerContainer.Setting(event), eventTrigger)); 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

@ -1,6 +1,7 @@
package tracify
import (
"context"
"log/slog"
"github.com/foomo/sesamy-cli/pkg/config"
@ -15,33 +16,30 @@ import (
"github.com/pkg/errors"
)
func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Tracify) error {
{ // create folder
if folder, err := tm.UpsertFolder("Tracify - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Tracify) error {
folder, err := tm.UpsertFolder(ctx, "Tracify - "+Name)
if err != nil {
return err
}
{ // conversion
token, err := tm.UpsertVariable(commonvariable.NewConstant(NameTokenConstant, cfg.Token))
token, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameTokenConstant, cfg.Token))
if err != nil {
return err
}
customerSiteID, err := tm.UpsertVariable(commonvariable.NewConstant(NameCustomerSiteIDConstant, cfg.CustomerSiteID))
customerSiteID, err := tm.UpsertVariable(ctx, folder, commonvariable.NewConstant(NameCustomerSiteIDConstant, cfg.CustomerSiteID))
if err != nil {
return err
}
tagTemplate, err := tm.UpsertCustomTemplate(template.NewTracifyTag(NameTracifyServerTagTemplate))
tagTemplate, err := tm.UpsertCustomTemplate(ctx, template.NewTracifyTag(NameTracifyServerTagTemplate))
if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.ServerContainer)
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer)
if err != nil {
return err
}
@ -49,22 +47,22 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Tracify) error
for event := range eventParameters {
var eventTriggerOpts []trigger.TracifyEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.TracifyEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewTracifyEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewTracifyEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(servertagx.NewTracify(event, token, customerSiteID, tagTemplate, cfg, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, servertagx.NewTracify(event, token, customerSiteID, tagTemplate, cfg, eventTrigger)); err != nil {
return err
}
}

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

@ -1,6 +1,8 @@
package umami
import (
"context"
"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"
@ -12,22 +14,19 @@ import (
"github.com/pkg/errors"
)
func Server(tm *tagmanager.TagManager, cfg config.Umami) error {
{ // create folder
if folder, err := tm.UpsertFolder("Sesamy - " + Name); err != nil {
return err
} else {
tm.SetFolderName(folder.Name)
}
func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.Umami) error {
folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name)
if err != nil {
return err
}
template, err := tm.UpsertCustomTemplate(containertemplate.NewUmami(Name))
template, err := tm.UpsertCustomTemplate(ctx, containertemplate.NewUmami(Name))
if err != nil {
return err
}
{ // create tags
eventParameters, err := utils.LoadEventParams(cfg.ServerContainer)
eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer)
if err != nil {
return err
}
@ -35,22 +34,22 @@ func Server(tm *tagmanager.TagManager, cfg config.Umami) error {
for event := range eventParameters {
var eventTriggerOpts []trigger.UmamiEventOption
if cfg.GoogleConsent.Enabled {
if err := googleconsent.ServerEnsure(tm); err != nil {
if err := googleconsent.ServerEnsure(ctx, tm); err != nil {
return err
}
consentVariable, err := tm.LookupVariable(googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode))
if err != nil {
return err
}
eventTriggerOpts = append(eventTriggerOpts, trigger.UmamiEventWithConsentMode(consentVariable))
}
eventTrigger, err := tm.UpsertTrigger(trigger.NewUmamiEvent(event, eventTriggerOpts...))
eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewUmamiEvent(event, eventTriggerOpts...))
if err != nil {
return errors.Wrap(err, "failed to upsert event trigger: "+event)
}
if _, err := tm.UpsertTag(containertag.NewUmami(event, cfg, template, eventTrigger)); err != nil {
if _, err := tm.UpsertTag(ctx, folder, containertag.NewUmami(event, cfg, template, eventTrigger)); err != nil {
return err
}
}

85
pkg/pterm/sloghandler.go Normal file
View File

@ -0,0 +1,85 @@
package pterm
import (
"context"
"fmt"
"log/slog"
"github.com/pterm/pterm"
)
type SlogHandler struct {
attrs []slog.Attr
}
// NewSlogHandler returns a new logging handler that can be intrgrated with log/slog.
func NewSlogHandler() *SlogHandler {
return &SlogHandler{}
}
// Enabled returns true if the given level is enabled.
func (s *SlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
switch level {
case slog.LevelDebug:
return pterm.PrintDebugMessages
default:
return true
}
}
// Handle handles the given record.
func (s *SlogHandler) Handle(ctx context.Context, record slog.Record) error {
level := record.Level
message := record.Message
// Convert slog Attrs to a map.
keyValsMap := make(map[string]any)
record.Attrs(func(attr slog.Attr) bool {
keyValsMap[attr.Key] = attr.Value
return true
})
for _, attr := range s.attrs {
keyValsMap[attr.Key] = attr.Value
}
args := pterm.DefaultLogger.ArgsFromMap(keyValsMap)
// Wrapping args inside another slice to match [][]LoggerArgument
argsWrapped := [][]pterm.LoggerArgument{args}
for _, arg := range argsWrapped {
for _, attr := range arg {
message += " " + attr.Key + ": " + fmt.Sprintf("%v", attr.Value)
}
}
switch level {
case slog.LevelDebug:
pterm.Debug.Println(message)
case slog.LevelInfo:
pterm.Info.Println(message)
case slog.LevelWarn:
pterm.Warning.Println(message)
case slog.LevelError:
pterm.Error.Println(message)
default:
pterm.Info.Println(message)
}
return nil
}
// WithAttrs returns a new handler with the given attributes.
func (s *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newS := *s
newS.attrs = attrs
return &newS
}
// WithGroup is not yet supported.
func (s *SlogHandler) WithGroup(name string) slog.Handler {
// Grouping is not yet supported by pterm.
return s
}

20
pkg/pterm/writer.go Normal file
View File

@ -0,0 +1,20 @@
package pterm
import (
"github.com/pterm/pterm"
)
type Writer struct {
printer pterm.PrefixPrinter
}
func NewWriter(printer pterm.PrefixPrinter) *Writer {
return &Writer{
printer: printer,
}
}
func (p *Writer) Write(b []byte) (int, error) {
p.printer.Println(string(b))
return len(b), nil
}

View File

@ -1,13 +1,19 @@
package transformation
import (
"maps"
"slices"
"google.golang.org/api/tagmanager/v2"
)
func NewMPv2UserData(name string, variables map[string]*tagmanager.Variable, client *tagmanager.Client) *tagmanager.Transformation {
var list []*tagmanager.Parameter
for k, v := range variables {
list = append(list, &tagmanager.Parameter{
variableKeys := slices.AppendSeq(make([]string, 0, len(variables)), maps.Keys(variables))
slices.Sort(variableKeys)
list := make([]*tagmanager.Parameter, len(variables))
for i, k := range variableKeys {
list[i] = &tagmanager.Parameter{
IsWeakReference: false,
Map: []*tagmanager.Parameter{
{
@ -18,11 +24,11 @@ func NewMPv2UserData(name string, variables map[string]*tagmanager.Variable, cli
{
Key: "paramValue",
Type: "template",
Value: "{{" + v.Name + "}}",
Value: "{{" + variables[k].Name + "}}",
},
},
Type: "map",
})
}
}
return &tagmanager.Transformation{

View File

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

Some files were not shown because too many files have changed in this diff Show More