Analytics Ops
Google Analytics 4 configuration as code and reporting for Ruby and Rails.
Analytics Ops gives you a review-first way to inspect GA4, detect drift, run useful reports, and apply a small set of safe configuration changes. The core is plain Ruby; Rails support is optional.
Five-minute read-only start
You need Ruby 3.2 or newer, a GA4 property you can read, and the Google Cloud CLI.
-
Add Analytics Ops to your bundle:
# Gemfile gem "analytics_ops", "~> 0.1", group: :developmentbundle install -
Enable the Google Analytics Admin API and Data API in your own Google Cloud project, then create local Application Default Credentials:
gcloud auth application-default login \ --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/analytics.readonly" -
Create
config/analytics_ops.yml:version: 1 profiles: production: property_id: "123456789" -
Check access without changing anything:
bundle exec analytics-ops doctor bundle exec analytics-ops discover bundle exec analytics-ops audit bundle exec analytics-ops report traffic_acquisition
Every command above is read-only. audit exits with status 2 when it finds
drift, which makes it useful in CI.
The safe change workflow
configuration → audit → saved plan → review → apply → verify
# Read-only: save a deterministic plan with mode 0600
bundle exec analytics-ops plan --output tmp/ga4-plan.json
# Review the JSON before doing anything
less tmp/ga4-plan.json
# Mutating: prints the exact saved operations and asks you to type yes
bundle exec analytics-ops apply tmp/ga4-plan.json
# Read-only: confirm managed settings now match
bundle exec analytics-ops verify
apply refreshes the remote snapshot, rejects stale plans, and executes only
the operations in the saved file. Ordinary plans never delete or archive
anything. If one operation fails, execution stops and reports what succeeded,
what failed, and what remains.
Common commands
| Command | Purpose | Remote writes? |
|---|---|---|
analytics-ops doctor |
Check configuration, credentials, APIs, property access, clients, and clock | No |
analytics-ops discover |
List accessible accounts, properties, and streams | No |
analytics-ops snapshot |
Print normalized managed remote state | No |
analytics-ops audit |
Show drift without writing a plan file | No |
analytics-ops plan --output FILE |
Review and save deterministic changes | No |
analytics-ops apply FILE |
Apply one reviewed, non-stale plan | Yes |
analytics-ops verify |
Check convergence | No |
analytics-ops report NAME |
Run a built-in standard report | No |
analytics-ops realtime |
Run the realtime-events report | No |
analytics-ops schema --format json |
Print the configuration schema | No |
Use --format json for automation. CSV is available only for reports:
bundle exec analytics-ops report landing_pages --format csv
See Commands for every option and exit status.
IDs: the quick distinction
Use fake values like these in examples and tests:
| Value | Example | Where it belongs |
|---|---|---|
| Account ID | 100000001 |
Discovery output only |
| Property ID | 123456789 |
property_id in configuration and API requests |
| Stream ID | 987654321 |
stream_id in configuration |
| Measurement ID | G-EXAMPLE1 |
Browser tagging; never use it as a stream ID |
| OAuth client | A Cloud project client ID/secret | Owned by your application, never this YAML |
| Service-account identity | ga-reader@example-project.iam.gserviceaccount.com |
Granted GA access; its key is never this YAML |
Ruby API
workspace = AnalyticsOps::Workspace.load(
"config/analytics_ops.yml",
profile: "production"
)
plan = workspace.plan
plan.write("tmp/ga4-plan.json")
report = workspace.report("calculator_completions")
report.rows.each { |row| puts row.fetch("eventCount") }
Loading the gem, loading YAML, and booting Rails do not contact Google. Network calls happen only when a workspace operation is invoked.
Rails
# Gemfile
gem "analytics_ops", require: "analytics_ops/rails", group: :development
bin/rails generate analytics_ops:install
bin/rake analytics:doctor
bin/rake 'analytics:report[traffic_acquisition]'
The integration is a Railtie, not an Engine. It adds a generator and operator Rake tasks—no models, migrations, routes, controllers, views, JavaScript, or boot-time network calls. See Rails integration.
What Analytics Ops does not do
- It does not inject browser analytics or manage consent banners.
- It does not store credentials, tokens, report rows, or local state.
- It does not delete accounts, properties, streams, or unmanaged resources.
- It does not claim to manage settings that Google exposes only in the UI.
- Experimental declarations are findings only in version 0.1.0; they are not silently applied through Alpha APIs.
Documentation
| Guide | Topic |
|---|---|
| Authentication | ADC, scopes, service accounts, and safe automation |
| Configuration | Complete strict YAML contract |
| Commands | CLI syntax, formats, and exit statuses |
| Reports | Built-in recipes and GA reporting limitations |
| Rails | Generator and Rake tasks |
| Safety | Plans, stale-state protection, rollback, and credentials |
| Plan format | Version-1 JSON contract |
| API support | Exactly what is managed, manual, or unsupported |
| Client compatibility | Tested official Google gem versions |
| Troubleshooting | Common failures and fixes |
| Architecture | Boundaries and data flow |
For development, run:
bin/setup
bin/check
Security issues belong in private vulnerability reporting; see SECURITY.md. Contributions follow CONTRIBUTING.md.
Analytics Ops is MIT licensed and is not affiliated with, sponsored by, or endorsed by Google LLC. Google Analytics is a trademark of Google LLC and is named only to describe API compatibility.