Skip to main content

Tiggy

Declarative Google Tag Manager. Describe a GTM container in YAML, then validate, plan and apply it the way Tundra does for Snowflake permissions.

Early days: import, validate and export work; planning and applying against a live container come next.

pip install tiggy-cli

tiggy import container.json spec/    # write a container export out as a YAML spec
tiggy validate spec/                 # every reference between tags, triggers and variables resolves
tiggy export spec/ -o container.json # the spec as a container export again
tiggy spec-test spec/                # planned: the spec matches the live container
tiggy run spec/ --dry                # planned: the GTM changes that would make it match

The spec

A spec is a directory of YAML fragments. Every .yml file under it is read and merged, so files and folders are for people to organise; the result doesn't depend on them. An object defined twice is an error naming both places. tiggy import lays a container out like this:

spec/
├── container.yml                   # container settings and built-in variables
├── custom_templates/
│   ├── facebook-pixel.yml
│   └── facebook-pixel.tpl          # GTM's own template file, byte for byte
├── folders/
│   └── stape-meta/
│       ├── folder.yml
│       ├── tags.yml
│       └── variables.yml
└── unfiled/
    ├── tags.yml
    └── plausible-analytics.html    # custom HTML, byte for byte

A tag reads like GTM's own model, with names where GTM has IDs:

tiggy: 1
tags:
  - name: GA4 - view_search_results
    type: gaawe
    folder: GA4
    firing_triggers:
      - CE - view_search_results
    tag_firing_option: ONCE_PER_EVENT
    consent_settings:
      consent_status: NOT_SET
    monitoring_metadata: {$type: MAP}
    parameter:
      sendEcommerceData: false
      eventSettingsTable:
        - parameter: search_term
          parameterValue: '{{DLV - search_term}}'
      eventName: view_search_results

A parameter's YAML type is its GTM type: a string is a TEMPLATE, true a BOOLEAN, 2 an INTEGER, a list a LIST and a mapping a MAP, so '2' and 2 differ. Anything those can't say exactly is spelled out with $type, such as {$type: MAP} for a map GTM stores without entries.

A spec means the same in every YAML tool. Unquoted, only true, false, null and plain integers are anything but strings; whatever some reader would type differently (no, ~, 0x10, 1.5, 12:30, unquoted dates) has to be quoted, and anchors, tags, merge keys and multiple documents are refused. Everything Tiggy writes reads the same in ruamel.yaml, PyYAML and yq, and it reads hand-written YAML only where they agree.

Nothing is defaulted or inferred: a field in the YAML is a field in GTM, and an absent one is absent. A field Tiggy doesn't model yet goes under unmodelled: and passes through untouched; one it does model has to be written under its own key.

Code lives in sidecar files named by {$file: path}, relative to the spec root and spelled exactly as on disk (case and Unicode form included, since Linux checkouts compare names exactly). Links, to files or directories, are refused: a Windows checkout turns them into text files.

import

tiggy import container.json spec/
tiggy import container.json spec/ --check

Writes the spec into a new or empty directory. Before anything lands, the written spec is reloaded, compiled and compared with the export file as written, so only GTM-assigned IDs and bookkeeping may differ. An export Tiggy can't reproduce exactly (a value it would read differently, two objects with one name) is refused, not approximated. --check writes nothing and lists what differs between the export and an existing spec, which makes it a drift gate for CI.

Exits 0 when written or equivalent, 1 when --check finds differences, 2 when it can't run.

Code files (.html, .js, .tpl) must stay byte-exact, so keep whitespace fixers away from them; import writes a .gitattributes and .editorconfig that do this for git and editors.

export

tiggy export spec/ -o container.json

Compiles a spec to a container export with fresh IDs, on stdout unless -o names a file. Problems go to stderr, so a broken spec never writes half an export. It reimports to the same spec byte for byte; whether GTM's own Import Container accepts it hasn't been tried yet.

validate

tiggy validate spec/
tiggy validate examples/broken-container.json --format json --strict

Takes a spec directory or a container export. Reports references that don't resolve (triggers, {{variables}}, setup and teardown tags, custom templates, folders), built-ins used but not enabled, duplicate names and IDs, variable and tag-sequencing loops, and unused triggers, variables and templates. For a spec, every finding points at its file and line, and a code file no $file names is a warning.

Exits 0 when valid, 1 when not (--strict counts warnings; a spec file it can't read is a problem too), 2 when the input isn't a spec or an export, or is an export Tiggy would misread. import --check exits 2 on a spec that doesn't load, since 1 there means the two differ.

Every command exits 130 when interrupted and 141 when whatever reads its output stops early (| head).

How it fits together

Layer Where Does
Resource model tiggy/gtm/ One Pydantic model per GTM resource, generated from Google's API schema
Spec tiggy/spec/ The YAML format: strict loading, the parameter codec, import, compile, canon
Adapters tiggy/io/ Container export JSON
Graph tiggy/graph.py What refers to what. The only code that knows GTM's reference rules
Checks tiggy/checks.py Rules over the graph, each yielding findings
Report tiggy/report.py Findings as text or JSON

How each GTM field appears in YAML is decided in one place, tiggy/spec/overlay.py, and checked against the generated models when Tiggy starts, so a field Google adds can't be silently dropped.

The models are generated from the Tag Manager API v2 discovery document, vendored in schema/. To pick up API changes:

uv run python scripts/generate_resources.py --refresh

Metadata

Release files for tiggy-cli 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tiggy-cli 0.2.0
File Size Uploaded
tiggy_cli-0.2.0.tar.gz 62.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tiggy-cli 0.2.0
File Interpreter ABI Platform
tiggy_cli-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 133.4 kB

Release files / tiggy_cli-0.2.0.tar.gz

Download URL tiggy_cli-0.2.0.tar.gz
Size 62.1 kB
Tags Source
SHA-256 checksum
How to use checksums
27202c93c0ec70f47e21850e46947ee28d6fb6a8ad7d4208fb3adb5cfbdaac35
BLAKE2b-256 checksum
How to use checksums
7d143e7943e56372faad07b473861d298f9fb3f28cad37771ae4247e4c6b64ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / tiggy_cli-0.2.0-py3-none-any.whl

Download URL tiggy_cli-0.2.0-py3-none-any.whl
Size 71.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cfe6901a592a05aa24c0349f95132f6cc735cd2d5c5feba1fc0893bb575fad22
BLAKE2b-256 checksum
How to use checksums
72b2dcac62ccb48e6403652081ee0c604c5400cad1a1d0958379fc50fd54fe33
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page