diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 65bdbe0..0000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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. diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index e415a86..0000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -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: diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..271db70 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,88 @@ +name: 🐛 Bug Report +description: Report a bug or unexpected behavior +title: "[Bug]: " +labels: ["bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report this issue! Please fill out the information below to help us understand and resolve the problem. + + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + description: Please confirm the following before submitting + options: + - label: I have searched existing issues to ensure this bug hasn't been reported + required: true + - label: I have checked the documentation + required: true + - label: I am using the latest version + required: false + + - type: input + id: version + attributes: + label: Version + description: What version are you using? + placeholder: e.g., 1.2.3 + validations: + required: true + + - type: textarea + id: description + attributes: + label: Bug Description + description: A clear and concise description of what the bug is + placeholder: What went wrong? + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to Reproduce + description: Detailed steps to reproduce the behavior + placeholder: | + 1. Import '...' + 2. Call function with '...' + 3. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What did you expect to happen? + placeholder: What should have happened instead? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + description: What actually happened? + placeholder: Include error messages, screenshots, or logs + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Any other context, environment, code, screenshots, or information about the problem + placeholder: Add any other relevant details here + validations: + required: false + + - type: textarea + id: solution + attributes: + label: Possible Solution + description: If you have suggestions on how to fix this, please share + placeholder: Optional - your ideas for fixing this issue + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md deleted file mode 100644 index bfa7836..0000000 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Enhancement Request -about: Suggest an enhancement -labels: enhancement ---- -**What would you like to be added**: - -**Why is this needed**: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..6566169 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,76 @@ +name: ✨ Feature Request +description: Suggest a new feature or enhancement +title: "[Feature]: " +labels: ["enhancement", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for your interest in improving this project! Please describe your feature request in detail. + + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + description: Please confirm the following before submitting + options: + - label: I have searched existing issues to ensure this hasn't been requested + required: true + - label: I have checked the documentation to ensure this feature doesn't exist + required: true + + - type: textarea + id: problem + attributes: + label: Problem Statement + description: Is your feature request related to a problem? Please describe. + placeholder: I'm frustrated when... / It would be helpful if... + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: Describe the solution you'd like + placeholder: A clear and concise description of what you want to happen + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Describe alternatives you've considered + placeholder: What other approaches have you thought about? + validations: + required: false + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Add any other context, code, mockups, or screenshots + placeholder: Links to similar features in other libraries, mockups, etc. + validations: + required: false + + - type: dropdown + id: priority + attributes: + label: Priority + description: How important is this feature to you? + options: + - Low - Nice to have + - Medium - Would significantly improve my workflow + - High - Blocking my usage of this library + validations: + required: false + + - type: checkboxes + id: contribution + attributes: + label: Contribution + description: Would you be willing to contribute this feature? + options: + - label: I'm willing to submit a PR to implement this feature diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml new file mode 100644 index 0000000..cdfd058 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -0,0 +1,55 @@ +name: ❓ Question +description: Ask a question about usage or clarification +title: "[Question]: " +labels: ["question"] +body: + - type: markdown + attributes: + value: | + Thanks for your question! Please provide as much detail as possible so we can help you effectively. + + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + description: Please confirm the following before submitting + options: + - label: I have checked the documentation + required: true + - label: I have searched existing issues for similar questions + required: true + + - type: textarea + id: question + attributes: + label: Your Question + description: What would you like to know? + placeholder: Please be as specific as possible + validations: + required: true + + - type: textarea + id: context + attributes: + label: Context + description: What are you trying to accomplish? + placeholder: Describe your use case or what you're building + validations: + required: false + + - type: textarea + id: attempted + attributes: + label: What I've Tried + description: What have you already attempted? + placeholder: Describe any solutions you've already explored + validations: + required: false + + - type: textarea + id: additional + attributes: + label: Additional Information + description: Any other details or code that might be helpful + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b174b8e..c8b182c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,19 +1,36 @@ -### 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).]_ + + +### Type of Change + + + +- [ ] 🐛 Bug fix +- [ ] ✨ New feature +- [ ] 💥 Breaking change +- [ ] 📝 Documentation +- [ ] ♻️ Refactoring +- [ ] ⚡ Performance +- [ ] ✅ Tests +- [ ] 🔧 Build/CI + +### Related Issue + + + +## Changes + + + +- ### 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 commented on my code, particularly in hard-to-understand areas. - [ ] I have made corresponding changes to the documentation. + +#### Notes + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95014af..e6157ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,11 +13,12 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 + - uses: jdx/mise-action@v3 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: check-latest: true go-version-file: go.mod @@ -35,7 +36,7 @@ jobs: - name: Login to docker.io run: docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} - - uses: goreleaser/goreleaser-action@v6 + - uses: goreleaser/goreleaser-action@v7 with: version: '~> v2' args: release --clean diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8b9634b..b71414c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,24 +19,31 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-go@v5 + - uses: jdx/mise-action@v3 + + - uses: actions/setup-go@v6 with: check-latest: true - go-version-file: 'go.mod' + go-version-file: go.mod - - uses: gotesttools/gotestfmt-action@v2 + - name: Setup golangci-lint cache + uses: actions/cache@v5 with: - token: ${{ secrets.GITHUB_TOKEN }} + path: ~/.cache/golangci-lint + key: golangci-lint-${{ runner.os }}-${{ hashFiles('**/go.mod') }} + restore-keys: | + golangci-lint-${{ runner.os }}-- - - uses: golangci/golangci-lint-action@v8 - with: - version: latest + - name: Run tidy + run: make tidy && git diff --exit-code + + - name: Run generate + run: make generate && git diff --exit-code + + - name: Run lint + run: make lint.branch - name: Run tests run: make test - - - uses: coverallsapp/github-action@v2 - with: - file: coverage.out diff --git a/.gitignore b/.gitignore index c2b241b..eb5ef41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,33 +1,50 @@ .* *.zip *.tar +*.tgz +*.pem *.out *.log -/bin/ -/tmp/ +bin/ +dist/ +tmp/ ## Git -!.gitkeep !.gitignore +!.gitkeep ## GitHub !.github/ +## Mise +!.mise.toml + +## Lefthook +!.lefthook.yaml + +## VSCode +!.vscode/ +.vscode/* +!.vscode/extensions.json +!.vscode/settings.default.json + ## Editorconfig !.editorconfig -## Docker -!.dockerignore +## Node ## +**/node_modules/ -## Husky -!.husky/ -!.husky.yaml +### Golang +/go.work +/go.work.sum +!.golangci.yaml +!.goreleaser.yaml -## Golang -go.work -go.work.sum -!.golangci.yml -!.goreleaser.yml +## Node +**/node_modules/ -## Ownbrew -!.ownbrew.yaml +## Vitepress +!docs/.vitepress +docs/.vitepress/* +!docs/.vitepress/theme/ +!docs/.vitepress/config.mts diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..898ae57 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,83 @@ +# yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json +version: "2" + +run: + build-tags: [ safe ] + modules-download-mode: readonly + +linters: + default: all + disable: + # Project specific linters + - paralleltest + # Discouraged linters + - noinlineerr # Disallows inline error handling (`if err := ...; err != nil {`). + - embeddedstructfieldcheck # Embedded types should be at the top of the field list of a struct, and there must be an empty line separating embedded fields from regular fields. [fast] + - cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false] + - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] + - dupl # Tool for code clone detection [fast: true, auto-fix: false] + - dupword # checks for duplicate words in the source code [fast: true, auto-fix: false] + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] + - 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] + - 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] + - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false] + - 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] + - 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] + - lll # Reports long lines [fast: true, auto-fix: false] + - maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false] + - nestif # Reports deeply nested if statements [fast: true, auto-fix: false] + - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false] + - mnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false] + - perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [fast: false, auto-fix: false] + - prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false] + - protogetter # Reports direct reads from proto message fields when getters should be used [fast: false, auto-fix: true] + - sloglint # ensure consistent code style when using log/slog [fast: false, auto-fix: false] + - tagalign # check that struct tags are well aligned [fast: true, auto-fix: true] + - tagliatelle # Checks the struct tags. [fast: true, auto-fix: false] + - unparam # Reports unused function parameters [fast: false, auto-fix: false] + - varnamelen # checks that the length of a variable's name matches its scope [fast: false, auto-fix: false] + - wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, 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] + # Deprected linters + - wsl + + # https://golangci-lint.run/docs/linters/ + settings: + # https://golangci-lint.run/docs/linters/configuration/#exhaustive + exhaustive: + default-signifies-exhaustive: true + # https://golangci-lint.run/docs/linters/configuration/#gosec + gosec: + severity: medium + confidence: medium + # https://golangci-lint.run/docs/linters/configuration/#revive + revive: + rules: + - name: package-comments + disabled: true + exclusions: + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - path: _test\.go + linters: + - forbidigo + paths: + - docs + - tmp +formatters: + enable: + - gofmt + - goimports diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 45050e1..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,221 +0,0 @@ -version: "2" -run: - go: 1.24.3 - build-tags: [safe] - modules-download-mode: readonly -linters: - 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] - - 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] - - unused # (megacheck) Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false] - - ## Recommended linters - - asasalint # check for pass []any as any in variadic func(...any) [fast: false, auto-fix: false] - - asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name [fast: true, auto-fix: false] - - bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false] - - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] - - canonicalheader # checks whether net/http.Header uses canonical header [fast: false, auto-fix: false] - - containedctx # containedctx is a linter that detects struct contained context.Context field [fast: false, auto-fix: false] - - contextcheck # check whether the function uses a non-inherited context [fast: false, auto-fix: false] - - copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false] - - decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false] - - durationcheck # check for two durations multiplied together [fast: false, auto-fix: false] - - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false] - - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false] - - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false] - - exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false] - - exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. [auto-fix] - - fatcontext # detects nested contexts in loops and function literals [fast: false, auto-fix: false] - #- forbidigo # Forbids identifiers [fast: false, auto-fix: false] - - forcetypeassert # finds forced type assertions [fast: true, auto-fix: false] - - gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false] - - 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] - - goheader # Checks is file header matches to pattern [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] - - gosec # (gas) Inspects source code for security problems [fast: false, auto-fix: false] - - gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase [fast: false, auto-fix: false] - - grouper # Analyze expression groups. [fast: true, auto-fix: false] - - iface # Detect the incorrect use of interfaces, helping developers avoid interface pollution. [fast: false, auto-fix: false] - - importas # Enforces consistent import aliases [fast: false, auto-fix: false] - - inamedparam # reports interfaces with unnamed method parameters [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] - - loggercheck # (logrlint) Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). [fast: false, auto-fix: false] - - makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false] - - mirror # reports wrong mirror patterns of bytes/strings usage [fast: false, auto-fix: true] - - misspell # Finds commonly misspelled English words [fast: true, auto-fix: true] - - musttag # enforce field tags in (un)marshaled structs [fast: false, auto-fix: false] - - nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast: true, auto-fix: false] - - nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false] - - nilnesserr # Reports constructs that checks for err != nil, but returns a different nil value error. - - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false] - - noctx # Finds sending http request without context.Context [fast: false, auto-fix: false] - - nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: true] - - nonamedreturns # Reports all named returns [fast: false, auto-fix: false] - - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. [fast: true, auto-fix: false] - #- paralleltest # Detects missing usage of t.Parallel() method in your Go test [fast: false, auto-fix: false] - - predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false] - - promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false] - - reassign # Checks that package variables are not reassigned [fast: false, auto-fix: false] - - recvcheck # checks for receiver type consistency [fast: false, auto-fix: false] - - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false] - - 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] - - 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] - - thelper # thelper detects tests helpers which is not start with t.Helper() method. [fast: false, auto-fix: false] - - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes. [fast: false, auto-fix: false] - - unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false] - - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false] - - usetesting # Reports uses of functions with replacement inside the testing package. [auto-fix] - - wastedassign # Finds wasted assignment statements [fast: false, auto-fix: false] - - whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast: true, auto-fix: true] - - ## Discouraged linters - #- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false] - #- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] - #- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] - #- dupl # Tool for code clone detection [fast: true, auto-fix: false] - #- dupword # checks for duplicate words in the source code [fast: true, auto-fix: false] - #- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] - #- 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] - #- 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] - #- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false] - #- 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] - #- 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] - #- lll # Reports long lines [fast: true, auto-fix: false] - #- maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false] - #- nestif # Reports deeply nested if statements [fast: true, auto-fix: false] - #- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false] - #- mnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false] - #- perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [fast: false, auto-fix: false] - #- prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false] - #- protogetter # Reports direct reads from proto message fields when getters should be used [fast: false, auto-fix: true] - #- sloglint # ensure consistent code style when using log/slog [fast: false, auto-fix: false] - #- tagalign # check that struct tags are well aligned [fast: true, auto-fix: true] - #- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false] - #- unparam # Reports unused function parameters [fast: false, auto-fix: false] - #- varnamelen # checks that the length of a variable's name matches its scope [fast: false, auto-fix: false] - #- 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$ diff --git a/.goreleaser.yml b/.goreleaser.yaml similarity index 100% rename from .goreleaser.yml rename to .goreleaser.yaml diff --git a/.husky.yaml b/.husky.yaml deleted file mode 100644 index 0284970..0000000 --- a/.husky.yaml +++ /dev/null @@ -1,15 +0,0 @@ -hooks: - pre-commit: - - golangci-lint run --fast-only - - husky lint-staged - commit-msg: - # only execute if not in a merge - - if [[ -z $(git rev-parse -q --verify MERGE_HEAD) ]]; then husky lint-commit; fi - -lint-staged: - '*.go': - - golangci-lint fmt - -lint-commit: - types: '^(feat|fix|build|chore|docs|perf|refactor|revert|style|test|wip)$' - header: '^(?P\w+)(\((?P[\w/.-]+)\))?(?P!)?:( +)?(?P
.+)' diff --git a/.husky/applypatch-msg b/.husky/applypatch-msg deleted file mode 100755 index 32d0649..0000000 --- a/.husky/applypatch-msg +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index 32d0649..0000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/fsmonitor-watchman b/.husky/fsmonitor-watchman deleted file mode 100755 index 32d0649..0000000 --- a/.husky/fsmonitor-watchman +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/post-update b/.husky/post-update deleted file mode 100755 index 32d0649..0000000 --- a/.husky/post-update +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/pre-applypatch b/.husky/pre-applypatch deleted file mode 100755 index 32d0649..0000000 --- a/.husky/pre-applypatch +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 32d0649..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/pre-merge-commit b/.husky/pre-merge-commit deleted file mode 100755 index 32d0649..0000000 --- a/.husky/pre-merge-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100755 index 32d0649..0000000 --- a/.husky/pre-push +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/pre-rebase b/.husky/pre-rebase deleted file mode 100755 index 32d0649..0000000 --- a/.husky/pre-rebase +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/pre-receive b/.husky/pre-receive deleted file mode 100755 index 32d0649..0000000 --- a/.husky/pre-receive +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/prepare-commit-msg b/.husky/prepare-commit-msg deleted file mode 100755 index 32d0649..0000000 --- a/.husky/prepare-commit-msg +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/push-to-checkout b/.husky/push-to-checkout deleted file mode 100755 index 32d0649..0000000 --- a/.husky/push-to-checkout +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/sendemail-validate b/.husky/sendemail-validate deleted file mode 100755 index 32d0649..0000000 --- a/.husky/sendemail-validate +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.husky/update b/.husky/update deleted file mode 100755 index 32d0649..0000000 --- a/.husky/update +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -husky hook $(basename "$0") $* diff --git a/.lefthook.yaml b/.lefthook.yaml new file mode 100644 index 0000000..3eff260 --- /dev/null +++ b/.lefthook.yaml @@ -0,0 +1,44 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/evilmartians/lefthook/refs/heads/master/schema.json +# https://lefthook.dev/configuration/ +output: [ summary ] + +pre-commit: + skip: [ merge, rebase ] + parallel: true + commands: + branch-name: + run: | + branch_name=$(git rev-parse --abbrev-ref HEAD) + if ! [[ "$branch_name" =~ ^(feature|fix)/ ]]; then + echo "Error: Branch name must start with 'feature/' or 'fix/'. Your branch: $branch_name" + exit 1 + fi + golangci-fmt: + glob: "*.go" + run: golangci-lint fmt {staged_files} + stage_fixed: true + golangci-lint: + glob: "*.go" + run: golangci-lint run --new --fast-only + stage_fixed: false + +commit-msg: + skip: [ merge, rebase ] + commands: + commit: + run: | + commit_msg_file={1} + commit_msg=$(cat "$commit_msg_file") + # Regex for conventional commits (type(scope?): subject) + regex="^(build|chore|ci|docs|feat|fix|perf|refactor|style|test|sec|wip|revert)(\([a-z0-9\-]+\))?(!)?: .{1,50}" + if ! echo "$commit_msg" | grep -qE "$regex"; then + echo "Error: Commit message does not follow Conventional Commits format." + echo "Format: type(scope?): subject" + echo "Example: feat(login): add remember me option" + exit 1 + fi + +post-checkout: + commands: + mise: + run: mise -q install diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..114e359 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,5 @@ +[tools] +# https://mise-tools.jdx.dev/tools/lefthook +lefthook = "2.1.4" +# https://mise-tools.jdx.dev/tools/golangci-lint +golangci-lint = "2.11.3" diff --git a/.ownbrew.yaml b/.ownbrew.yaml deleted file mode 100644 index 1249b2e..0000000 --- a/.ownbrew.yaml +++ /dev/null @@ -1,16 +0,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" -tempDir: ".ownbrew/tmp" -cellarDir: ".ownbrew/bin" -packages: - ## https://github.com/golangci/golangci-lint/releases - - name: golangci-lint - tap: foomo/tap/golangci/golangci-lint - version: 2.1.6 - ## https://github.com/go-courier/husky/releases - - name: husky - tap: foomo/tap/go-courier/husky - version: 1.8.1 diff --git a/Makefile b/Makefile index ad91cca..aa1cf6f 100644 --- a/Makefile +++ b/Makefile @@ -1,55 +1,95 @@ -.DEFAULT_GOAL := help +.DEFAULT_GOAL:=help -include .makerc -PATH:=bin:$(PATH) + +# --- Config ----------------------------------------------------------------- + +# Newline hack for error output +define br + + +endef # --- Targets ----------------------------------------------------------------- # This allows us to accept extra arguments -%: .husky +%: .mise .lefthook @: -.PHONY: .husky -# Configure git hooks for husky -.husky: - @if ! command -v husky &> /dev/null; then \ - echo "ERROR: missing executeable 'husky', please run:"; \ - echo "\n$ make brew\n"; \ - fi - @git config core.hooksPath .husky +.PHONY: .mise +# Install dependencies +.mise: +ifeq (, $(shell command -v mise)) + $(error $(br)$(br)Please ensure you have 'mise' installed and activated!$(br)$(br) $$ brew update$(br) $$ brew install mise$(br)$(br)See the documentation: https://mise.jdx.dev/getting-started.html) +endif + @mise install + +.PHONY: .lefthook +# Configure git hooks for lefthook +.lefthook: + @lefthook install --reset-hooks-path ### Tasks -.PHONY: doc -## Open go docs -doc: - @open "http://localhost:6060/pkg/github.com/foomo/sesamy-cli/" - @godoc -http=localhost:6060 -play +.PHONY: check +## Run lint & test +check: tidy generate lint test -.PHONY: test -## Run tests -test: - @GO_TEST_TAGS=-skip go test -tags=safe -coverprofile=coverage.out -race -json -v ./... 2>&1 | tee /tmp/gotest.log | gotestfmt +.PHONY: tidy +## Run go mod tidy +tidy: + @echo "〉go mod tidy" + @go mod tidy .PHONY: lint ## Run linter lint: + @echo "〉golangci-lint run" @golangci-lint run .PHONY: lint.fix ## Fix lint violations lint.fix: + @echo "〉golangci-lint run fix" @golangci-lint run --fix -.PHONY: tidy -## Run go mod tidy -tidy: - @go mod tidy +.PHONY: lint.branch +## Run linter with --new-from-rev=origin/main +lint.branch: + @echo "〉golangci-lint run with --new-from-rev=origin/main" + @golangci-lint run --new-from-rev=origin/main + +.PHONY: test +## Run tests +test: + @echo "〉go test" + @GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -tags=safe ./... + +.PHONY: test.race +## Run tests with -race +test.race: + @echo "〉go test -race" + @GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -tags=safe -race ./... + +.PHONY: test.nocache +## Run tests with -count=1 +test.nocache: + @echo "〉go test -count=1" + @GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -tags=safe -count=1 ./... .PHONY: outdated ## Show outdated direct dependencies outdated: + @echo "〉go mod outdated" @go list -u -m -json all | go-mod-outdated -update -direct +.PHONY: generate +## Run go generate +generate: + @echo "〉go generate" + @go generate ./... + +### Build + .PHONY: build ## Build binary build: @@ -68,24 +108,30 @@ install: install.debug: @go build -tags=safe -gclags="all=-N -l" -o ${GOPATH}/bin/sesamy main.go -### Utils +### Documentation -.PHONY: brew -## Install project binaries -brew: - @ownbrew install +.PHONY: godocs +## Open go docs +godocs: + @echo "〉starting go docs" + @go doc -http + +### Utils .PHONY: help ## Show help text help: - @echo "\033[1;36mSesamy CLI\033[0m" + @echo "" + @echo "SESAMY CLI" + @echo "" + @echo "Usage:\n make [task]" @awk '{ \ 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); \ + if(help) printf "%-23s %s\n\n", cmd, help; help=""; \ + printf "\n%s:\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=""; \ + if(help) printf " %-23s %s\n", cmd, help; help=""; \ } else if($$0 ~ /^##/){ \ help = help ? help "\n " substr($$0,3) : substr($$0,3); \ } else if(help){ \ diff --git a/README.md b/README.md index b5cadbd..e8118f8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![goreleaser](https://github.com/foomo/sesamy-cli/actions/workflows/release.yml/badge.svg)](https://github.com/foomo/sesamy-cli/actions)

