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.
- marketing_toolbox-0.1.0/.github/workflows/ci.yml +42 -0
- marketing_toolbox-0.1.0/.github/workflows/release.yml +185 -0
- marketing_toolbox-0.1.0/.gitignore +19 -0
- marketing_toolbox-0.1.0/LICENSE +21 -0
- marketing_toolbox-0.1.0/PKG-INFO +175 -0
- marketing_toolbox-0.1.0/README.md +160 -0
- marketing_toolbox-0.1.0/docs/authentication.md +71 -0
- marketing_toolbox-0.1.0/docs/cli-contract.md +50 -0
- marketing_toolbox-0.1.0/docs/releasing.md +126 -0
- marketing_toolbox-0.1.0/docs/specification/README.md +22 -0
- marketing_toolbox-0.1.0/docs/specification/v1/README.md +24 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4adminctl/catalog.md +105 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/audience-exports-create.md +93 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/audience-exports-query.md +52 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-batch-pivot-run.md +47 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-batch-run.md +49 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-pivot-run.md +47 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/commands/reports-run.md +47 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/audience-exports-create/invalid-cross-property-audience.json +4 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/audience-exports-create/invalid-output-only-field.json +5 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/audience-exports-create/valid-basic-request.json +6 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-pivot-run/invalid-nested-property.json +9 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-pivot-run/invalid-no-pivots.json +3 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-pivot-run/valid-two-pivot-reports-request.json +16 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/invalid-empty-requests.json +3 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/invalid-nested-property.json +8 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/invalid-six-requests.json +10 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/valid-two-reports-request.json +17 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-batch-run/valid-two-reports-success.json +21 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/invalid-no-metrics.json +4 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/invalid-no-pivot-limit.json +5 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/valid-two-pivots-request.json +16 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-pivot-run/valid-two-pivots-success.json +34 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-body-property.json +4 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-limit.json +4 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-minute-range-order.json +4 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/invalid-three-minute-ranges.json +4 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-realtime-run/valid-basic-request.json +6 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/invalid-body-property.json +14 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/invalid-unknown-field.json +14 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/valid-basic-request.json +20 -0
- marketing_toolbox-0.1.0/docs/specification/v1/ga4datactl/fixtures/reports-run/valid-basic-success.json +37 -0
- marketing_toolbox-0.1.0/docs/specification/v1/gtmctl/catalog.md +215 -0
- marketing_toolbox-0.1.0/docs/specification/v1/gtmctl/command-contracts.md +92 -0
- marketing_toolbox-0.1.0/docs/specification/v1/requirements.md +51 -0
- marketing_toolbox-0.1.0/docs/specification/v1/sdk-backed-introspection.md +79 -0
- marketing_toolbox-0.1.0/packaging/ga4adminctl/LICENSE +21 -0
- marketing_toolbox-0.1.0/packaging/ga4adminctl/README.md +9 -0
- marketing_toolbox-0.1.0/packaging/ga4adminctl/pyproject.toml +22 -0
- marketing_toolbox-0.1.0/packaging/ga4adminctl/src/ga4adminctl_alias/__init__.py +1 -0
- marketing_toolbox-0.1.0/packaging/ga4datactl/LICENSE +21 -0
- marketing_toolbox-0.1.0/packaging/ga4datactl/README.md +9 -0
- marketing_toolbox-0.1.0/packaging/ga4datactl/pyproject.toml +22 -0
- marketing_toolbox-0.1.0/packaging/ga4datactl/src/ga4datactl_alias/__init__.py +1 -0
- marketing_toolbox-0.1.0/packaging/gtmctl/LICENSE +21 -0
- marketing_toolbox-0.1.0/packaging/gtmctl/README.md +9 -0
- marketing_toolbox-0.1.0/packaging/gtmctl/pyproject.toml +22 -0
- marketing_toolbox-0.1.0/packaging/gtmctl/src/gtmctl_alias/__init__.py +1 -0
- marketing_toolbox-0.1.0/pyproject.toml +61 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/__init__.py +3 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/cli.py +108 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/_common.py +98 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/accounts.py +293 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/properties.py +340 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/resources.py +688 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/sdk.py +271 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/commands/secrets.py +247 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/foundation/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/foundation/errors.py +43 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/foundation/serialization.py +40 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/foundation/validation.py +136 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/access.py +113 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/accounts.py +140 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/mutations.py +40 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/properties.py +249 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/reads.py +106 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/resources.py +680 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/operations/secrets.py +145 -0
- marketing_toolbox-0.1.0/src/ga4adminctl/service.py +223 -0
- marketing_toolbox-0.1.0/src/ga4datactl/__init__.py +3 -0
- marketing_toolbox-0.1.0/src/ga4datactl/cli.py +76 -0
- marketing_toolbox-0.1.0/src/ga4datactl/commands/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/ga4datactl/commands/_common.py +68 -0
- marketing_toolbox-0.1.0/src/ga4datactl/commands/audience_exports.py +176 -0
- marketing_toolbox-0.1.0/src/ga4datactl/commands/metadata.py +26 -0
- marketing_toolbox-0.1.0/src/ga4datactl/commands/reports.py +187 -0
- marketing_toolbox-0.1.0/src/ga4datactl/commands/sdk.py +111 -0
- marketing_toolbox-0.1.0/src/ga4datactl/foundation/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/ga4datactl/foundation/errors.py +63 -0
- marketing_toolbox-0.1.0/src/ga4datactl/foundation/serialization.py +40 -0
- marketing_toolbox-0.1.0/src/ga4datactl/foundation/validation.py +272 -0
- marketing_toolbox-0.1.0/src/ga4datactl/operations/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/ga4datactl/operations/audience_exports.py +215 -0
- marketing_toolbox-0.1.0/src/ga4datactl/operations/metadata.py +62 -0
- marketing_toolbox-0.1.0/src/ga4datactl/operations/reports.py +258 -0
- marketing_toolbox-0.1.0/src/ga4datactl/schemas.py +229 -0
- marketing_toolbox-0.1.0/src/ga4datactl/service.py +165 -0
- marketing_toolbox-0.1.0/src/gtmctl/__init__.py +3 -0
- marketing_toolbox-0.1.0/src/gtmctl/cli.py +46 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/_common.py +52 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/accounts.py +491 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/built_in_variable_mutations.py +149 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/container_actions.py +165 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/core_mutations.py +241 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/environment_mutations.py +215 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/gallery_template_import.py +99 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/sdk.py +131 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/tag_mutations.py +212 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/version_mutations.py +237 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/workspace_discovery.py +289 -0
- marketing_toolbox-0.1.0/src/gtmctl/commands/workspace_operations.py +226 -0
- marketing_toolbox-0.1.0/src/gtmctl/foundation/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/gtmctl/foundation/body.py +67 -0
- marketing_toolbox-0.1.0/src/gtmctl/foundation/errors.py +59 -0
- marketing_toolbox-0.1.0/src/gtmctl/foundation/validation.py +95 -0
- marketing_toolbox-0.1.0/src/gtmctl/operations/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/gtmctl/operations/mutations.py +1066 -0
- marketing_toolbox-0.1.0/src/gtmctl/operations/reads.py +529 -0
- marketing_toolbox-0.1.0/src/marketing_common/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/marketing_common/auth.py +74 -0
- marketing_toolbox-0.1.0/src/marketing_common/cli.py +151 -0
- marketing_toolbox-0.1.0/src/marketing_common/command.py +25 -0
- marketing_toolbox-0.1.0/src/marketing_common/discovery.py +149 -0
- marketing_toolbox-0.1.0/src/marketing_common/introspection.py +150 -0
- marketing_toolbox-0.1.0/src/marketing_common/typer_compat.py +10 -0
- marketing_toolbox-0.1.0/src/marketing_toolbox/__init__.py +1 -0
- marketing_toolbox-0.1.0/src/marketing_toolbox/cli.py +40 -0
- marketing_toolbox-0.1.0/tests/test_auth.py +87 -0
- marketing_toolbox-0.1.0/tests/test_cli.py +1020 -0
- marketing_toolbox-0.1.0/tests/test_ga4_admin.py +188 -0
- marketing_toolbox-0.1.0/tests/test_ga4_admin_catalog_contract.py +115 -0
- marketing_toolbox-0.1.0/tests/test_ga4_admin_discovery.py +1131 -0
- marketing_toolbox-0.1.0/tests/test_ga4_admin_refactor.py +229 -0
- marketing_toolbox-0.1.0/tests/test_ga4_data.py +472 -0
- marketing_toolbox-0.1.0/tests/test_gtm_account_admin.py +372 -0
- marketing_toolbox-0.1.0/tests/test_gtm_body.py +20 -0
- marketing_toolbox-0.1.0/tests/test_gtm_cli.py +86 -0
- marketing_toolbox-0.1.0/tests/test_gtm_container_actions.py +214 -0
- marketing_toolbox-0.1.0/tests/test_gtm_core_mutations.py +186 -0
- marketing_toolbox-0.1.0/tests/test_gtm_environment_mutations.py +152 -0
- marketing_toolbox-0.1.0/tests/test_gtm_errors.py +108 -0
- marketing_toolbox-0.1.0/tests/test_gtm_folder_mutations.py +198 -0
- marketing_toolbox-0.1.0/tests/test_gtm_gallery_template_import.py +138 -0
- marketing_toolbox-0.1.0/tests/test_gtm_gtag_and_built_in_variable_mutations.py +154 -0
- marketing_toolbox-0.1.0/tests/test_gtm_reads.py +200 -0
- marketing_toolbox-0.1.0/tests/test_gtm_request_fidelity.py +346 -0
- marketing_toolbox-0.1.0/tests/test_gtm_spec_catalog.py +111 -0
- marketing_toolbox-0.1.0/tests/test_gtm_tag_mutations.py +405 -0
- marketing_toolbox-0.1.0/tests/test_gtm_variable_trigger_mutations.py +213 -0
- marketing_toolbox-0.1.0/tests/test_gtm_version_mutations.py +155 -0
- marketing_toolbox-0.1.0/tests/test_gtm_version_publish.py +246 -0
- marketing_toolbox-0.1.0/tests/test_gtm_workspace_actions.py +160 -0
- marketing_toolbox-0.1.0/tests/test_gtm_workspace_entity_mutations.py +94 -0
- marketing_toolbox-0.1.0/tests/test_release_metadata.py +262 -0
- 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.
|