marketing-toolbox 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. marketing_toolbox-0.1.0/.github/workflows/ci.yml +42 -0
  2. marketing_toolbox-0.1.0/.github/workflows/release.yml +185 -0
  3. marketing_toolbox-0.1.0/.gitignore +19 -0
  4. marketing_toolbox-0.1.0/LICENSE +21 -0
  5. marketing_toolbox-0.1.0/PKG-INFO +175 -0
  6. marketing_toolbox-0.1.0/README.md +160 -0
  7. marketing_toolbox-0.1.0/docs/authentication.md +71 -0
  8. marketing_toolbox-0.1.0/docs/cli-contract.md +50 -0
  9. marketing_toolbox-0.1.0/docs/releasing.md +126 -0
  10. marketing_toolbox-0.1.0/docs/specification/README.md +22 -0
  11. marketing_toolbox-0.1.0/docs/specification/v1/README.md +24 -0
  12. marketing_toolbox-0.1.0/docs/specification/v1/ga4adminctl/catalog.md +105 -0
  13. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/audience-exports-create.md +93 -0
  14. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/audience-exports-query.md +52 -0
  15. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-batch-pivot-run.md +47 -0
  16. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-batch-run.md +49 -0
  17. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-pivot-run.md +47 -0
  18. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-run.md +47 -0
  19. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/audience-exports-create/invalid-cross-property-audience.json +4 -0
  20. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/audience-exports-create/invalid-output-only-field.json +5 -0
  21. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/audience-exports-create/valid-basic-request.json +6 -0
  22. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-pivot-run/invalid-nested-property.json +9 -0
  23. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-pivot-run/invalid-no-pivots.json +3 -0
  24. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-pivot-run/valid-two-pivot-reports-request.json +16 -0
  25. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/invalid-empty-requests.json +3 -0
  26. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/invalid-nested-property.json +8 -0
  27. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/invalid-six-requests.json +10 -0
  28. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/valid-two-reports-request.json +17 -0
  29. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/valid-two-reports-success.json +21 -0
  30. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/invalid-no-metrics.json +4 -0
  31. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/invalid-no-pivot-limit.json +5 -0
  32. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/valid-two-pivots-request.json +16 -0
  33. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/valid-two-pivots-success.json +34 -0
  34. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-body-property.json +4 -0
  35. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-limit.json +4 -0
  36. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-minute-range-order.json +4 -0
  37. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-three-minute-ranges.json +4 -0
  38. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/valid-basic-request.json +6 -0
  39. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/invalid-body-property.json +14 -0
  40. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/invalid-unknown-field.json +14 -0
  41. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/valid-basic-request.json +20 -0
  42. marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/valid-basic-success.json +37 -0
  43. marketing_toolbox-0.1.0/docs/specification/v1/gtmctl/catalog.md +215 -0
  44. marketing_toolbox-0.1.0/docs/specification/v1/gtmctl/command-contracts.md +92 -0
  45. marketing_toolbox-0.1.0/docs/specification/v1/requirements.md +51 -0
  46. marketing_toolbox-0.1.0/docs/specification/v1/sdk-backed-introspection.md +79 -0
  47. marketing_toolbox-0.1.0/packaging/ga4adminctl/LICENSE +21 -0
  48. marketing_toolbox-0.1.0/packaging/ga4adminctl/README.md +9 -0
  49. marketing_toolbox-0.1.0/packaging/ga4adminctl/pyproject.toml +22 -0
  50. marketing_toolbox-0.1.0/packaging/ga4adminctl/src/ga4adminctl_alias/__init__.py +1 -0
  51. marketing_toolbox-0.1.0/packaging/ga4datactl/LICENSE +21 -0
  52. marketing_toolbox-0.1.0/packaging/ga4datactl/README.md +9 -0
  53. marketing_toolbox-0.1.0/packaging/ga4datactl/pyproject.toml +22 -0
  54. marketing_toolbox-0.1.0/packaging/ga4datactl/src/ga4datactl_alias/__init__.py +1 -0
  55. marketing_toolbox-0.1.0/packaging/gtmctl/LICENSE +21 -0
  56. marketing_toolbox-0.1.0/packaging/gtmctl/README.md +9 -0
  57. marketing_toolbox-0.1.0/packaging/gtmctl/pyproject.toml +22 -0
  58. marketing_toolbox-0.1.0/packaging/gtmctl/src/gtmctl_alias/__init__.py +1 -0
  59. marketing_toolbox-0.1.0/pyproject.toml +61 -0
  60. marketing_toolbox-0.1.0/src/ga4adminctl/__init__.py +3 -0
  61. marketing_toolbox-0.1.0/src/ga4adminctl/cli.py +108 -0
  62. marketing_toolbox-0.1.0/src/ga4adminctl/commands/__init__.py +1 -0
  63. marketing_toolbox-0.1.0/src/ga4adminctl/commands/_common.py +98 -0
  64. marketing_toolbox-0.1.0/src/ga4adminctl/commands/accounts.py +293 -0
  65. marketing_toolbox-0.1.0/src/ga4adminctl/commands/properties.py +340 -0
  66. marketing_toolbox-0.1.0/src/ga4adminctl/commands/resources.py +688 -0
  67. marketing_toolbox-0.1.0/src/ga4adminctl/commands/sdk.py +271 -0
  68. marketing_toolbox-0.1.0/src/ga4adminctl/commands/secrets.py +247 -0
  69. marketing_toolbox-0.1.0/src/ga4adminctl/foundation/__init__.py +1 -0
  70. marketing_toolbox-0.1.0/src/ga4adminctl/foundation/errors.py +43 -0
  71. marketing_toolbox-0.1.0/src/ga4adminctl/foundation/serialization.py +40 -0
  72. marketing_toolbox-0.1.0/src/ga4adminctl/foundation/validation.py +136 -0
  73. marketing_toolbox-0.1.0/src/ga4adminctl/operations/__init__.py +1 -0
  74. marketing_toolbox-0.1.0/src/ga4adminctl/operations/access.py +113 -0
  75. marketing_toolbox-0.1.0/src/ga4adminctl/operations/accounts.py +140 -0
  76. marketing_toolbox-0.1.0/src/ga4adminctl/operations/mutations.py +40 -0
  77. marketing_toolbox-0.1.0/src/ga4adminctl/operations/properties.py +249 -0
  78. marketing_toolbox-0.1.0/src/ga4adminctl/operations/reads.py +106 -0
  79. marketing_toolbox-0.1.0/src/ga4adminctl/operations/resources.py +680 -0
  80. marketing_toolbox-0.1.0/src/ga4adminctl/operations/secrets.py +145 -0
  81. marketing_toolbox-0.1.0/src/ga4adminctl/service.py +223 -0
  82. marketing_toolbox-0.1.0/src/ga4datactl/__init__.py +3 -0
  83. marketing_toolbox-0.1.0/src/ga4datactl/cli.py +76 -0
  84. marketing_toolbox-0.1.0/src/ga4datactl/commands/__init__.py +1 -0
  85. marketing_toolbox-0.1.0/src/ga4datactl/commands/_common.py +68 -0
  86. marketing_toolbox-0.1.0/src/ga4datactl/commands/audience_exports.py +176 -0
  87. marketing_toolbox-0.1.0/src/ga4datactl/commands/metadata.py +26 -0
  88. marketing_toolbox-0.1.0/src/ga4datactl/commands/reports.py +187 -0
  89. marketing_toolbox-0.1.0/src/ga4datactl/commands/sdk.py +111 -0
  90. marketing_toolbox-0.1.0/src/ga4datactl/foundation/__init__.py +1 -0
  91. marketing_toolbox-0.1.0/src/ga4datactl/foundation/errors.py +63 -0
  92. marketing_toolbox-0.1.0/src/ga4datactl/foundation/serialization.py +40 -0
  93. marketing_toolbox-0.1.0/src/ga4datactl/foundation/validation.py +272 -0
  94. marketing_toolbox-0.1.0/src/ga4datactl/operations/__init__.py +1 -0
  95. marketing_toolbox-0.1.0/src/ga4datactl/operations/audience_exports.py +215 -0
  96. marketing_toolbox-0.1.0/src/ga4datactl/operations/metadata.py +62 -0
  97. marketing_toolbox-0.1.0/src/ga4datactl/operations/reports.py +258 -0
  98. marketing_toolbox-0.1.0/src/ga4datactl/schemas.py +229 -0
  99. marketing_toolbox-0.1.0/src/ga4datactl/service.py +165 -0
  100. marketing_toolbox-0.1.0/src/gtmctl/__init__.py +3 -0
  101. marketing_toolbox-0.1.0/src/gtmctl/cli.py +46 -0
  102. marketing_toolbox-0.1.0/src/gtmctl/commands/__init__.py +1 -0
  103. marketing_toolbox-0.1.0/src/gtmctl/commands/_common.py +52 -0
  104. marketing_toolbox-0.1.0/src/gtmctl/commands/accounts.py +491 -0
  105. marketing_toolbox-0.1.0/src/gtmctl/commands/built_in_variable_mutations.py +149 -0
  106. marketing_toolbox-0.1.0/src/gtmctl/commands/container_actions.py +165 -0
  107. marketing_toolbox-0.1.0/src/gtmctl/commands/core_mutations.py +241 -0
  108. marketing_toolbox-0.1.0/src/gtmctl/commands/environment_mutations.py +215 -0
  109. marketing_toolbox-0.1.0/src/gtmctl/commands/gallery_template_import.py +99 -0
  110. marketing_toolbox-0.1.0/src/gtmctl/commands/sdk.py +131 -0
  111. marketing_toolbox-0.1.0/src/gtmctl/commands/tag_mutations.py +212 -0
  112. marketing_toolbox-0.1.0/src/gtmctl/commands/version_mutations.py +237 -0
  113. marketing_toolbox-0.1.0/src/gtmctl/commands/workspace_discovery.py +289 -0
  114. marketing_toolbox-0.1.0/src/gtmctl/commands/workspace_operations.py +226 -0
  115. marketing_toolbox-0.1.0/src/gtmctl/foundation/__init__.py +1 -0
  116. marketing_toolbox-0.1.0/src/gtmctl/foundation/body.py +67 -0
  117. marketing_toolbox-0.1.0/src/gtmctl/foundation/errors.py +59 -0
  118. marketing_toolbox-0.1.0/src/gtmctl/foundation/validation.py +95 -0
  119. marketing_toolbox-0.1.0/src/gtmctl/operations/__init__.py +1 -0
  120. marketing_toolbox-0.1.0/src/gtmctl/operations/mutations.py +1066 -0
  121. marketing_toolbox-0.1.0/src/gtmctl/operations/reads.py +529 -0
  122. marketing_toolbox-0.1.0/src/marketing_common/__init__.py +1 -0
  123. marketing_toolbox-0.1.0/src/marketing_common/auth.py +74 -0
  124. marketing_toolbox-0.1.0/src/marketing_common/cli.py +151 -0
  125. marketing_toolbox-0.1.0/src/marketing_common/command.py +25 -0
  126. marketing_toolbox-0.1.0/src/marketing_common/discovery.py +149 -0
  127. marketing_toolbox-0.1.0/src/marketing_common/introspection.py +150 -0
  128. marketing_toolbox-0.1.0/src/marketing_common/typer_compat.py +10 -0
  129. marketing_toolbox-0.1.0/src/marketing_toolbox/__init__.py +1 -0
  130. marketing_toolbox-0.1.0/src/marketing_toolbox/cli.py +40 -0
  131. marketing_toolbox-0.1.0/tests/test_auth.py +87 -0
  132. marketing_toolbox-0.1.0/tests/test_cli.py +1020 -0
  133. marketing_toolbox-0.1.0/tests/test_ga4_admin.py +188 -0
  134. marketing_toolbox-0.1.0/tests/test_ga4_admin_catalog_contract.py +115 -0
  135. marketing_toolbox-0.1.0/tests/test_ga4_admin_discovery.py +1131 -0
  136. marketing_toolbox-0.1.0/tests/test_ga4_admin_refactor.py +229 -0
  137. marketing_toolbox-0.1.0/tests/test_ga4_data.py +472 -0
  138. marketing_toolbox-0.1.0/tests/test_gtm_account_admin.py +372 -0
  139. marketing_toolbox-0.1.0/tests/test_gtm_body.py +20 -0
  140. marketing_toolbox-0.1.0/tests/test_gtm_cli.py +86 -0
  141. marketing_toolbox-0.1.0/tests/test_gtm_container_actions.py +214 -0
  142. marketing_toolbox-0.1.0/tests/test_gtm_core_mutations.py +186 -0
  143. marketing_toolbox-0.1.0/tests/test_gtm_environment_mutations.py +152 -0
  144. marketing_toolbox-0.1.0/tests/test_gtm_errors.py +108 -0
  145. marketing_toolbox-0.1.0/tests/test_gtm_folder_mutations.py +198 -0
  146. marketing_toolbox-0.1.0/tests/test_gtm_gallery_template_import.py +138 -0
  147. marketing_toolbox-0.1.0/tests/test_gtm_gtag_and_built_in_variable_mutations.py +154 -0
  148. marketing_toolbox-0.1.0/tests/test_gtm_reads.py +200 -0
  149. marketing_toolbox-0.1.0/tests/test_gtm_request_fidelity.py +346 -0
  150. marketing_toolbox-0.1.0/tests/test_gtm_spec_catalog.py +111 -0
  151. marketing_toolbox-0.1.0/tests/test_gtm_tag_mutations.py +405 -0
  152. marketing_toolbox-0.1.0/tests/test_gtm_variable_trigger_mutations.py +213 -0
  153. marketing_toolbox-0.1.0/tests/test_gtm_version_mutations.py +155 -0
  154. marketing_toolbox-0.1.0/tests/test_gtm_version_publish.py +246 -0
  155. marketing_toolbox-0.1.0/tests/test_gtm_workspace_actions.py +160 -0
  156. marketing_toolbox-0.1.0/tests/test_gtm_workspace_entity_mutations.py +94 -0
  157. marketing_toolbox-0.1.0/tests/test_release_metadata.py +262 -0
  158. marketing_toolbox-0.1.0/uv.lock +1234 -0
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ validate:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.11", "3.13"]
17
+ steps:
18
+ - name: Check out source
19
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
20
+ - name: Set up Python
21
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ - name: Set up uv
25
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
26
+ with:
27
+ version: "0.12.17"
28
+ enable-cache: false
29
+ - name: Install locked development and build environment
30
+ run: uv sync --locked --all-groups --all-packages
31
+ - name: Verify lockfile
32
+ run: uv lock --check
33
+ - name: Run tests
34
+ run: uv run --locked --no-sync pytest
35
+ - name: Lint
36
+ run: uv run --locked --no-sync ruff check .
37
+ - name: Check formatting
38
+ run: uv run --locked --no-sync ruff format --check .
39
+ - name: Type check
40
+ run: uv run --locked --no-sync mypy
41
+ - name: Build distributions offline
42
+ run: uv build --all-packages --offline --no-build-isolation --no-python-downloads --out-dir dist
@@ -0,0 +1,185 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+ workflow_dispatch:
8
+ inputs:
9
+ publish_target:
10
+ description: "Bootstrap target (the only supported manual target)"
11
+ required: true
12
+ type: choice
13
+ options:
14
+ - marketing-toolbox
15
+ default: marketing-toolbox
16
+ confirmation:
17
+ description: "Type BOOTSTRAP-MARKETING-TOOLBOX to confirm the core-only publish"
18
+ required: true
19
+ type: string
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ jobs:
25
+ build:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - name: Check out release source
29
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
30
+ with:
31
+ ref: ${{ github.event_name == 'workflow_dispatch' && 'refs/heads/main' || github.ref }}
32
+
33
+ - name: Verify release mode
34
+ env:
35
+ EVENT_NAME: ${{ github.event_name }}
36
+ WORKFLOW_REF: ${{ github.ref }}
37
+ PUBLISH_TARGET: ${{ inputs.publish_target }}
38
+ CONFIRMATION: ${{ inputs.confirmation }}
39
+ run: |
40
+ set -euo pipefail
41
+ if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
42
+ [[ "$WORKFLOW_REF" == "refs/heads/main" ]] || {
43
+ echo "manual bootstrap releases are allowed only from main" >&2
44
+ exit 1
45
+ }
46
+ [[ "$PUBLISH_TARGET" == "marketing-toolbox" ]] || {
47
+ echo "manual releases may publish only marketing-toolbox" >&2
48
+ exit 1
49
+ }
50
+ [[ "$CONFIRMATION" == "BOOTSTRAP-MARKETING-TOOLBOX" ]] || {
51
+ echo "manual core-only publish confirmation did not match" >&2
52
+ exit 1
53
+ }
54
+ else
55
+ [[ "$EVENT_NAME" == "push" ]] || {
56
+ echo "unsupported release event: $EVENT_NAME" >&2
57
+ exit 1
58
+ }
59
+ fi
60
+
61
+ - name: Set up Python
62
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
63
+ with:
64
+ python-version: "3.11"
65
+
66
+ - name: Set up uv
67
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
68
+ with:
69
+ version: "0.12.17"
70
+ enable-cache: false
71
+
72
+ - name: Install locked development and build environment
73
+ run: uv sync --locked --all-groups --all-packages
74
+
75
+ - name: Verify tag and package versions
76
+ if: github.event_name == 'push'
77
+ env:
78
+ RELEASE_TAG: ${{ github.ref_name }}
79
+ run: |
80
+ uv run --locked --no-sync python - <<'PY'
81
+ import os
82
+ import tomllib
83
+ from pathlib import Path
84
+
85
+ tag = os.environ["RELEASE_TAG"]
86
+ if not tag.startswith("v") or tag == "v":
87
+ raise SystemExit(f"release tag must be v<version>: {tag}")
88
+ tag_version = tag[1:]
89
+
90
+ manifests = [
91
+ Path("pyproject.toml"),
92
+ Path("packaging/ga4adminctl/pyproject.toml"),
93
+ Path("packaging/ga4datactl/pyproject.toml"),
94
+ Path("packaging/gtmctl/pyproject.toml"),
95
+ ]
96
+ versions = []
97
+ for manifest in manifests:
98
+ with manifest.open("rb") as file:
99
+ project = tomllib.load(file)["project"]
100
+ versions.append((project["name"], project["version"]))
101
+
102
+ if any(version != tag_version for _, version in versions):
103
+ raise SystemExit(
104
+ f"tag {tag_version} does not match package versions: {versions}"
105
+ )
106
+ if any(tag != f"v{version}" for _, version in versions):
107
+ raise SystemExit(f"tag must be v<version>: {tag}, packages: {versions}")
108
+ if len({version for _, version in versions}) != 1:
109
+ raise SystemExit(f"package versions are not synchronized: {versions}")
110
+ print(f"validated release {tag}: {versions}")
111
+ PY
112
+
113
+ - name: Verify lockfile
114
+ run: uv lock --check
115
+ - name: Run tests and metadata checks
116
+ run: uv run --locked --no-sync pytest
117
+ - name: Lint
118
+ run: uv run --locked --no-sync ruff check .
119
+ - name: Check formatting
120
+ run: uv run --locked --no-sync ruff format --check .
121
+ - name: Type check
122
+ run: uv run --locked --no-sync mypy
123
+
124
+ - name: Build all distributions
125
+ run: uv build --all-packages --offline --no-build-isolation --no-python-downloads --clear --out-dir dist
126
+ - name: Inspect distributions
127
+ run: uvx twine check dist/*
128
+ - name: Upload release distributions
129
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
130
+ with:
131
+ name: release-distributions
132
+ path: |
133
+ dist/*.whl
134
+ dist/*.tar.gz
135
+ if-no-files-found: error
136
+
137
+ publish:
138
+ needs: build
139
+ runs-on: ubuntu-latest
140
+ environment:
141
+ name: pypi
142
+ permissions:
143
+ id-token: write
144
+ steps:
145
+ - name: Download release distributions
146
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
147
+ with:
148
+ name: release-distributions
149
+ path: dist
150
+ merge-multiple: true
151
+ - name: Stage distributions for publication
152
+ env:
153
+ EVENT_NAME: ${{ github.event_name }}
154
+ run: |
155
+ set -euo pipefail
156
+ rm -rf publish-dist
157
+ mkdir publish-dist
158
+ shopt -s nullglob
159
+ if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
160
+ core_artifacts=(dist/marketing_toolbox-*.whl dist/marketing_toolbox-*.tar.gz)
161
+ [[ ${#core_artifacts[@]} -eq 2 ]] || {
162
+ echo "expected exactly one marketing-toolbox wheel and sdist" >&2
163
+ exit 1
164
+ }
165
+ cp "${core_artifacts[@]}" publish-dist/
166
+ else
167
+ all_artifacts=(dist/*.whl dist/*.tar.gz)
168
+ [[ ${#all_artifacts[@]} -eq 8 ]] || {
169
+ echo "expected wheel and sdist for all four distributions" >&2
170
+ exit 1
171
+ }
172
+ cp "${all_artifacts[@]}" publish-dist/
173
+ fi
174
+ published_artifacts=(publish-dist/*)
175
+ [[ ${#published_artifacts[@]} -eq 2 && "$EVENT_NAME" == "workflow_dispatch" || \
176
+ ${#published_artifacts[@]} -eq 8 && "$EVENT_NAME" == "push" ]] || {
177
+ echo "publication staging did not match the release mode" >&2
178
+ exit 1
179
+ }
180
+ - name: Publish distributions to PyPI
181
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
182
+ with:
183
+ packages-dir: publish-dist/
184
+ # The bootstrap has already uploaded marketing-toolbox 0.1.0.
185
+ skip-existing: true
@@ -0,0 +1,19 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+
7
+ # uv virtual environment
8
+ .venv/
9
+
10
+ # Local credentials and environment
11
+ .env
12
+ .env.*
13
+ *.json
14
+ !pyproject.toml
15
+ !uv.lock
16
+ # Versioned GA4 request/response fixtures are part of the CLI contract.
17
+ !docs/specification/v1/ga4datactl/fixtures/**/*.json
18
+
19
+ /.worktrees/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Enver
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,175 @@
1
+ Metadata-Version: 2.4
2
+ Name: marketing-toolbox
3
+ Version: 0.1.0
4
+ Summary: Command-line tools for Google Analytics 4 and Google Tag Manager.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: google-analytics-admin==0.30.1
9
+ Requires-Dist: google-analytics-data==0.23.0
10
+ Requires-Dist: google-api-python-client==2.198.0
11
+ Requires-Dist: google-auth==2.56.2
12
+ Requires-Dist: jsonschema==4.25.1
13
+ Requires-Dist: typer==0.27.0
14
+ Description-Content-Type: text/markdown
15
+
16
+ # marketing-toolbox
17
+
18
+ `marketing-toolbox` provides three command-line interfaces for the official Google
19
+ APIs:
20
+
21
+ - `ga4datactl` — Google Analytics Data API v1beta
22
+ - `ga4adminctl` — Google Analytics Admin API v1beta
23
+ - `gtmctl` — Google Tag Manager API v2
24
+
25
+ The tools provide explicit commands, structured JSON output, normalized errors,
26
+ and safety controls for non-interactive automation. They use the official
27
+ Google Python clients for authentication, transport, retries, and API models.
28
+
29
+ ## Quick start
30
+
31
+ With [uv](https://docs.astral.sh/uv/):
32
+
33
+ ```bash
34
+ uv sync --all-groups
35
+ uv run ga4datactl --help
36
+ uv run ga4adminctl --help
37
+ uv run gtmctl --help
38
+ uv run pytest
39
+ uv run ruff check .
40
+ ```
41
+
42
+ The installed entry points are `ga4datactl`, `ga4adminctl`, and `gtmctl`.
43
+
44
+ ## Published installation (after the first release)
45
+
46
+ After the first release has been configured and published to all four PyPI
47
+ projects, the three command-specific distributions can be run without a
48
+ persistent installation with [`uvx`](https://docs.astral.sh/uv/guides/tools/):
49
+
50
+ ```bash
51
+ uvx ga4datactl --help
52
+ uvx ga4adminctl --help
53
+ uvx gtmctl --help
54
+ ```
55
+
56
+ After publication, pin a release when reproducibility matters. The package
57
+ named with `--from` is the distribution to resolve, and the following argument
58
+ is its executable:
59
+
60
+ ```bash
61
+ uvx --from ga4datactl==0.1.0 ga4datactl --help
62
+ uvx --from ga4adminctl==0.1.0 ga4adminctl --help
63
+ uvx --from gtmctl==0.1.0 gtmctl --help
64
+ ```
65
+
66
+ For a published distribution, `uvx` caches its isolated tool environment. An
67
+ unpinned invocation can be refreshed to pick up a newer published release; use
68
+ `--refresh` when you need to re-resolve and reinstall the tool:
69
+
70
+ ```bash
71
+ uvx --refresh ga4datactl --help
72
+ ```
73
+
74
+ A version-pinned invocation remains on that version, even when refreshed:
75
+
76
+ ```bash
77
+ uvx --refresh --from ga4datactl==0.1.0 ga4datactl --help
78
+ ```
79
+
80
+ Each command-specific distribution is a thin launcher distribution. It exposes
81
+ one command and pins the corresponding `marketing-toolbox` release; the
82
+ `marketing-toolbox` distribution contains the shared implementation and all three
83
+ entry points. This gives each CLI a discoverable PyPI name while keeping the
84
+ implementation in one distribution.
85
+
86
+ ## Command map
87
+
88
+ ```text
89
+ ga4datactl
90
+ reports (run, batch-run, pivot-run, batch-pivot-run, realtime-run, compatibility-check)
91
+ metadata get
92
+ audience-exports (get, list, create, query)
93
+ sdk schema --command "<eligible leaf path>"
94
+
95
+ ga4adminctl
96
+ account-summaries list
97
+ accounts (...)
98
+ properties (...)
99
+ sdk schema --command "<eligible leaf path>"
100
+
101
+ gtmctl
102
+ accounts (...)
103
+ sdk schema --command "<eligible body leaf path>"
104
+ ```
105
+
106
+ Use `--help` for the executable command set. The complete current target maps
107
+ are the [GA4 Admin catalog](docs/specification/v1/ga4adminctl/catalog.md) and
108
+ [GTM catalog](docs/specification/v1/gtmctl/catalog.md). The
109
+ [GA4 Data command contracts](docs/specification/v1/ga4datactl/commands/) cover
110
+ its public request and safety boundaries.
111
+
112
+ `sdk schema` reads the pinned local SDK or Discovery descriptor. It does not
113
+ load credentials or call Google.
114
+
115
+ ## Authentication
116
+
117
+ Commands that call Google use a service account supplied at process runtime by
118
+ one of these environment variables, in precedence order:
119
+
120
+ ```text
121
+ GOOGLE_SERVICE_ACCOUNT_JSON
122
+ GOOGLE_APPLICATION_CREDENTIALS
123
+ ```
124
+
125
+ The first contains the complete service-account JSON document and is parsed in
126
+ memory. The second names a service-account JSON file. Credentials are never
127
+ accepted as command-line arguments or written to output. Interactive OAuth,
128
+ user credentials, and application-default user credentials are not supported.
129
+ See [docs/authentication.md](docs/authentication.md).
130
+
131
+ ## Output and safety
132
+
133
+ Successful commands write one versioned JSON document to stdout. Diagnostics
134
+ and warnings go to stderr. Read operations run normally; Google-side writes
135
+ require `--apply`, and `--dry-run` validates and plans without making the
136
+ mutation request. Command-specific acknowledgements remain required for
137
+ high-impact operations and sensitive reads.
138
+
139
+ `--apply` expresses caller intent; Google IAM, the invoking caller's execution
140
+ policy, and any required human approval remain the authorization boundary. See
141
+ the [CLI contract](docs/cli-contract.md) for output, errors, exit codes, and
142
+ mutation behavior.
143
+
144
+ ## Installation
145
+
146
+ From a checkout, install the package with:
147
+
148
+ ```bash
149
+ uv tool install .
150
+ ```
151
+
152
+ This installs all three entry points into uv's managed executable directory.
153
+ Ensure that directory is on `PATH`. For repository development, use `uv sync`
154
+ and invoke commands with `uv run`.
155
+
156
+ After the first release has been configured and published, the root
157
+ distribution will also be available as a compatibility path. For example, this
158
+ runs the root distribution's `ga4datactl` entry point:
159
+
160
+ ```bash
161
+ uvx --from marketing-toolbox ga4datactl --help
162
+ uvx --from marketing-toolbox ga4adminctl --help
163
+ uvx --from marketing-toolbox gtmctl --help
164
+ ```
165
+
166
+ The pinned form is `uvx --from marketing-toolbox==0.1.0 ga4datactl --help`, for
167
+ example. For new installs from PyPI after publication, prefer the
168
+ command-specific paths above.
169
+
170
+ ## Specification
171
+
172
+ The current public contracts, catalogs, schemas, and deterministic fixtures are
173
+ indexed from [docs/specification/README.md](docs/specification/README.md).
174
+ [SDK-backed introspection](docs/specification/v1/sdk-backed-introspection.md)
175
+ describes the local descriptor command and its current behavior.
@@ -0,0 +1,160 @@
1
+ # marketing-toolbox
2
+
3
+ `marketing-toolbox` provides three command-line interfaces for the official Google
4
+ APIs:
5
+
6
+ - `ga4datactl` — Google Analytics Data API v1beta
7
+ - `ga4adminctl` — Google Analytics Admin API v1beta
8
+ - `gtmctl` — Google Tag Manager API v2
9
+
10
+ The tools provide explicit commands, structured JSON output, normalized errors,
11
+ and safety controls for non-interactive automation. They use the official
12
+ Google Python clients for authentication, transport, retries, and API models.
13
+
14
+ ## Quick start
15
+
16
+ With [uv](https://docs.astral.sh/uv/):
17
+
18
+ ```bash
19
+ uv sync --all-groups
20
+ uv run ga4datactl --help
21
+ uv run ga4adminctl --help
22
+ uv run gtmctl --help
23
+ uv run pytest
24
+ uv run ruff check .
25
+ ```
26
+
27
+ The installed entry points are `ga4datactl`, `ga4adminctl`, and `gtmctl`.
28
+
29
+ ## Published installation (after the first release)
30
+
31
+ After the first release has been configured and published to all four PyPI
32
+ projects, the three command-specific distributions can be run without a
33
+ persistent installation with [`uvx`](https://docs.astral.sh/uv/guides/tools/):
34
+
35
+ ```bash
36
+ uvx ga4datactl --help
37
+ uvx ga4adminctl --help
38
+ uvx gtmctl --help
39
+ ```
40
+
41
+ After publication, pin a release when reproducibility matters. The package
42
+ named with `--from` is the distribution to resolve, and the following argument
43
+ is its executable:
44
+
45
+ ```bash
46
+ uvx --from ga4datactl==0.1.0 ga4datactl --help
47
+ uvx --from ga4adminctl==0.1.0 ga4adminctl --help
48
+ uvx --from gtmctl==0.1.0 gtmctl --help
49
+ ```
50
+
51
+ For a published distribution, `uvx` caches its isolated tool environment. An
52
+ unpinned invocation can be refreshed to pick up a newer published release; use
53
+ `--refresh` when you need to re-resolve and reinstall the tool:
54
+
55
+ ```bash
56
+ uvx --refresh ga4datactl --help
57
+ ```
58
+
59
+ A version-pinned invocation remains on that version, even when refreshed:
60
+
61
+ ```bash
62
+ uvx --refresh --from ga4datactl==0.1.0 ga4datactl --help
63
+ ```
64
+
65
+ Each command-specific distribution is a thin launcher distribution. It exposes
66
+ one command and pins the corresponding `marketing-toolbox` release; the
67
+ `marketing-toolbox` distribution contains the shared implementation and all three
68
+ entry points. This gives each CLI a discoverable PyPI name while keeping the
69
+ implementation in one distribution.
70
+
71
+ ## Command map
72
+
73
+ ```text
74
+ ga4datactl
75
+ reports (run, batch-run, pivot-run, batch-pivot-run, realtime-run, compatibility-check)
76
+ metadata get
77
+ audience-exports (get, list, create, query)
78
+ sdk schema --command "<eligible leaf path>"
79
+
80
+ ga4adminctl
81
+ account-summaries list
82
+ accounts (...)
83
+ properties (...)
84
+ sdk schema --command "<eligible leaf path>"
85
+
86
+ gtmctl
87
+ accounts (...)
88
+ sdk schema --command "<eligible body leaf path>"
89
+ ```
90
+
91
+ Use `--help` for the executable command set. The complete current target maps
92
+ are the [GA4 Admin catalog](docs/specification/v1/ga4adminctl/catalog.md) and
93
+ [GTM catalog](docs/specification/v1/gtmctl/catalog.md). The
94
+ [GA4 Data command contracts](docs/specification/v1/ga4datactl/commands/) cover
95
+ its public request and safety boundaries.
96
+
97
+ `sdk schema` reads the pinned local SDK or Discovery descriptor. It does not
98
+ load credentials or call Google.
99
+
100
+ ## Authentication
101
+
102
+ Commands that call Google use a service account supplied at process runtime by
103
+ one of these environment variables, in precedence order:
104
+
105
+ ```text
106
+ GOOGLE_SERVICE_ACCOUNT_JSON
107
+ GOOGLE_APPLICATION_CREDENTIALS
108
+ ```
109
+
110
+ The first contains the complete service-account JSON document and is parsed in
111
+ memory. The second names a service-account JSON file. Credentials are never
112
+ accepted as command-line arguments or written to output. Interactive OAuth,
113
+ user credentials, and application-default user credentials are not supported.
114
+ See [docs/authentication.md](docs/authentication.md).
115
+
116
+ ## Output and safety
117
+
118
+ Successful commands write one versioned JSON document to stdout. Diagnostics
119
+ and warnings go to stderr. Read operations run normally; Google-side writes
120
+ require `--apply`, and `--dry-run` validates and plans without making the
121
+ mutation request. Command-specific acknowledgements remain required for
122
+ high-impact operations and sensitive reads.
123
+
124
+ `--apply` expresses caller intent; Google IAM, the invoking caller's execution
125
+ policy, and any required human approval remain the authorization boundary. See
126
+ the [CLI contract](docs/cli-contract.md) for output, errors, exit codes, and
127
+ mutation behavior.
128
+
129
+ ## Installation
130
+
131
+ From a checkout, install the package with:
132
+
133
+ ```bash
134
+ uv tool install .
135
+ ```
136
+
137
+ This installs all three entry points into uv's managed executable directory.
138
+ Ensure that directory is on `PATH`. For repository development, use `uv sync`
139
+ and invoke commands with `uv run`.
140
+
141
+ After the first release has been configured and published, the root
142
+ distribution will also be available as a compatibility path. For example, this
143
+ runs the root distribution's `ga4datactl` entry point:
144
+
145
+ ```bash
146
+ uvx --from marketing-toolbox ga4datactl --help
147
+ uvx --from marketing-toolbox ga4adminctl --help
148
+ uvx --from marketing-toolbox gtmctl --help
149
+ ```
150
+
151
+ The pinned form is `uvx --from marketing-toolbox==0.1.0 ga4datactl --help`, for
152
+ example. For new installs from PyPI after publication, prefer the
153
+ command-specific paths above.
154
+
155
+ ## Specification
156
+
157
+ The current public contracts, catalogs, schemas, and deterministic fixtures are
158
+ indexed from [docs/specification/README.md](docs/specification/README.md).
159
+ [SDK-backed introspection](docs/specification/v1/sdk-backed-introspection.md)
160
+ describes the local descriptor command and its current behavior.
@@ -0,0 +1,71 @@
1
+ # Service-account authentication
2
+
3
+ All three CLIs authenticate as a Google service account. This behavior is the
4
+ same for a checkout, a `uv tool install .` installation, and a `uvx`
5
+ installation after the first release has been configured and published to all
6
+ four PyPI projects. `uvx` passes the invoking process's environment to the CLI,
7
+ so set the credential before running the command:
8
+
9
+ ```bash
10
+ export GOOGLE_APPLICATION_CREDENTIALS=/secure/path/service-account.json
11
+
12
+ # Checkout/marketing-toolbox installation:
13
+ uv run ga4datactl --help
14
+
15
+ # After the first release has been configured and published:
16
+ uvx ga4datactl --help
17
+ ```
18
+
19
+ For an in-memory credential instead:
20
+
21
+ ```bash
22
+ # Set GOOGLE_SERVICE_ACCOUNT_JSON to the complete service-account JSON document.
23
+
24
+ # Checkout/marketing-toolbox installation:
25
+ uv run gtmctl accounts list
26
+
27
+ # After the first release has been configured and published:
28
+ uvx gtmctl accounts list
29
+ ```
30
+
31
+ Supply the credential only at process runtime using one of these mechanisms, in precedence order:
32
+
33
+ ```text
34
+ GOOGLE_SERVICE_ACCOUNT_JSON
35
+ GOOGLE_APPLICATION_CREDENTIALS
36
+ ```
37
+
38
+ `GOOGLE_SERVICE_ACCOUNT_JSON` contains the complete service-account JSON document and is parsed in memory. `GOOGLE_APPLICATION_CREDENTIALS` names a readable service-account JSON file managed by the runtime. Neither representation may be committed, printed, passed as a command-line flag, or copied into request/output files.
39
+
40
+ ## Credential loading
41
+
42
+ The shared `marketing_common.auth.service_account_credentials()` loader prefers the in-memory JSON secret and otherwise loads the declared service-account file, then creates scoped `google-auth` credentials. It does not discover user credentials, start a browser OAuth flow, or fall back to a developer's local Google login.
43
+
44
+ Missing or invalid credentials return a sanitized configuration error that names only the credential environment-variable key, never any secret content or file contents.
45
+
46
+ ## Per-command-family scopes
47
+
48
+ Each live command scopes its credentials for its own operation. Help, `--version`, dry-run planning, and `sdk schema` do not load credentials. The following is the runtime selection, not a recommendation to grant every scope to every service account.
49
+
50
+ | CLI | Command family | Requested OAuth scope |
51
+ |---|---|---|
52
+ | `ga4datactl` | Reports, metadata, and audience-export `get`, `list`, and sensitive `query` | `https://www.googleapis.com/auth/analytics.readonly` |
53
+ | `ga4datactl` | `audience-exports create` | `https://www.googleapis.com/auth/analytics` |
54
+ | `ga4adminctl` | Ordinary reads | `https://www.googleapis.com/auth/analytics.readonly` |
55
+ | `ga4adminctl` | Mutations, sensitive actions, and account change-history search | `https://www.googleapis.com/auth/analytics.edit` |
56
+ | `gtmctl` | Ordinary GTM reads | `https://www.googleapis.com/auth/tagmanager.readonly` |
57
+ | `gtmctl` | Account user-permission reads and mutations | `https://www.googleapis.com/auth/tagmanager.manage.users` |
58
+ | `gtmctl` | `accounts update` | `https://www.googleapis.com/auth/tagmanager.manage.accounts` |
59
+ | `gtmctl` | Ordinary container/workspace/entity mutations | `https://www.googleapis.com/auth/tagmanager.edit.containers` |
60
+ | `gtmctl` | Container-version `update`, `delete`, and `undelete`; workspace `create-version` and `quick-preview` | `https://www.googleapis.com/auth/tagmanager.edit.containerversions` |
61
+ | `gtmctl` | Container-version `publish` and environment `reauthorize` | `https://www.googleapis.com/auth/tagmanager.publish` |
62
+ | `gtmctl` | Container and workspace deletion | `https://www.googleapis.com/auth/tagmanager.delete.containers` |
63
+
64
+ The [GTM catalog](specification/v1/gtmctl/catalog.md) records the exact scope allowed for each official target; the runtime families above identify the scope this CLI currently requests. Scopes constrain the token request, but Google Analytics and GTM IAM roles remain the ultimate authorization boundary. Configure each service account with only the Google-side permissions required for its intended commands.
65
+
66
+ ## Runtime requirements
67
+
68
+ - The process launcher must inject `GOOGLE_SERVICE_ACCOUNT_JSON` or securely provide the service-account file named by `GOOGLE_APPLICATION_CREDENTIALS`.
69
+ - Local tests and help/version commands do not require credentials.
70
+ - SDK-backed API commands fail before making a Google request if neither credential source is available or the supplied credential is invalid.
71
+ - Do not use interactive OAuth, application-default user credentials, or `.env` files for these tools.