- sesamy + sesamy

# Sesamy CLI @@ -532,10 +532,12 @@ You might need to increase your Google Tag Manager API quotas, since they are li ## How to Contribute -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. +Contributions are welcome! Please read the [contributing guide](docs/CONTRIBUTING.md). + +![Contributors](https://contributors-table.vercel.app/image?repo=foomo/sesamy-cli&width=50&columns=15) ## License -Distributed under MIT License, please see license file within the code for more details. +Distributed under MIT License, please see the [license](LICENSE) file within the code for more details. _Made with ♥ [foomo](https://www.foomo.org) by [bestbytes](https://www.bestbytes.com)_ diff --git a/cmd/diff/diff.go b/cmd/diff/diff.go index 8267836..2681791 100644 --- a/cmd/diff/diff.go +++ b/cmd/diff/diff.go @@ -15,6 +15,7 @@ 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 @@ -23,12 +24,14 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } 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: @@ -49,6 +52,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.Tag { if value.Name == entity.Tag.Name { // unset props @@ -58,6 +62,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -66,6 +71,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.Folder != nil: res = append(res, " # Folder: "+entity.Folder.Name+" ("+entity.ChangeStatus+")") @@ -85,6 +91,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.Folder { if value.Name == entity.Folder.Name { // unset props @@ -94,6 +101,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -102,6 +110,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.Trigger != nil: res = append(res, " # Trigger: "+entity.Trigger.Name+" ("+entity.ChangeStatus+")") @@ -121,6 +130,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.Trigger { if value.Name == entity.Trigger.Name { // unset props @@ -130,6 +140,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -138,6 +149,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.Variable != nil: res = append(res, " # Variable: "+entity.Variable.Name+" ("+entity.ChangeStatus+")") @@ -157,6 +169,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.Variable { if value.Name == entity.Variable.Name { // unset props @@ -166,6 +179,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -174,6 +188,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.Client != nil: res = append(res, " # Client: "+entity.Client.Name+" ("+entity.ChangeStatus+")") @@ -193,6 +208,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.Client { if value.Name == entity.Client.Name { // unset props @@ -202,6 +218,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -210,6 +227,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.GtagConfig != nil: res = append(res, " # GtagConfig: "+entity.GtagConfig.AccountId+" ("+entity.ChangeStatus+")") @@ -229,6 +247,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.GtagConfig { if value.AccountId == entity.GtagConfig.AccountId { // unset props @@ -238,6 +257,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -246,6 +266,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.BuiltInVariable != nil: res = append(res, " # BuiltInVariable: "+entity.BuiltInVariable.Name+" ("+entity.ChangeStatus+")") @@ -263,12 +284,14 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.BuiltInVariable { if value.Name == entity.BuiltInVariable.Name { original, err = ToYalm(value) if err != nil { return "", err } + break } } @@ -277,6 +300,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.CustomTemplate != nil: res = append(res, " # CustomTemplate: "+entity.CustomTemplate.Name+" ("+entity.ChangeStatus+")") @@ -296,6 +320,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.CustomTemplate { if value.Name == entity.CustomTemplate.Name { // unset props @@ -305,6 +330,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -313,6 +339,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) case entity.Transformation != nil: res = append(res, " # Transformation: "+entity.Transformation.Name+" ("+entity.ChangeStatus+")") @@ -332,6 +359,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin } var original string + for _, value := range live.Transformation { if value.Name == entity.Transformation.Name { // unset props @@ -341,6 +369,7 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + break } } @@ -349,11 +378,13 @@ func diff(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager) (strin if err != nil { return "", err } + res = append(res, d...) default: l.Warn("unknown entity type", "entity", entity) } } + return strings.Join(res, " ---\n"), nil } @@ -373,11 +404,13 @@ func ToDiff(original, changed string) ([]string, error) { } var ret []string + for _, d := range yamldiff.Do(yamls1, yamls2) { if value := d.Dump(); len(value) > 4 { ret = append(ret, value) } } + return ret, nil } diff --git a/cmd/diff/server.go b/cmd/diff/server.go index 7f806d1..85749b4 100644 --- a/cmd/diff/server.go +++ b/cmd/diff/server.go @@ -51,7 +51,9 @@ func NewServer(l *slog.Logger) *cobra.Command { if !c.GetBool("raw") { out = utils.Highlight(out) } + _, err = fmt.Println(out) + return err }, } diff --git a/cmd/diff/web.go b/cmd/diff/web.go index 9b9e44f..33f1216 100644 --- a/cmd/diff/web.go +++ b/cmd/diff/web.go @@ -51,7 +51,9 @@ func NewWeb(l *slog.Logger) *cobra.Command { if !c.GetBool("raw") { out = utils.Highlight(out) } + _, err = fmt.Println(out) + return err }, } diff --git a/cmd/list/list.go b/cmd/list/list.go index 1c5f19c..5a39967 100644 --- a/cmd/list/list.go +++ b/cmd/list/list.go @@ -15,6 +15,7 @@ func dump(i interface{ MarshalJSON() ([]byte, error) }, err error) (string, erro if err != nil { return "", err } + out, err := i.MarshalJSON() if err != nil { return "", err @@ -24,6 +25,7 @@ func dump(i interface{ MarshalJSON() ([]byte, error) }, err error) (string, erro if err := json2yaml.Convert(&output, bytes.NewBuffer(out)); err != nil { return "", err } + return output.String(), nil } @@ -50,11 +52,13 @@ func list(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, resour if err != nil { return "", err } + var ret strings.Builder for _, template := range r.Template { ret.WriteString("---- Template data: " + template.Name + " ----------------------\n") ret.WriteString(template.TemplateData + "\n") } + return ret.String(), nil case "gtag-config": return dump(tm.Service().Accounts.Containers.Workspaces.GtagConfig.List(tm.WorkspacePath()).Context(ctx).Do()) diff --git a/cmd/list/server.go b/cmd/list/server.go index 448e8bc..4c93e40 100644 --- a/cmd/list/server.go +++ b/cmd/list/server.go @@ -68,7 +68,9 @@ func NewServer(l *slog.Logger) *cobra.Command { if !c.GetBool("raw") { out = utils.Highlight(out) } + _, err = fmt.Println(out) + return err }, } diff --git a/cmd/list/web.go b/cmd/list/web.go index f420a70..1bcf0ea 100644 --- a/cmd/list/web.go +++ b/cmd/list/web.go @@ -67,7 +67,9 @@ func NewWeb(l *slog.Logger) *cobra.Command { if !c.GetBool("raw") { out = utils.Highlight(out) } + _, err = fmt.Println(out) + return err }, } diff --git a/cmd/open.go b/cmd/open.go index 8d245df..1eba09a 100644 --- a/cmd/open.go +++ b/cmd/open.go @@ -31,11 +31,13 @@ func NewOpen(l *slog.Logger) *cobra.Command { } 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, diff --git a/cmd/provision/server.go b/cmd/provision/server.go index bb06dd2..d01b7cb 100644 --- a/cmd/provision/server.go +++ b/cmd/provision/server.go @@ -77,6 +77,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.GoogleAnalytics.Enabled && utils.Tag(googleanalyticsprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", googleanalyticsprovider.Name, "tag", googleanalyticsprovider.Tag) + if err := googleanalyticsprovider.Server(cmd.Context(), tm, cfg.GoogleAnalytics, cfg.RedactVisitorIP, cfg.EnableGeoResolution); err != nil { return errors.Wrap(err, "failed to provision google analytics") } @@ -84,6 +85,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.ConversionLinker.Enabled && utils.Tag(conversionlinkerprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", conversionlinkerprovider.Name, "tag", conversionlinkerprovider.Tag) + if err := conversionlinkerprovider.Server(cmd.Context(), tm, cfg.ConversionLinker); err != nil { return errors.Wrap(err, "failed to provision conversion linker") } @@ -91,6 +93,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.Umami.Enabled && utils.Tag(umamiprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", umamiprovider.Name, "tag", umamiprovider.Tag) + if err := umamiprovider.Server(cmd.Context(), tm, cfg.Umami); err != nil { return errors.Wrap(err, "failed to provision umami") } @@ -98,6 +101,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.Facebook.Enabled && utils.Tag(facebookprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", facebookprovider.Name, "tag", facebookprovider.Tag) + if err := facebookprovider.Server(cmd.Context(), l, tm, cfg.Facebook); err != nil { return errors.Wrap(err, "failed to provision facebook") } @@ -105,6 +109,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.GoogleAds.Enabled && utils.Tag(googleadsprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", googleadsprovider.Name, "tag", googleadsprovider.Tag) + if err := googleadsprovider.Server(cmd.Context(), l, tm, cfg.GoogleAds); err != nil { return errors.Wrap(err, "failed to provision google ads") } @@ -112,6 +117,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.Emarsys.Enabled && utils.Tag(emarsysprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", emarsysprovider.Name, "tag", emarsysprovider.Tag) + if err := emarsysprovider.Server(cmd.Context(), l, tm, cfg.Emarsys); err != nil { return errors.Wrap(err, "failed to provision emarsys") } @@ -119,6 +125,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.Tracify.Enabled && utils.Tag(tracifyprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", tracifyprovider.Name, "tag", tracifyprovider.Tag) + if err := tracifyprovider.Server(cmd.Context(), l, tm, cfg.Tracify); err != nil { return errors.Wrap(err, "failed to provision tracify") } @@ -126,6 +133,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.Criteo.Enabled && utils.Tag(criteoprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", criteoprovider.Name, "tag", criteoprovider.Tag) + if err := criteoprovider.Server(cmd.Context(), l, tm, cfg.Criteo); err != nil { return errors.Wrap(err, "failed to provision criteo") } @@ -133,6 +141,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.MicrosoftAds.Enabled && utils.Tag(microsoftadsprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", microsoftadsprovider.Name, "tag", microsoftadsprovider.Tag) + if err := microsoftadsprovider.Server(cmd.Context(), l, tm, cfg.MicrosoftAds); err != nil { return errors.Wrap(err, "failed to provision microsoftads") } @@ -140,6 +149,7 @@ func NewServer(l *slog.Logger) *cobra.Command { if cfg.Mixpanel.Enabled && utils.Tag(mixpanelprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", mixpanelprovider.Name, "tag", mixpanelprovider.Tag) + if err := mixpanelprovider.Server(cmd.Context(), l, tm, cfg.Mixpanel); err != nil { return errors.Wrap(err, "failed to provision mixpanel") } @@ -147,6 +157,7 @@ func NewServer(l *slog.Logger) *cobra.Command { 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") } @@ -163,6 +174,7 @@ func NewServer(l *slog.Logger) *cobra.Command { for _, s := range i { child.Children = append(child.Children, pterm.TreeNode{Text: s}) } + tree.Children = append(tree.Children, child) } diff --git a/cmd/provision/web.go b/cmd/provision/web.go index b64cebe..8120720 100644 --- a/cmd/provision/web.go +++ b/cmd/provision/web.go @@ -59,6 +59,7 @@ func NewWeb(l *slog.Logger) *cobra.Command { if utils.Tag(googletagprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", googletagprovider.Name, "tag", googletagprovider.Tag) + if err := googletagprovider.Web(cmd.Context(), tm, cfg.GoogleTag); err != nil { return errors.Wrap(err, "failed to provision google tag provider") } @@ -72,6 +73,7 @@ func NewWeb(l *slog.Logger) *cobra.Command { if cfg.GoogleAnalytics.Enabled && utils.Tag(googleanaylticsprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", googleanaylticsprovider.Name, "tag", googleanaylticsprovider.Tag) + if err := googleanaylticsprovider.Web(cmd.Context(), tm, cfg.GoogleAnalytics); err != nil { return errors.Wrap(err, "failed to provision google analytics provider") } @@ -79,6 +81,7 @@ func NewWeb(l *slog.Logger) *cobra.Command { if cfg.Emarsys.Enabled && utils.Tag(emarsysprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", emarsysprovider.Name, "tag", emarsysprovider.Tag) + if err := emarsysprovider.Web(cmd.Context(), tm, cfg.Emarsys); err != nil { return errors.Wrap(err, "failed to provision emarsys provider") } @@ -86,6 +89,7 @@ func NewWeb(l *slog.Logger) *cobra.Command { if cfg.Hotjar.Enabled && utils.Tag(hotjarprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", hotjarprovider.Name, "tag", hotjarprovider.Tag) + if err := hotjarprovider.Web(cmd.Context(), tm, cfg.Hotjar); err != nil { return errors.Wrap(err, "failed to provision hotjar provider") } @@ -93,6 +97,7 @@ func NewWeb(l *slog.Logger) *cobra.Command { if cfg.Criteo.Enabled && utils.Tag(criteoprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", criteoprovider.Name, "tag", criteoprovider.Tag) + if err := criteoprovider.Web(cmd.Context(), l, tm, cfg.Criteo); err != nil { return errors.Wrap(err, "failed to provision criteo provider") } @@ -100,6 +105,7 @@ func NewWeb(l *slog.Logger) *cobra.Command { if cfg.Cookiebot.Enabled && utils.Tag(cookiebotprovider.Tag, tags) { l.Info("🅿️ Running provider", "name", cookiebotprovider.Name, "tag", cookiebotprovider.Tag) + if err := cookiebotprovider.Web(cmd.Context(), tm, cfg.Cookiebot); err != nil { return errors.Wrap(err, "failed to provision cookiebot provider") } @@ -116,8 +122,10 @@ func NewWeb(l *slog.Logger) *cobra.Command { for _, s := range i { child.Children = append(child.Children, pterm.TreeNode{Text: s}) } + tree.Children = append(tree.Children, child) } + if err := pterm.DefaultTree.WithRoot(tree).WithWriter(ptermx.NewWriter(pterm.Warning)).Render(); err != nil { l.Warn("failed to render missed resources", "error", err) } diff --git a/cmd/tags.go b/cmd/tags.go index 68569ed..13b2486 100644 --- a/cmd/tags.go +++ b/cmd/tags.go @@ -54,6 +54,7 @@ func NewTags(l *slog.Logger) *cobra.Command { for _, name := range slices.Sorted(maps.Keys(tags)) { data = append(data, []string{name, tags[name]}) } + return pterm.DefaultTable.WithHasHeader().WithData(data).Render() }, } diff --git a/cmd/typescript.go b/cmd/typescript.go index 649b7cd..cdc8825 100644 --- a/cmd/typescript.go +++ b/cmd/typescript.go @@ -51,6 +51,7 @@ func NewTypeScript(l *slog.Logger) *cobra.Command { } l.InfoContext(cmd.Context(), "generated typescript code", "dir", outPath, "files", slices.AppendSeq(make([]string, 0, len(files)), maps.Keys(files))) + for filename, file := range files { if err = os.WriteFile(path.Join(outPath, filename), []byte(file.String()), 0600); err != nil { return errors.Wrap(err, "failed to write typescript code") diff --git a/.github/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md similarity index 100% rename from .github/CODE_OF_CONDUCT.md rename to docs/CODE_OF_CONDUCT.md diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..6958aa2 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +Thank you for your interest in contributing to our project! This guide will help you get started with the development process. + +## Development Workflow + +1. Fork this repository +2. Create a new branch: `git checkout -b feature/your-feature-name` +3. Make your changes +4. Check and fix code style and formatting issues: `make lint` +5. Run checks: `make test` +6. Build the project: `make build` +7. Commit your changes using the conventions below +8. Push your branch to your fork +9. Open a pull request + +## Commit Message Conventions + +We follow [Conventional Commits](https://www.conventionalcommits.org/) for clear and structured commit messages: + +- `feat:` New features +- `fix:` Bug fixes +- `docs:` Documentation changes +- `style:` Code style changes (formatting, etc.) +- `refactor:` Code changes that neither fix bugs nor add features +- `perf:` Performance improvements +- `test:` Adding or updating tests +- `chore:` Maintenance tasks, dependencies, etc. + +## Pull Request Guidelines + +1. Update documentation if needed +2. Ensure all tests pass +3. Address any feedback from code reviews +4. Once approved, your PR will be merged + +## Code of Conduct & Security + +Please be respectful and constructive in all interactions within our community and follow the [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) and [SECURITY](SECURITY.md) guidelines. + +## Questions? + +If you have any questions, please [open an issue](https://github.com/foomo/sesamy-cli/issues/new) for discussion. diff --git a/.github/SECURITY.md b/docs/SECURITY.md similarity index 100% rename from .github/SECURITY.md rename to docs/SECURITY.md diff --git a/.github/assets/sesamy.png b/docs/public/logo.png similarity index 100% rename from .github/assets/sesamy.png rename to docs/public/logo.png diff --git a/go.mod b/go.mod index 6de9923..8223c29 100644 --- a/go.mod +++ b/go.mod @@ -1,95 +1,94 @@ module github.com/foomo/sesamy-cli -go 1.25.1 +go 1.26 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/go v0.4.0 github.com/foomo/gocontemplate v0.2.0 - github.com/foomo/sesamy-go v0.11.2 + github.com/foomo/sesamy-go v0.12.1 github.com/invopop/jsonschema v0.13.0 - github.com/itchyny/json2yaml v0.1.4 + github.com/itchyny/json2yaml v0.1.5 github.com/joho/godotenv v1.5.1 github.com/knadh/koanf/parsers/yaml v1.1.0 - github.com/knadh/koanf/providers/file v1.2.0 + github.com/knadh/koanf/providers/file v1.2.1 github.com/knadh/koanf/providers/rawbytes v1.0.0 - github.com/knadh/koanf/v2 v2.2.2 + github.com/knadh/koanf/v2 v2.3.3 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pkg/errors v0.9.1 - github.com/pterm/pterm v0.12.81 - github.com/spf13/cobra v1.10.1 + github.com/pterm/pterm v0.12.83 + github.com/spf13/cobra v1.10.2 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 + github.com/wissance/stringFormatter v1.6.1 + google.golang.org/api v0.272.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.16.5 // indirect + cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/compute/metadata v0.9.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/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/containerd/console v1.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/foomo/gostandards v0.2.0 // indirect + github.com/foomo/gostandards v0.3.0 // 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/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/goccy/go-yaml v1.15.6 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.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/mailru/easyjson v0.9.0 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mailru/easyjson v0.9.2 // indirect + github.com/mattn/go-runewidth v0.0.21 // 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.11.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/sagikazarmark/locafero v0.12.0 // 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.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.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 // indirect + go.opentelemetry.io/otel v1.42.0 // indirect + go.opentelemetry.io/otel/metric v1.42.0 // indirect + go.opentelemetry.io/otel/trace v1.42.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 + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/tools v0.42.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 // indirect + google.golang.org/grpc v1.79.2 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a14c8d4..44eeeb2 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,12 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= -cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= -cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= 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= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= 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= @@ -32,6 +32,10 @@ github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPn github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= @@ -47,14 +51,14 @@ 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/go v0.4.0 h1:9Qo5DLdoTI965sj6pDHm990Wzqk1RpbcGu0TZ4r2RNw= +github.com/foomo/go v0.4.0/go.mod h1:bZ/DrRPjvjpsg093nIHuifyHLNcgjKppBXzBCwSTYrU= 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.11.2 h1:P405IMMDzW/4hMKbAbvCVBaFjOARb26/PdG1wTBAxBM= -github.com/foomo/sesamy-go v0.11.2/go.mod h1:yORAeN0fDaZ0irXx4QdMp61wuDBAeEBHD0bTN4JjKxA= +github.com/foomo/gostandards v0.3.0 h1:Ev88Tdu0+52UH/d0l2KcAWx542A13o42d++LSTG0DC4= +github.com/foomo/gostandards v0.3.0/go.mod h1:8hJ56tpyFAVwavs5FuUF3po7Bq7hOTkQ1+DzFi4GCWg= +github.com/foomo/sesamy-go v0.12.1 h1:Gk14r7IYB3vAA+Dc4LEq+6fR/OfBDyQSRkmqhWQachE= +github.com/foomo/sesamy-go v0.12.1/go.mod h1:SW0G8wQJQ8GXjw+POcXyhtdQoI6qCZlYv7I8YuBTU1w= 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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -64,8 +68,8 @@ 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/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.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= @@ -77,10 +81,10 @@ 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.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/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= 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= @@ -91,8 +95,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/itchyny/json2yaml v0.1.4 h1:/pErVOXGG5iTyXHi/QKR4y3uzhLjGTEmmJIy97YT+k8= -github.com/itchyny/json2yaml v0.1.4/go.mod h1:6iudhBZdarpjLFRNj+clWLAkGft+9uCcjAZYXUH9eGI= +github.com/itchyny/json2yaml v0.1.5 h1:wKaepxwivjcn2ssiLu7v4KrLKSIILWtwD/8L054tTdI= +github.com/itchyny/json2yaml v0.1.5/go.mod h1:lBQ0JI7HugKRQy/c5/U3KRymvBNub/HTbSXHS9/4018= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -104,12 +108,12 @@ github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpb 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/file v1.2.1 h1:bEWbtQwYrA+W2DtdBrQWyXqJaJSG3KrP3AESOJYp9wM= +github.com/knadh/koanf/providers/file v1.2.1/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/knadh/koanf/v2 v2.3.3 h1:jLJC8XCRfLC7n4F+ZKKdBsbq1bfXTpuFhf4L7t94D94= +github.com/knadh/koanf/v2 v2.3.3/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= 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= @@ -119,12 +123,12 @@ 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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M= +github.com/mailru/easyjson v0.9.2/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/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= +github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= 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= @@ -147,26 +151,22 @@ 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.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA= -github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw= +github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA= +github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk= 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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -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/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= 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.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/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 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= @@ -189,53 +189,53 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu 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.5.0 h1:hiGpYEJS3B3H/6M03HecqW8nDO0OXgQ75REVTXp6reM= -github.com/wissance/stringFormatter v1.5.0/go.mod h1:H7Mz15+5i8ypmv6bLknM/uD+U1teUW99PlW0DNCNscA= +github.com/wissance/stringFormatter v1.6.1 h1:Pf5m2lMi1z256+SgWLj+u4SGqSzix0HP0Z0t4QgMM2I= +github.com/wissance/stringFormatter v1.6.1/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= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/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.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.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 h1:PnV4kVnw0zOmwwFkAzCN5O07fw1YOIQor120zrh0AVo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0/go.mod h1:ofAwF4uinaf8SXdVzzbL4OsxJ3VfeEg3f/F6CeF49/Y= +go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= +go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= +go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= +go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= +go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= 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.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/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= 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.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= 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.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/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= @@ -247,45 +247,45 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 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.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= 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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= 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.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 h1:aJmi6DVGGIStN9Mobk/tZOOQUBbj0BPjZjjnOdoZKts= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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= diff --git a/main.go b/main.go index ee147e5..3a0c133 100644 --- a/main.go +++ b/main.go @@ -31,23 +31,28 @@ func main() { 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 } } diff --git a/main_test.go b/main_test.go index 4969f3c..4c91fd4 100644 --- a/main_test.go +++ b/main_test.go @@ -29,6 +29,7 @@ func TestConfig(t *testing.T) { if t.Name() == "" { return t.String() } + return strings.ReplaceAll(t.PkgPath(), "/", ".") + "." + t.Name() } require.NoError(t, reflector.AddGoComments("github.com/foomo/sesamy-cli", "./")) @@ -37,6 +38,7 @@ func TestConfig(t *testing.T) { require.NoError(t, err) filename := path.Join(cwd, "sesamy.schema.json") + expected, err := os.ReadFile(filename) if !errors.Is(err, os.ErrNotExist) { require.NoError(t, err) diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index 6cbf3fb..486349b 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -22,8 +22,10 @@ func ReadConfig(l *slog.Logger, c *viper.Viper, cmd *cobra.Command) (*config.Con 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 { @@ -33,13 +35,16 @@ func ReadConfig(l *slog.Logger, c *viper.Viper, cmd *cobra.Command) (*config.Con pterm.Debug.Println("reading config from filename: " + filename) p = file.Provider(filename) } + if err := k.Load(p, yaml.Parser()); err != nil { return nil, errors.Wrap(err, "error loading config file: "+filename) } } var cfg *config.Config + pterm.Debug.Println("unmarshalling config") + if err := k.UnmarshalWithConf("", &cfg, koanf.UnmarshalConf{ Tag: "yaml", }); err != nil { diff --git a/pkg/code/file.go b/pkg/code/file.go index e3a6984..5cfbea1 100644 --- a/pkg/code/file.go +++ b/pkg/code/file.go @@ -68,6 +68,7 @@ func (f *File) String() string { section := f.Sections[id] sectionParts := section.Parts slices.Sort(sectionParts) + for _, part := range sectionParts { b.WriteString(part + "\n") } diff --git a/pkg/config/facebook.go b/pkg/config/facebook.go index 50ec43b..82dc449 100644 --- a/pkg/config/facebook.go +++ b/pkg/config/facebook.go @@ -26,5 +26,6 @@ func (s *FacebookServerContainer) Setting(eventName string) FacebookConversionAP if value, ok := s.Settings[eventName]; ok { return value } + return FacebookConversionAPITag{} } diff --git a/pkg/config/googleadsconversion.go b/pkg/config/googleadsconversion.go index 90f1987..77dfb3d 100644 --- a/pkg/config/googleadsconversion.go +++ b/pkg/config/googleadsconversion.go @@ -22,5 +22,6 @@ func (s *GoogleAdsConversionServerContainer) Setting(eventName string) []GoogleA if value, ok := s.Settings[eventName]; ok { return value } + return nil } diff --git a/pkg/config/googleapi.go b/pkg/config/googleapi.go index 10d2122..1b2bafb 100644 --- a/pkg/config/googleapi.go +++ b/pkg/config/googleapi.go @@ -13,9 +13,10 @@ type GoogleAPI struct { func (c GoogleAPI) GetClientOption() googleapioption.ClientOption { var ret googleapioption.ClientOption if c.CredentialsFile != "" { - ret = googleapioption.WithCredentialsFile(c.CredentialsFile) + ret = googleapioption.WithAuthCredentialsFile(googleapioption.ServiceAccount, c.CredentialsFile) } else { - ret = googleapioption.WithCredentialsJSON([]byte(c.Credentials)) + ret = googleapioption.WithAuthCredentialsJSON(googleapioption.ServiceAccount, []byte(c.Credentials)) } + return ret } diff --git a/pkg/config/microsoftadsconversion.go b/pkg/config/microsoftadsconversion.go index c3acef4..11b7596 100644 --- a/pkg/config/microsoftadsconversion.go +++ b/pkg/config/microsoftadsconversion.go @@ -25,5 +25,6 @@ func (s *MicrosoftAdsServerContainer) Setting(eventName string) MicrosoftAdsConv if value, ok := s.Settings[eventName]; ok { return value } + return MicrosoftAdsConversionTag{} } diff --git a/pkg/provider/conversionlinker/server.go b/pkg/provider/conversionlinker/server.go index cf7e8bf..4e1e7f5 100644 --- a/pkg/provider/conversionlinker/server.go +++ b/pkg/provider/conversionlinker/server.go @@ -19,14 +19,17 @@ func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.Conversio } var eventTriggerOpts []trigger.ConversionLinkerEventOption + 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.ConversionLinkerEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/conversionlinker/server/trigger/conversionlinkerevent.go b/pkg/provider/conversionlinker/server/trigger/conversionlinkerevent.go index e74fb38..8706ef3 100644 --- a/pkg/provider/conversionlinker/server/trigger/conversionlinkerevent.go +++ b/pkg/provider/conversionlinker/server/trigger/conversionlinkerevent.go @@ -19,6 +19,7 @@ func ConversionLinkerEventWithConsentMode(mode *tagmanager.Variable) ConversionL func NewConversionLinkerEvent(name string, opts ...ConversionLinkerEventOption) *tagmanager.Trigger { o := &ConversionLinkerEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/cookiebot/web/tag/cookiebotinitialization.go b/pkg/provider/cookiebot/web/tag/cookiebotinitialization.go index db15b72..7c5fa91 100644 --- a/pkg/provider/cookiebot/web/tag/cookiebotinitialization.go +++ b/pkg/provider/cookiebot/web/tag/cookiebotinitialization.go @@ -105,6 +105,7 @@ func NewCookiebotInitialization(name string, cfg config.Cookiebot, template *tag Type: "map", }) } + parameter = append(parameter, param) } diff --git a/pkg/provider/criteo/server.go b/pkg/provider/criteo/server.go index 8c7eb37..7580cd3 100644 --- a/pkg/provider/criteo/server.go +++ b/pkg/provider/criteo/server.go @@ -26,6 +26,7 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg if errors.Is(err, tagmanager.ErrNotFound) { l.Warn("Please install the 'Criteo Events API' Tag Template manually first") } + return err } @@ -52,14 +53,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg for event := range eventParameters { var eventTriggerOpts []trigger.CriteoEventOption + 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.CriteoEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/criteo/server/trigger/criteoevent.go b/pkg/provider/criteo/server/trigger/criteoevent.go index 4a8513a..8015f2f 100644 --- a/pkg/provider/criteo/server/trigger/criteoevent.go +++ b/pkg/provider/criteo/server/trigger/criteoevent.go @@ -23,6 +23,7 @@ func CriteoEventWithConsentMode(mode *tagmanager.Variable) CriteoEventOption { func NewCriteoEvent(name string, opts ...CriteoEventOption) *tagmanager.Trigger { o := &CriteoEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/criteo/web.go b/pkg/provider/criteo/web.go index b40c1e4..773974f 100644 --- a/pkg/provider/criteo/web.go +++ b/pkg/provider/criteo/web.go @@ -22,6 +22,7 @@ func Web(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg con if errors.Is(err, tagmanager.ErrNotFound) { l.Warn("Please install the 'Criteo User Identification' template manually first") } + return err } diff --git a/pkg/provider/criteo/web/tag/useridentification.go b/pkg/provider/criteo/web/tag/useridentification.go index b3fcfd6..801d416 100644 --- a/pkg/provider/criteo/web/tag/useridentification.go +++ b/pkg/provider/criteo/web/tag/useridentification.go @@ -29,5 +29,6 @@ func NewUserIdentification(name string, callerID, partnerID *tagmanager.Variable }, Type: utils.TemplateType(template), } + return ret } diff --git a/pkg/provider/emarsys/server.go b/pkg/provider/emarsys/server.go index 2a3f686..e54dc0c 100644 --- a/pkg/provider/emarsys/server.go +++ b/pkg/provider/emarsys/server.go @@ -52,14 +52,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg for event := range eventParameters { var eventTriggerOpts []trigger.EmarsysEventOption + 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.EmarsysEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/emarsys/server/template/emarsyswebextendtagdata.go b/pkg/provider/emarsys/server/template/emarsyswebextendtagdata.go index 8a112ae..2b229c8 100644 --- a/pkg/provider/emarsys/server/template/emarsyswebextendtagdata.go +++ b/pkg/provider/emarsys/server/template/emarsyswebextendtagdata.go @@ -163,9 +163,9 @@ function mapEventData() { } case 'view_item_list': { const prefix = createRegex('^/', 'i'); - const seperator = createRegex('/', 'i'); + const separator = createRegex('/', 'i'); mappedData.cart = serializeItems(eventData.items || []); - mappedData.category = eventData.item_list_id.replace(prefix,"").replace(seperator, " > "); + mappedData.category = eventData.item_list_id.replace(prefix,"").replace(separator, " > "); break; } case 'purchase': { diff --git a/pkg/provider/emarsys/server/trigger/emarsysevent.go b/pkg/provider/emarsys/server/trigger/emarsysevent.go index bb4d985..f0eb6b6 100644 --- a/pkg/provider/emarsys/server/trigger/emarsysevent.go +++ b/pkg/provider/emarsys/server/trigger/emarsysevent.go @@ -23,6 +23,7 @@ func EmarsysEventWithConsentMode(mode *tagmanager.Variable) EmarsysEventOption { func NewEmarsysEvent(name string, opts ...EmarsysEventOption) *tagmanager.Trigger { o := &EmarsysEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/emarsys/web/tag/emarsysinitialization.go b/pkg/provider/emarsys/web/tag/emarsysinitialization.go index 71b3be0..1f4f430 100644 --- a/pkg/provider/emarsys/web/tag/emarsysinitialization.go +++ b/pkg/provider/emarsys/web/tag/emarsysinitialization.go @@ -13,5 +13,6 @@ func NewEmarsysInitialization(name string, template *tagmanager.CustomTemplate) TagFiringOption: "oncePerEvent", Type: utils.TemplateType(template), } + return ret } diff --git a/pkg/provider/facebook/server.go b/pkg/provider/facebook/server.go index a06a2dc..c7781a8 100644 --- a/pkg/provider/facebook/server.go +++ b/pkg/provider/facebook/server.go @@ -33,8 +33,10 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg } 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 @@ -57,14 +59,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg for event := range eventParameters { var eventTriggerOpts []trigger.FacebookEventOption + 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.FacebookEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/facebook/server/trigger/facebookevent.go b/pkg/provider/facebook/server/trigger/facebookevent.go index 68a7be2..a299d30 100644 --- a/pkg/provider/facebook/server/trigger/facebookevent.go +++ b/pkg/provider/facebook/server/trigger/facebookevent.go @@ -23,6 +23,7 @@ func FacebookEventWithConsentMode(mode *tagmanager.Variable) FacebookEventOption func NewFacebookEvent(name string, opts ...FacebookEventOption) *tagmanager.Trigger { o := &FacebookEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/googleads/server.go b/pkg/provider/googleads/server.go index e083901..91896ad 100644 --- a/pkg/provider/googleads/server.go +++ b/pkg/provider/googleads/server.go @@ -53,14 +53,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg for event := range eventParameters { var eventTriggerOpts []trigger.GoogleAdsEventOption + 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.GoogleAdsEventWithConsentMode(consentVariable)) } @@ -80,14 +83,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg // remarketing if cfg.Remarketing.Enabled { var eventTriggerOpts []trigger.GoogleAdsRemarketingEventOption + 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.GoogleAdsRemarketingEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/googleads/server/tag/googleadsconversiontracking.go b/pkg/provider/googleads/server/tag/googleadsconversiontracking.go index cc923ff..f840e3a 100644 --- a/pkg/provider/googleads/server/tag/googleadsconversiontracking.go +++ b/pkg/provider/googleads/server/tag/googleadsconversiontracking.go @@ -13,6 +13,7 @@ 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 diff --git a/pkg/provider/googleads/server/trigger/googleadsevent.go b/pkg/provider/googleads/server/trigger/googleadsevent.go index 9849f87..e324b91 100644 --- a/pkg/provider/googleads/server/trigger/googleadsevent.go +++ b/pkg/provider/googleads/server/trigger/googleadsevent.go @@ -23,6 +23,7 @@ func GoogleAdsEventWithConsentMode(mode *tagmanager.Variable) GoogleAdsEventOpti func NewGoogleAdsEvent(name string, opts ...GoogleAdsEventOption) *tagmanager.Trigger { o := &GoogleAdsEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/googleads/server/trigger/googleadsremarketingevent.go b/pkg/provider/googleads/server/trigger/googleadsremarketingevent.go index 04cd084..2b700b6 100644 --- a/pkg/provider/googleads/server/trigger/googleadsremarketingevent.go +++ b/pkg/provider/googleads/server/trigger/googleadsremarketingevent.go @@ -19,6 +19,7 @@ func GoogleAdsRemarketingEventWithConsentMode(mode *tagmanager.Variable) GoogleA func NewGoogleAdsRemarketingEvent(name string, opts ...GoogleAdsRemarketingEventOption) *tagmanager.Trigger { o := &GoogleAdsRemarketingEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/googleanalytics/server.go b/pkg/provider/googleanalytics/server.go index f69218a..90b8f66 100644 --- a/pkg/provider/googleanalytics/server.go +++ b/pkg/provider/googleanalytics/server.go @@ -55,6 +55,7 @@ func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleAna if err != nil { return err } + if _, err = tm.UpsertTrigger(ctx, folder, servertrigger.NewClient(NameMeasurementProtocolGA4ClientTrigger, client)); err != nil { return err } @@ -106,14 +107,17 @@ func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleAna for event := range eventParameters { var eventTriggerOpts []trigger.GoogleAnalyticsEventOption + 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.GoogleAnalyticsEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/googleanalytics/server/trigger/googleanalyticsevent.go b/pkg/provider/googleanalytics/server/trigger/googleanalyticsevent.go index 4ca26ee..c3ab2db 100644 --- a/pkg/provider/googleanalytics/server/trigger/googleanalyticsevent.go +++ b/pkg/provider/googleanalytics/server/trigger/googleanalyticsevent.go @@ -23,6 +23,7 @@ func GoogleAnalyticsEventWithConsentMode(mode *tagmanager.Variable) GoogleAnalyt func NewGoogleAnalyticsEvent(name string, opts ...GoogleAnalyticsEventOption) *tagmanager.Trigger { o := &GoogleAnalyticsEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/googleconsent/server.go b/pkg/provider/googleconsent/server.go index 6490cac..2c33472 100644 --- a/pkg/provider/googleconsent/server.go +++ b/pkg/provider/googleconsent/server.go @@ -19,9 +19,11 @@ func ServerEnsure(ctx context.Context, tm *tagmanager.TagManager) error { if err != nil { return err } + if _, err = tm.UpsertVariable(ctx, folder, variable.NewGoogleConsentModeAdStorage(consentTemplate)); err != nil { return err } + if _, err = tm.UpsertVariable(ctx, folder, variable.NewGoogleConsentModeAnalyticsStorage(consentTemplate)); err != nil { return err } diff --git a/pkg/provider/googletag/web.go b/pkg/provider/googletag/web.go index 8f893aa..962974e 100644 --- a/pkg/provider/googletag/web.go +++ b/pkg/provider/googletag/web.go @@ -28,16 +28,19 @@ func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag) e 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(ctx, folder, variable.NewDataLayer(v)) if err != nil { return err } + eventSettings[k] = dlv } @@ -50,6 +53,7 @@ func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag) e if err != nil { return err } + if _, err = tm.UpsertTag(ctx, folder, webtag.NewGoogleTag(NameGoogleTag, tagID, settingsVariable, eventSettings)); err != nil { return err } @@ -99,5 +103,6 @@ func CreateWebDatalayerVariables(ctx context.Context, tm *tagmanager.TagManager, return nil, err } } + return variables, nil } diff --git a/pkg/provider/googletag/web/tag/googletag.go b/pkg/provider/googletag/web/tag/googletag.go index fe6c64d..3278a75 100644 --- a/pkg/provider/googletag/web/tag/googletag.go +++ b/pkg/provider/googletag/web/tag/googletag.go @@ -47,5 +47,6 @@ func NewGoogleTag(name string, tagID *tagmanager.Variable, configSettings *tagma }, Type: "googtag", } + return ret } diff --git a/pkg/provider/googletag/web/variable/googletageventsettings.go b/pkg/provider/googletag/web/variable/googletageventsettings.go index b9bc6ef..120c474 100644 --- a/pkg/provider/googletag/web/variable/googletageventsettings.go +++ b/pkg/provider/googletag/web/variable/googletageventsettings.go @@ -15,6 +15,7 @@ func NewGoogleTagEventSettings(name string, variables map[string]*tagmanager.Var for k := range variables { parameters = append(parameters, k) } + sort.Strings(parameters) list := make([]*tagmanager.Parameter, len(parameters)) diff --git a/pkg/provider/googletagmanager/server.go b/pkg/provider/googletagmanager/server.go index 2c18df3..58538c1 100644 --- a/pkg/provider/googletagmanager/server.go +++ b/pkg/provider/googletagmanager/server.go @@ -40,6 +40,7 @@ func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTag return err } } + for key, value := range cfg.ServerContaienrVariables.LookupTables { if _, err := tm.UpsertVariable(ctx, folder, commonvariable.NewLookupTable(key, value)); err != nil { return err diff --git a/pkg/provider/googletagmanager/web.go b/pkg/provider/googletagmanager/web.go index b1bc8f8..e20e2a8 100644 --- a/pkg/provider/googletagmanager/web.go +++ b/pkg/provider/googletagmanager/web.go @@ -21,6 +21,7 @@ func Web(ctx context.Context, tm *tagmanager.TagManager, cfg config.GoogleTagMan return err } } + for key, value := range cfg.WebContaienrVariables.LookupTables { if _, err := tm.UpsertVariable(ctx, folder, commonvariable.NewLookupTable(key, value)); err != nil { return err diff --git a/pkg/provider/hotjar/web/tag/hotjar.go b/pkg/provider/hotjar/web/tag/hotjar.go index 3f0a22b..610dde2 100644 --- a/pkg/provider/hotjar/web/tag/hotjar.go +++ b/pkg/provider/hotjar/web/tag/hotjar.go @@ -18,5 +18,6 @@ func NewHotjar(name string, siteID *tagmanager.Variable) *tagmanager.Tag { }, Type: "hjtc", } + return ret } diff --git a/pkg/provider/microsoftads/server.go b/pkg/provider/microsoftads/server.go index 042a2fd..d08ed58 100644 --- a/pkg/provider/microsoftads/server.go +++ b/pkg/provider/microsoftads/server.go @@ -41,14 +41,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg for event := range eventParameters { var eventTriggerOpts []trigger.ConversionEventOption + 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.ConversionEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/microsoftads/server/trigger/conversionevent.go b/pkg/provider/microsoftads/server/trigger/conversionevent.go index 5da3298..87bcdde 100644 --- a/pkg/provider/microsoftads/server/trigger/conversionevent.go +++ b/pkg/provider/microsoftads/server/trigger/conversionevent.go @@ -23,6 +23,7 @@ func ConversionEventWithConsentMode(mode *tagmanager.Variable) ConversionEventOp func NewConversionEvent(name string, opts ...ConversionEventOption) *tagmanager.Trigger { o := &ConversionEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/mixpanel/server.go b/pkg/provider/mixpanel/server.go index 8a25e03..4775d93 100644 --- a/pkg/provider/mixpanel/server.go +++ b/pkg/provider/mixpanel/server.go @@ -34,6 +34,7 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg if errors.Is(err, tagmanager.ErrNotFound) { l.Warn("Please install the 'Mixpanel' by stape-io Tag Template manually first") } + return err } @@ -50,18 +51,22 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg 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 @@ -89,18 +94,22 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg 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 @@ -128,14 +137,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg 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)) } @@ -158,18 +170,22 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg 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 @@ -197,14 +213,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg 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)) } diff --git a/pkg/provider/mixpanel/server/tag/set.go b/pkg/provider/mixpanel/server/tag/set.go index a135f7a..d46a888 100644 --- a/pkg/provider/mixpanel/server/tag/set.go +++ b/pkg/provider/mixpanel/server/tag/set.go @@ -53,6 +53,7 @@ func NewSet(name string, projectToken *tagmanager.Variable, template *tagmanager if len(params) > 0 { var list []*tagmanager.Parameter + for _, key := range slices.Sorted(maps.Keys(params)) { param := params[key] list = append(list, &tagmanager.Parameter{ @@ -71,6 +72,7 @@ func NewSet(name string, projectToken *tagmanager.Variable, template *tagmanager }, }) } + parameter = append(parameter, &tagmanager.Parameter{ Key: "userPropertiesTable", Type: "list", diff --git a/pkg/provider/mixpanel/server/tag/setonce.go b/pkg/provider/mixpanel/server/tag/setonce.go index d4bcc83..e124d86 100644 --- a/pkg/provider/mixpanel/server/tag/setonce.go +++ b/pkg/provider/mixpanel/server/tag/setonce.go @@ -53,6 +53,7 @@ func NewSetOnce(name string, projectToken *tagmanager.Variable, template *tagman if len(params) > 0 { var list []*tagmanager.Parameter + for _, key := range slices.Sorted(maps.Keys(params)) { param := params[key] list = append(list, &tagmanager.Parameter{ @@ -71,6 +72,7 @@ func NewSetOnce(name string, projectToken *tagmanager.Variable, template *tagman }, }) } + parameter = append(parameter, &tagmanager.Parameter{ Key: "userPropertiesToSetOnce", Type: "list", diff --git a/pkg/provider/mixpanel/server/tag/track.go b/pkg/provider/mixpanel/server/tag/track.go index 05ba0ad..5a6f49c 100644 --- a/pkg/provider/mixpanel/server/tag/track.go +++ b/pkg/provider/mixpanel/server/tag/track.go @@ -58,6 +58,7 @@ func NewTrack(name string, projectToken *tagmanager.Variable, template *tagmanag if len(params) > 0 { var list []*tagmanager.Parameter + for _, key := range slices.Sorted(maps.Keys(params)) { param := params[key] list = append(list, &tagmanager.Parameter{ @@ -76,6 +77,7 @@ func NewTrack(name string, projectToken *tagmanager.Variable, template *tagmanag }, }) } + parameter = append(parameter, &tagmanager.Parameter{ Key: "trackParameters", Type: "list", diff --git a/pkg/provider/mixpanel/server/trigger/event.go b/pkg/provider/mixpanel/server/trigger/event.go index 9cf6dad..a8ced2f 100644 --- a/pkg/provider/mixpanel/server/trigger/event.go +++ b/pkg/provider/mixpanel/server/trigger/event.go @@ -23,6 +23,7 @@ func EventWithConsentMode(mode *tagmanager.Variable) EventOption { func NewEvent(name string, opts ...EventOption) *tagmanager.Trigger { o := &EventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/pinterest/server.go b/pkg/provider/pinterest/server.go index e5b443c..a4745e2 100644 --- a/pkg/provider/pinterest/server.go +++ b/pkg/provider/pinterest/server.go @@ -25,6 +25,7 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg if errors.Is(err, tagmanager.ErrNotFound) { l.Warn("Please install the 'Pinterest API for Conversions Ta' Tag Template manually first") } + return err } @@ -36,14 +37,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg 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)) } diff --git a/pkg/provider/pinterest/server/tag/conversion.go b/pkg/provider/pinterest/server/tag/conversion.go index d392777..3064e83 100644 --- a/pkg/provider/pinterest/server/tag/conversion.go +++ b/pkg/provider/pinterest/server/tag/conversion.go @@ -40,6 +40,7 @@ func NewConversion(name string, cfg config.Pinterest, template *tagmanager.Custo if testMode { return "log" } + return "donotlog" }(cfg.TestModeEnabled), }, diff --git a/pkg/provider/pinterest/server/trigger/conversion.go b/pkg/provider/pinterest/server/trigger/conversion.go index fcf69f5..7b4e055 100644 --- a/pkg/provider/pinterest/server/trigger/conversion.go +++ b/pkg/provider/pinterest/server/trigger/conversion.go @@ -31,6 +31,7 @@ func ConversionWithConsentMode(mode *tagmanager.Variable) EventOption { func NewConversion(name string, opts ...EventOption) *tagmanager.Trigger { o := &EventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/tracify/server.go b/pkg/provider/tracify/server.go index 31b7417..9af7b4d 100644 --- a/pkg/provider/tracify/server.go +++ b/pkg/provider/tracify/server.go @@ -46,14 +46,17 @@ func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg for event := range eventParameters { var eventTriggerOpts []trigger.TracifyEventOption + 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.TracifyEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/tracify/server/trigger/tracifysevent.go b/pkg/provider/tracify/server/trigger/tracifysevent.go index 6b929e5..ac492f9 100644 --- a/pkg/provider/tracify/server/trigger/tracifysevent.go +++ b/pkg/provider/tracify/server/trigger/tracifysevent.go @@ -23,6 +23,7 @@ func TracifyEventWithConsentMode(mode *tagmanager.Variable) TracifyEventOption { func NewTracifyEvent(name string, opts ...TracifyEventOption) *tagmanager.Trigger { o := &TracifyEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/provider/umami/server.go b/pkg/provider/umami/server.go index a5bfec1..e11de31 100644 --- a/pkg/provider/umami/server.go +++ b/pkg/provider/umami/server.go @@ -33,14 +33,17 @@ func Server(ctx context.Context, tm *tagmanager.TagManager, cfg config.Umami) er for event := range eventParameters { var eventTriggerOpts []trigger.UmamiEventOption + 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.UmamiEventWithConsentMode(consentVariable)) } diff --git a/pkg/provider/umami/server/trigger/umamievent.go b/pkg/provider/umami/server/trigger/umamievent.go index 23dafc6..b42ebc3 100644 --- a/pkg/provider/umami/server/trigger/umamievent.go +++ b/pkg/provider/umami/server/trigger/umamievent.go @@ -23,6 +23,7 @@ func UmamiEventWithConsentMode(mode *tagmanager.Variable) UmamiEventOption { func NewUmamiEvent(name string, opts ...UmamiEventOption) *tagmanager.Trigger { o := &UmamiEventOptions{} + for _, opt := range opts { if opt != nil { opt(o) diff --git a/pkg/pterm/sloghandler.go b/pkg/pterm/sloghandler.go index e5b83b1..05a8cad 100644 --- a/pkg/pterm/sloghandler.go +++ b/pkg/pterm/sloghandler.go @@ -75,6 +75,7 @@ func (s *SlogHandler) Handle(ctx context.Context, record slog.Record) error { func (s *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { newS := *s newS.attrs = attrs + return &newS } diff --git a/pkg/tagmanager/common/variable/lookuptable.go b/pkg/tagmanager/common/variable/lookuptable.go index 24fea1f..a784412 100644 --- a/pkg/tagmanager/common/variable/lookuptable.go +++ b/pkg/tagmanager/common/variable/lookuptable.go @@ -17,6 +17,7 @@ func NewLookupTable(name string, data config.LookupTable) *tagmanager.Variable { { keys := slices.AppendSeq(make([]string, 0, len(data.KeyTable)), maps.Keys(data.KeyTable)) slices.Sort(keys) + for _, k := range keys { v := data.KeyTable[k] list = append(list, &tagmanager.Parameter{ @@ -36,8 +37,10 @@ func NewLookupTable(name string, data config.LookupTable) *tagmanager.Variable { }) } } + keys := slices.AppendSeq(make([]string, 0, len(data.ValueTable)), maps.Keys(data.ValueTable)) slices.Sort(keys) + for _, k := range keys { v := data.ValueTable[k] list = append(list, &tagmanager.Parameter{ @@ -56,6 +59,7 @@ func NewLookupTable(name string, data config.LookupTable) *tagmanager.Variable { }, }) } + return &tagmanager.Variable{ Name: LookupTableName(name), Parameter: []*tagmanager.Parameter{ diff --git a/pkg/tagmanager/tagmanager.go b/pkg/tagmanager/tagmanager.go index 1cdaa28..d471d3a 100644 --- a/pkg/tagmanager/tagmanager.go +++ b/pkg/tagmanager/tagmanager.go @@ -60,6 +60,7 @@ func (l AccessedMap[T]) Get(key string) T { if l.Has(key) { l.keys[key] = true } + return l.data[key] } @@ -70,11 +71,13 @@ func (l AccessedMap[T]) Set(key string, value T) { func (l AccessedMap[T]) Misssed() map[string]T { ret := map[string]T{} + for k := range l.data { if !l.keys[k] { ret[k] = l.data[k] } } + return ret } @@ -145,6 +148,7 @@ func New(ctx context.Context, l *slog.Logger, accountID string, container config func (t *TagManager) Missed() map[string][]string { ret := map[string][]string{} + if t.clients != nil { for _, i2 := range t.clients.Misssed() { ret["Clients"] = append(ret["Clients"], i2.Name) @@ -208,6 +212,7 @@ func (t *TagManager) Missed() map[string][]string { } else { ret["Transformations"] = append(ret["Transformations"], "All") } + return ret } @@ -227,6 +232,7 @@ func (t *TagManager) Service() *tagmanager.Service { if t.requestThrottler != nil { <-t.requestThrottler.C } + return t.service } @@ -244,6 +250,7 @@ func (t *TagManager) WorkspacePath() string { func (t *TagManager) Notes(v any) string { var hash string + if v != nil { if out, err := json.Marshal(v); err != nil { t.l.Warn("failed to marshal tag manager", "error", err) @@ -251,6 +258,7 @@ func (t *TagManager) Notes(v any) string { hash = fmt.Sprintf(" - %x", md5.Sum(out)) //nolint: gosec //just a checksum } } + return t.notes + hash } @@ -264,12 +272,15 @@ func (t *TagManager) EnsureWorkspaceID(ctx context.Context) error { if name == "" { name = "Default Workspace" } + workspace, err := t.GetWorkspace(ctx, name) if err != nil { return errors.Wrap(err, "failed to get default workspace") } + t.container.WorkspaceID = workspace.WorkspaceId } + return nil } @@ -289,6 +300,7 @@ func (t *TagManager) LookupClient(ctx context.Context, name string) (*tagmanager func (t *TagManager) LoadClients(ctx context.Context) (*AccessedMap[*tagmanager.Client], error) { if t.clients == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Client") + r, err := t.Service().Accounts.Containers.Workspaces.Clients.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -298,6 +310,7 @@ func (t *TagManager) LoadClients(ctx context.Context) (*AccessedMap[*tagmanager. for _, value := range r.Client { res[value.Name] = value } + t.clients = NewAccessedMap(res) } @@ -320,6 +333,7 @@ func (t *TagManager) LookupFolder(ctx context.Context, name string) (*tagmanager func (t *TagManager) LoadFolders(ctx context.Context) (*AccessedMap[*tagmanager.Folder], error) { if t.folders == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Folder") + r, err := t.Service().Accounts.Containers.Workspaces.Folders.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -329,8 +343,10 @@ func (t *TagManager) LoadFolders(ctx context.Context) (*AccessedMap[*tagmanager. for _, value := range r.Folder { res[value.Name] = value } + t.folders = NewAccessedMap(res) } + return t.folders, nil } @@ -350,6 +366,7 @@ func (t *TagManager) LookupVariable(ctx context.Context, name string) (*tagmanag func (t *TagManager) LoadVariables(ctx context.Context) (*AccessedMap[*tagmanager.Variable], error) { if t.variables == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Variable") + r, err := t.Service().Accounts.Containers.Workspaces.Variables.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -359,6 +376,7 @@ func (t *TagManager) LoadVariables(ctx context.Context) (*AccessedMap[*tagmanage for _, value := range r.Variable { res[value.Name] = value } + t.variables = NewAccessedMap(res) } @@ -407,6 +425,7 @@ func (t *TagManager) GetBuiltInVariable(ctx context.Context, typeName string) (* func (t *TagManager) LoadWorkspaces(ctx context.Context) (*AccessedMap[*tagmanager.Workspace], error) { if t.workspaces == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Workspaces") + r, err := t.Service().Accounts.Containers.Workspaces.List(t.ContainerPath()).Context(ctx).Do() if err != nil { return nil, err @@ -416,6 +435,7 @@ func (t *TagManager) LoadWorkspaces(ctx context.Context) (*AccessedMap[*tagmanag for _, value := range r.Workspace { res[value.Name] = value } + t.workspaces = NewAccessedMap(res) } @@ -425,6 +445,7 @@ func (t *TagManager) LoadWorkspaces(ctx context.Context) (*AccessedMap[*tagmanag func (t *TagManager) LoadEnvironments(ctx context.Context) (*AccessedMap[*tagmanager.Environment], error) { if t.environments == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Environments") + r, err := t.Service().Accounts.Containers.Environments.List(t.ContainerPath()).Context(ctx).Do() if err != nil { return nil, err @@ -434,6 +455,7 @@ func (t *TagManager) LoadEnvironments(ctx context.Context) (*AccessedMap[*tagman for _, value := range r.Environment { res[value.Type] = value } + t.environments = NewAccessedMap(res) } @@ -443,6 +465,7 @@ func (t *TagManager) LoadEnvironments(ctx context.Context) (*AccessedMap[*tagman func (t *TagManager) LoadBuiltInVariables(ctx context.Context) (*AccessedMap[*tagmanager.BuiltInVariable], error) { if t.builtInVariables == nil { t.l.Info("└ ⬇︎ Loading list", "type", "BuiltInVariable") + r, err := t.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -452,6 +475,7 @@ func (t *TagManager) LoadBuiltInVariables(ctx context.Context) (*AccessedMap[*ta for _, value := range r.BuiltInVariable { res[value.Type] = value } + t.builtInVariables = NewAccessedMap(res) } @@ -500,6 +524,7 @@ func (t *TagManager) LookupTransformation(ctx context.Context, name string) (*ta func (t *TagManager) LoadTriggers(ctx context.Context) (*AccessedMap[*tagmanager.Trigger], error) { if t.triggers == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Trigger") + r, err := t.Service().Accounts.Containers.Workspaces.Triggers.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -509,6 +534,7 @@ func (t *TagManager) LoadTriggers(ctx context.Context) (*AccessedMap[*tagmanager for _, value := range r.Trigger { res[value.Name] = value } + t.triggers = NewAccessedMap(res) } @@ -531,6 +557,7 @@ func (t *TagManager) LookupTag(ctx context.Context, name string) (*tagmanager.Ta func (t *TagManager) LoadTags(ctx context.Context) (*AccessedMap[*tagmanager.Tag], error) { if t.tags == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Tag") + r, err := t.Service().Accounts.Containers.Workspaces.Tags.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -540,6 +567,7 @@ func (t *TagManager) LoadTags(ctx context.Context) (*AccessedMap[*tagmanager.Tag for _, value := range r.Tag { res[value.Name] = value } + t.tags = NewAccessedMap(res) } @@ -562,6 +590,7 @@ func (t *TagManager) CustomTemplate(ctx context.Context, name string) (*tagmanag func (t *TagManager) LoadCustomTemplates(ctx context.Context) (*AccessedMap[*tagmanager.CustomTemplate], error) { if t.customTemplates == nil { t.l.Info("└ ⬇︎ Loading list", "type", "CustomTemplate") + r, err := t.Service().Accounts.Containers.Workspaces.Templates.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -571,6 +600,7 @@ func (t *TagManager) LoadCustomTemplates(ctx context.Context) (*AccessedMap[*tag for _, value := range r.Template { res[value.Name] = value } + t.customTemplates = NewAccessedMap(res) } @@ -580,6 +610,7 @@ func (t *TagManager) LoadCustomTemplates(ctx context.Context) (*AccessedMap[*tag func (t *TagManager) LoadTransformations(ctx context.Context) (*AccessedMap[*tagmanager.Transformation], error) { if t.transformations == nil { t.l.Info("└ ⬇︎ Loading list", "type", "Transformation") + r, err := t.Service().Accounts.Containers.Workspaces.Transformations.List(t.WorkspacePath()).Context(ctx).Do() if err != nil { return nil, err @@ -589,6 +620,7 @@ func (t *TagManager) LoadTransformations(ctx context.Context) (*AccessedMap[*tag for _, value := range r.Transformation { res[value.Name] = value } + t.transformations = NewAccessedMap(res) } @@ -611,8 +643,10 @@ func (t *TagManager) UpsertClient(ctx context.Context, folder *tagmanager.Folder } var value *tagmanager.Client + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Clients.Create(t.WorkspacePath(), item).Context(ctx).Do() t.clients.Set(item.Name, value) } else if item.Notes == cache.Notes { @@ -622,10 +656,12 @@ func (t *TagManager) UpsertClient(ctx context.Context, folder *tagmanager.Folder value, err = t.Service().Accounts.Containers.Workspaces.Clients.Update(t.WorkspacePath()+"/clients/"+cache.ClientId, item).Context(ctx).Do() t.clients.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } @@ -648,8 +684,10 @@ func (t *TagManager) UpsertTransformation(ctx context.Context, folder *tagmanage } var value *tagmanager.Transformation + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Transformations.Create(t.WorkspacePath(), item).Context(ctx).Do() t.transformations.Set(item.Name, value) } else if item.Notes == cache.Notes { @@ -659,12 +697,15 @@ func (t *TagManager) UpsertTransformation(ctx context.Context, folder *tagmanage value, err = t.Service().Accounts.Containers.Workspaces.Transformations.Update(t.WorkspacePath()+"/transformations/"+cache.TransformationId, item).Context(ctx).Do() t.transformations.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } + return t.LookupTransformation(ctx, item.Name) } @@ -686,8 +727,10 @@ func (t *TagManager) UpsertFolder(ctx context.Context, name string) (*tagmanager } var value *tagmanager.Folder + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Folders.Create(t.WorkspacePath(), item).Context(ctx).Do() t.folders.Set(item.Name, value) } else if item.Notes == cache.Notes { @@ -697,10 +740,12 @@ func (t *TagManager) UpsertFolder(ctx context.Context, name string) (*tagmanager value, err = t.Service().Accounts.Containers.Workspaces.Folders.Update(t.WorkspacePath()+"/folders/"+cache.FolderId, item).Context(ctx).Do() t.folders.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } @@ -723,8 +768,10 @@ func (t *TagManager) UpsertVariable(ctx context.Context, folder *tagmanager.Fold } var value *tagmanager.Variable + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Variables.Create(t.WorkspacePath(), item).Context(ctx).Do() t.variables.Set(item.Name, value) } else if item.Notes == cache.Notes { @@ -734,10 +781,12 @@ func (t *TagManager) UpsertVariable(ctx context.Context, folder *tagmanager.Fold value, err = t.Service().Accounts.Containers.Workspaces.Variables.Update(t.WorkspacePath()+"/variables/"+cache.VariableId, item).Context(ctx).Do() t.variables.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } @@ -758,6 +807,7 @@ func (t *TagManager) EnableBuiltInVariable(ctx context.Context, typeName string) } l.Info("└ 🚀 New") + resp, err := t.Service().Accounts.Containers.Workspaces.BuiltInVariables.Create(t.WorkspacePath()).Type(typeName).Context(ctx).Do() if err != nil { return nil, errors.Wrap(err, "failed to create built-in variable") @@ -786,8 +836,10 @@ func (t *TagManager) UpsertTrigger(ctx context.Context, folder *tagmanager.Folde } var value *tagmanager.Trigger + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Triggers.Create(t.WorkspacePath(), item).Context(ctx).Do() t.triggers.Set(item.Name, value) } else if item.Notes == cache.Notes { @@ -797,10 +849,12 @@ func (t *TagManager) UpsertTrigger(ctx context.Context, folder *tagmanager.Folde value, err = t.Service().Accounts.Containers.Workspaces.Triggers.Update(t.WorkspacePath()+"/triggers/"+cache.TriggerId, item).Context(ctx).Do() t.triggers.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } @@ -823,8 +877,10 @@ func (t *TagManager) UpsertTag(ctx context.Context, folder *tagmanager.Folder, i } var value *tagmanager.Tag + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Tags.Create(t.WorkspacePath(), item).Context(ctx).Do() t.tags.Set(item.Name, value) } else if item.Notes == cache.Notes { @@ -834,12 +890,15 @@ func (t *TagManager) UpsertTag(ctx context.Context, folder *tagmanager.Folder, i value, err = t.Service().Accounts.Containers.Workspaces.Tags.Update(t.WorkspacePath()+"/tags/"+cache.TagId, item).Context(ctx).Do() t.tags.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } + return t.LookupTag(ctx, item.Name) } @@ -856,8 +915,10 @@ func (t *TagManager) UpsertCustomTemplate(ctx context.Context, item *tagmanager. } var value *tagmanager.CustomTemplate + if cache == nil { l.Info("└ 🚀 New") + value, err = t.Service().Accounts.Containers.Workspaces.Templates.Create(t.WorkspacePath(), item).Context(ctx).Do() t.customTemplates.Set(item.Name, value) } else if item.TemplateData == cache.TemplateData { @@ -867,11 +928,14 @@ func (t *TagManager) UpsertCustomTemplate(ctx context.Context, item *tagmanager. value, err = t.Service().Accounts.Containers.Workspaces.Templates.Update(t.WorkspacePath()+"/templates/"+cache.TemplateId, item).Context(ctx).Do() t.customTemplates.Set(item.Name, value) } + if err != nil { if out, err := json.MarshalIndent(item, "", " "); err == nil { l.Debug(string(out)) } + return nil, err } + return t.CustomTemplate(ctx, item.Name) } diff --git a/pkg/typescript/generator/generate.go b/pkg/typescript/generator/generate.go index e01dfb4..3be475b 100644 --- a/pkg/typescript/generator/generate.go +++ b/pkg/typescript/generator/generate.go @@ -21,6 +21,7 @@ func Generate(l *slog.Logger, ctpl *contemplate.Contemplate, opts ...Option) (ma o := Options{ PackageNameReplacer: strings.NewReplacer(".", "_", "/", "_", "-", "_"), } + for _, opt := range opts { if opt != nil { opt(&o) @@ -30,13 +31,16 @@ func Generate(l *slog.Logger, ctpl *contemplate.Contemplate, opts ...Option) (ma ret := make(map[string]*code.File, len(ctpl.Packages)) for path, pkg := range ctpl.Packages { l.Debug("👷 adding package", "name", pkg.Name(), "path", pkg.Path()) + inst := NewPackage(l, ctpl, pkg, o.PackageNameReplacer) if err := inst.Generate(); err != nil { return nil, err } + if len(inst.Code().Body().Parts) > 0 { ret[o.PackageNameReplacer.Replace(path)+".ts"] = inst.Code() } } + return ret, nil } diff --git a/pkg/typescript/generator/package.go b/pkg/typescript/generator/package.go index de58bdf..7290c81 100644 --- a/pkg/typescript/generator/package.go +++ b/pkg/typescript/generator/package.go @@ -43,6 +43,7 @@ func NewPackage(l *slog.Logger, goctpl *contemplate.Contemplate, pkg *contemplat code: code.NewFile(), imports: typescript.NewImports(), } + return inst } @@ -70,6 +71,7 @@ func (p *Package) IsSesamyEvent(expr ast.Expr) bool { } } } + return false } @@ -78,6 +80,7 @@ func (p *Package) Generate() error { p.code.Annotations().Sprintf(`// Code generated by sesamy. DO NOT EDIT.`) var err error + for scopeTypeName, scopeType := range p.pkg.ScopeTypes() { switch t := scopeType.Type().Underlying().(type) { case *types.Struct: @@ -87,6 +90,7 @@ func (p *Package) Generate() error { } else { err = p.renderStruct(scopeType, scopeTypeName, t) } + if err != nil { return err } @@ -164,8 +168,11 @@ func (p *Package) renderSesamyEvent(v ast.Expr) error { p.imports.Import("@foomo/sesamy").AddModule("collect") p.imports.Import("./github_com_foomo_sesamy_go_pkg_sesamy").AddModule("EventName") - var typeName string - var typeType string + var ( + typeName string + typeType string + ) + if inputIdent := assume.T[*ast.Ident](v); inputIdent != nil { typeName = inputIdent.Name if inputTypeSpec := assume.T[*ast.TypeSpec](inputIdent.Obj.Decl); inputTypeSpec != nil { @@ -188,6 +195,7 @@ export const {0} = ( typeType, typeName, ) + return nil } @@ -260,6 +268,7 @@ func (p *Package) tsDeclFromTypeParamList(v *types.TypeParamList) string { if v == nil { return "" } + params := make([]string, v.Len()) for i := range v.Len() { params[i] = p.tsDeclFromTypeParam(v.At(i)) @@ -318,6 +327,7 @@ func (p *Package) tsTypeFromTypeName(t *types.TypeName) string { p.imports.Import("./" + tsPkg).SetDefault("type * as " + tsPkg) name = tsPkg + "." + name } + return name } diff --git a/pkg/typescript/import.go b/pkg/typescript/import.go index 4d79418..2c79add 100644 --- a/pkg/typescript/import.go +++ b/pkg/typescript/import.go @@ -47,6 +47,7 @@ func (i *Import) SetDefault(v string) { func (i *Import) Modules() []string { ret := slices.AppendSeq(make([]string, 0, len(i.modules)), maps.Keys(i.modules)) slices.Sort(ret) + return ret } @@ -63,6 +64,7 @@ func (i *Import) Alias(name string) string { if v, ok := i.modules[name]; ok { return v } + return "" } @@ -97,11 +99,14 @@ func (i *Import) String() string { if alias != "" { name += " as " + alias } + if i.IsType(name) { name = "type " + name } + modules = append(modules, name) } + defModules = append(defModules, "{ "+strings.Join(modules, ", ")+" }") } diff --git a/pkg/typescript/imports.go b/pkg/typescript/imports.go index ecf2c47..abe8180 100644 --- a/pkg/typescript/imports.go +++ b/pkg/typescript/imports.go @@ -26,6 +26,7 @@ func (i *Imports) Import(location string) *Import { if _, ok := i.imports[location]; !ok { i.imports[location] = NewImport(location) } + return i.imports[location] } diff --git a/pkg/utils/contemplate.go b/pkg/utils/contemplate.go index 3e58079..5727f70 100644 --- a/pkg/utils/contemplate.go +++ b/pkg/utils/contemplate.go @@ -28,6 +28,7 @@ func LoadEventParams(ctx context.Context, cfg contemplate.Config) (map[string]ma if err != nil { return nil, errors.Wrap(err, "failed to load event params: "+cfgPkg.Path+"."+typ) } + ret[strcase.SnakeCase(typ)] = eventParams } } @@ -37,27 +38,34 @@ func LoadEventParams(ctx context.Context, cfg contemplate.Config) (map[string]ma func getEventParams(obj types.Object) (map[string]string, error) { ret := map[string]string{} + if obj == nil { return nil, errors.New("obj is nil") } + if obj.Type() == nil { return nil, errors.New("object is not a type: " + obj.String()) } + if obj.Type().Underlying() == nil { return nil, errors.New("underlying object is not a type: " + obj.Type().String()) } + if eventStruct := assume.T[*types.Struct](obj.Type().Underlying()); eventStruct != nil { - for i := range eventStruct.NumFields() { - if eventField := eventStruct.Field(i); eventField.Name() == "Params" { + for eventField := range eventStruct.Fields() { + if eventField.Name() == "Params" { if paramsStruct := assume.T[*types.Struct](eventField.Type().Underlying()); paramsStruct != nil { for j := range paramsStruct.NumFields() { - var name string - var value string + var ( + name string + value string + ) tag, err := ParseStructTagName(paramsStruct.Tag(j), "json") if err != nil { return nil, errors.Wrapf(err, "failed to parse tag `%s`", paramsStruct.Tag(j)) } + name = tag value = "eventModel." + tag @@ -65,11 +73,13 @@ func getEventParams(obj types.Object) (map[string]string, error) { if tag, err := ParseStructTagName(paramsStruct.Tag(j), "dlv"); err == nil { value = tag } + ret[name] = value } } } } } + return ret, nil } diff --git a/pkg/utils/highlight.go b/pkg/utils/highlight.go index 6faaa19..8d6a3ab 100644 --- a/pkg/utils/highlight.go +++ b/pkg/utils/highlight.go @@ -21,9 +21,11 @@ func Highlight(source string) string { if l == nil { l = lexers.Analyse(source) } + if l == nil { l = lexers.Fallback } + l = chroma.Coalesce(l) // Determine formatter. @@ -70,6 +72,7 @@ func (w *numberWriter) Write(p []byte) (int, error) { ) for i, c := range original { tokenLen++ + if c != '\n' { continue } @@ -82,11 +85,13 @@ func (w *numberWriter) Write(p []byte) (int, error) { if w.currentLine > 9999 { format = "%d |\t%s%s" } + format = "\033[34m" + format + "\033[0m" if _, err := fmt.Fprintf(w.w, format, w.currentLine, string(w.buf), string(token)); err != nil { return i + 1, err } + w.buf = w.buf[:0] w.currentLine++ } @@ -94,5 +99,6 @@ func (w *numberWriter) Write(p []byte) (int, error) { if len(p) > 0 { w.buf = append(w.buf, p...) } + return len(original), nil } diff --git a/pkg/utils/tag.go b/pkg/utils/tag.go index a5752a5..e4affb1 100644 --- a/pkg/utils/tag.go +++ b/pkg/utils/tag.go @@ -8,8 +8,10 @@ func Tag(name string, tags []string) bool { if len(tags) == 0 { return true } + if len(tags) > 0 && !slices.Contains(tags, "-"+name) && slices.Contains(tags, name) { return true } + return false } diff --git a/pkg/utils/tagmanager.go b/pkg/utils/tagmanager.go index 5723023..5a0e90f 100644 --- a/pkg/utils/tagmanager.go +++ b/pkg/utils/tagmanager.go @@ -10,6 +10,7 @@ func TemplateType(template *tagmanager.CustomTemplate) string { if template.GalleryReference != nil && template.GalleryReference.GalleryTemplateId != "" && !template.GalleryReference.IsModified { return fmt.Sprintf("cvt_%s", template.GalleryReference.GalleryTemplateId) } + return fmt.Sprintf("cvt_%s_%s", template.ContainerId, template.TemplateId) } @@ -18,5 +19,6 @@ func TriggerIDs(triggers []*tagmanager.Trigger) []string { for i, trigger := range triggers { ret[i] = trigger.TriggerId } + return ret } diff --git a/test/tagmanager/client_test.go b/test/tagmanager/client_test.go index 08f791f..980d55f 100644 --- a/test/tagmanager/client_test.go +++ b/test/tagmanager/client_test.go @@ -10,7 +10,9 @@ import ( func dump(t *testing.T, i interface{ MarshalJSON() ([]byte, error) }) { t.Helper() + var ret bytes.Buffer + out, err := i.MarshalJSON() require.NoError(t, err) require.NoError(t, json.Indent(&ret, out, "", " ")) diff --git a/test/tagmanager/clientserver_test.go b/test/tagmanager/clientserver_test.go index bf4b22c..5d482bf 100644 --- a/test/tagmanager/clientserver_test.go +++ b/test/tagmanager/clientserver_test.go @@ -31,7 +31,7 @@ func TestNewClient_Server(t *testing.T) { WorkspaceID: os.Getenv("TEST_SERVER_WORKSPACE_ID"), }, tagmanager.WithClientOptions( - option.WithCredentialsFile(os.Getenv("TEST_CREDENTIALS_FILE")), + option.WithAuthCredentialsFile(option.ServiceAccount, os.Getenv("TEST_CREDENTIALS_FILE")), ), ) require.NoError(t, err) diff --git a/test/tagmanager/clientweb_test.go b/test/tagmanager/clientweb_test.go index b1b696a..00e614a 100644 --- a/test/tagmanager/clientweb_test.go +++ b/test/tagmanager/clientweb_test.go @@ -31,7 +31,7 @@ func TestNewClient_Web(t *testing.T) { WorkspaceID: os.Getenv("TEST_WEB_WORKSPACE_ID"), }, tagmanager.WithClientOptions( - option.WithCredentialsFile(os.Getenv("TEST_CREDENTIALS_FILE")), + option.WithAuthCredentialsFile(option.ServiceAccount, os.Getenv("TEST_CREDENTIALS_FILE")), ), ) require.NoError(t, err) @@ -58,6 +58,7 @@ func TestNewClient_Web(t *testing.T) { folderID := "25" { // --- Folders --- name := "Sesamy" + t.Run("list folders", func(t *testing.T) { cmd := c.Service().Accounts.Containers.Workspaces.Folders.List(c.WorkspacePath()) if r, err := cmd.Do(); assert.NoError(t, err) { @@ -87,6 +88,7 @@ func TestNewClient_Web(t *testing.T) { return } } + t.Error("not found") } }) @@ -127,6 +129,7 @@ func TestNewClient_Web(t *testing.T) { { // --- Triggers --- name := "login" + t.Run("list triggers", func(t *testing.T) { cmd := c.Service().Accounts.Containers.Workspaces.Triggers.List(c.WorkspacePath()) if r, err := cmd.Do(); assert.NoError(t, err) { @@ -175,6 +178,7 @@ func TestNewClient_Web(t *testing.T) { return } } + t.Error("not found") } }) @@ -188,6 +192,7 @@ func TestNewClient_Web(t *testing.T) { { // --- Tags --- name := "login" + t.Run("list tags", func(t *testing.T) { cmd := c.Service().Accounts.Containers.Workspaces.Tags.List(c.WorkspacePath()) if r, err := cmd.Do(); assert.NoError(t, err) {