Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- YAML configuration file support (`diffdash.yml`) for shared team configuration
- New `--config` CLI flag to specify custom config file path
- Configurable file filtering: `ignore_paths`, `include_paths`, `excluded_suffixes`, `excluded_directories`
- Configuration file discovery (current dir, git root, explicit path)
- Example configuration file (`diffdash.example.yml`)
- Grafana v1 golden fixture contract test
- Adapter isolation spec
- Hard-fail on missing branch name
Expand Down
189 changes: 159 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,39 @@ gem install diffdash-*.gem

## Quick Start

### 1. Add these to your application's `.env` (dotenv) file
### Option A: Configuration File (Recommended for Teams)

Create a `diffdash.yml` in your repository root:

```yaml
grafana:
url: https://myorg.grafana.net
folder_id: 42

ignore_paths:
- vendor/
- lib/legacy/

default_env: production
```

Then set your API token via environment variable (never commit tokens):

```bash
export DIFFDASH_GRAFANA_TOKEN=glsa_xxxxxxxxxxxx
```

### Option B: Environment Variables Only

Add to your `.env` file:

```bash
DIFFDASH_GRAFANA_URL=https://myorg.grafana.net
DIFFDASH_GRAFANA_TOKEN=glsa_xxxxxxxxxxxx
DIFFDASH_GRAFANA_FOLDER_ID=42 # optional
DIFFDASH_OUTPUTS=grafana,json
```

### 2. Find your folder ID (optional)
### Find Your Folder ID (Optional)

```bash
diffdash folders
Expand All @@ -49,14 +72,17 @@ Available Grafana folders:
Set DIFFDASH_GRAFANA_FOLDER_ID in your .env file to use a specific folder
```

### 3. Generate Dashboard
### Generate Dashboard

```bash
# From your repo with changed files
diffdash

# Or dry-run to see JSON without uploading
diffdash --dry-run

# Use a custom config file
diffdash --config path/to/config.yml
```

## CLI Usage
Expand All @@ -70,26 +96,87 @@ diffdash [command] [options]
- *(none)* - Run analysis and generate/upload dashboard

**Options:**
- `--config FILE` - Path to configuration file (default: `diffdash.yml` in repo root)
- `--dry-run` - Generate JSON only, don't upload to Grafana
- `--verbose` - Show detailed progress and dynamic metric warnings
- `--verbose` - Show detailed progress, config source, and dynamic metric warnings
- `--version` - Show version number
- `--help` - Show help

## Configuration File

Create a `diffdash.yml` (or `.diffdash.yml`) in your repository root to share configuration across your team.

### Example Configuration

```yaml
# Grafana connection
grafana:
url: https://myorg.grafana.net
folder_id: 42

# Output adapters (grafana, json)
outputs:
- grafana
- json

# General settings
default_env: production
pr_comment: true
app_name: my-service

# File filtering
ignore_paths:
- vendor/
- lib/legacy/
- tmp/

include_paths: # Optional whitelist (empty = scan all)
- app/
- lib/

excluded_suffixes: # Defaults: _spec.rb, _test.rb
- _spec.rb
- _test.rb

excluded_directories: # Defaults: spec, test, config
- spec
- test
- config
```

### Configuration Precedence

Configuration is loaded from multiple sources (highest to lowest priority):

1. **Environment variables** - Always take precedence
2. **`--config` flag** - Explicitly specified config file
3. **Config file in current directory** - `diffdash.yml`, `.diffdash.yml`, `diffdash.yaml`, `.diffdash.yaml`
4. **Config file in git root** - If different from current directory
5. **Default values**

### Security Note

**API tokens are only loaded from environment variables** — never from config files. This prevents accidental commits of secrets. The `grafana.token` key is intentionally ignored if present in the YAML.

## Environment Variables

Set these in a `.env` file in your project root:
Environment variables can be set in a `.env` file or exported directly. They **always override** config file values.

| Variable | Required | Description |
|----------|----------|-------------|
| `DIFFDASH_GRAFANA_URL` | Yes | Grafana instance URL (e.g., `https://myorg.grafana.net`) |
| `DIFFDASH_GRAFANA_TOKEN` | Yes | Grafana API token (Service Account token with Editor role) |
| `DIFFDASH_GRAFANA_FOLDER_ID` | No | Target folder ID for dashboards |
| `DIFFDASH_OUTPUTS` | No | Comma-separated outputs (default: `grafana`) |
| `DIFFDASH_DRY_RUN` | No | Set to `true` to force dry-run mode |
| `DIFFDASH_DEFAULT_ENV` | No | Default environment filter (default: `production`) |
| `DIFFDASH_APP_NAME` | No | Override app name in dashboard (defaults to Git repo name) |
| `DIFFDASH_PR_COMMENT` | No | Set to `false` to disable PR comments with dashboard link |
| `DIFFDASH_PR_DEPLOY_ANNOTATION_EXPR` | No | PromQL expr for PR deployment annotation |
| Variable | Required | Config File Equivalent | Description |
|----------|----------|------------------------|-------------|
| `DIFFDASH_GRAFANA_URL` | Yes* | `grafana.url` | Grafana instance URL |
| `DIFFDASH_GRAFANA_TOKEN` | Yes | *(env only)* | Grafana API token (never in config file) |
| `DIFFDASH_GRAFANA_FOLDER_ID` | No | `grafana.folder_id` | Target folder ID for dashboards |
| `DIFFDASH_OUTPUTS` | No | `outputs` | Comma-separated outputs (default: `grafana`) |
| `DIFFDASH_DRY_RUN` | No | — | Set to `true` to force dry-run mode |
| `DIFFDASH_DEFAULT_ENV` | No | `default_env` | Default environment filter (default: `production`) |
| `DIFFDASH_APP_NAME` | No | `app_name` | Override app name (defaults to Git repo name) |
| `DIFFDASH_PR_COMMENT` | No | `pr_comment` | Set to `false` to disable PR comments |
| `DIFFDASH_PR_DEPLOY_ANNOTATION_EXPR` | No | `pr_deploy_annotation_expr` | PromQL expr for PR deployment annotation |

*Required unless set in config file.

**Legacy fallbacks:** `GRAFANA_URL`, `GRAFANA_TOKEN`, `GRAFANA_FOLDER_ID` are also supported if the `DIFFDASH_` versions aren't set.

## Output

Expand Down Expand Up @@ -203,14 +290,45 @@ If any limit is exceeded, the gem aborts with a clear error message and exits wi

## File Filtering

**Included:**
- Files ending with `.rb`
- Ruby application code
By default, diffdash scans Ruby files while excluding test and config directories.

**Default behavior:**

**Excluded:**
- `*_spec.rb`, `*_test.rb`
- Files in `/spec/`, `/test/`, `/config/`
- Non-Ruby files
| Filter | Default Value | Config Key |
|--------|---------------|------------|
| Included extensions | `.rb` | — |
| Excluded suffixes | `_spec.rb`, `_test.rb` | `excluded_suffixes` |
| Excluded directories | `spec`, `test`, `config` | `excluded_directories` |
| Ignored paths | *(none)* | `ignore_paths` |
| Included paths | *(all)* | `include_paths` |

**Customizing in `diffdash.yml`:**

```yaml
# Ignore additional paths
ignore_paths:
- vendor/
- lib/legacy/
- tmp/

# Only scan specific paths (optional whitelist)
include_paths:
- app/
- lib/

# Add custom excluded suffixes
excluded_suffixes:
- _spec.rb
- _test.rb
- _integration.rb

# Add custom excluded directories
excluded_directories:
- spec
- test
- config
- features
```

## Inheritance & Module Support

Expand Down Expand Up @@ -256,12 +374,23 @@ When `PaymentProcessor` is changed, signals from `BaseProcessor` and `Loggable`

### Setup

1. **Add secrets to your repository:**
- `DIFFDASH_GRAFANA_URL` - Your Grafana instance URL
1. **Create `diffdash.yml`** in your repository root with shared settings:

```yaml
grafana:
url: https://myorg.grafana.net
folder_id: 42

ignore_paths:
- vendor/

default_env: staging
```

2. **Add the API token as a repository secret:**
- `DIFFDASH_GRAFANA_TOKEN` - Service Account token with Editor role
- `DIFFDASH_GRAFANA_FOLDER_ID` (optional) - Folder ID for dashboards

2. **Create workflow file** `.github/workflows/pr-dashboard.yml`:
3. **Create workflow file** `.github/workflows/pr-dashboard.yml`:

```yaml
name: PR Observability Dashboard
Expand All @@ -287,12 +416,12 @@ jobs:

- name: Generate dashboard
env:
DIFFDASH_GRAFANA_URL: ${{ secrets.DIFFDASH_GRAFANA_URL }}
DIFFDASH_GRAFANA_TOKEN: ${{ secrets.DIFFDASH_GRAFANA_TOKEN }}
DIFFDASH_GRAFANA_FOLDER_ID: ${{ secrets.DIFFDASH_GRAFANA_FOLDER_ID }}
run: diffdash --verbose
```

The workflow reads `grafana.url` and `grafana.folder_id` from the committed `diffdash.yml`, while the token is injected securely via environment variable.

## Development

```bash
Expand Down
65 changes: 65 additions & 0 deletions diffdash.example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Example diffdash configuration file
# Copy this to diffdash.yml and customize for your project
#
# Configuration is loaded from the following sources in order of precedence:
# 1. Environment variables (highest priority)
# 2. Path specified via --config flag
# 3. diffdash.yml in current directory
# 4. diffdash.yml in git repository root
# 5. Default values (lowest priority)
#
# NOTE: API tokens should ONLY be set via environment variables for security.

# Grafana configuration
grafana:
# Grafana instance URL (also accepts DIFFDASH_GRAFANA_URL env var)
url: https://grafana.mycompany.com

# Grafana folder ID to store dashboards (also accepts DIFFDASH_GRAFANA_FOLDER_ID)
# Use `diffdash folders` to list available folders
folder_id: 42

# Output adapters to use (also accepts DIFFDASH_OUTPUTS env var as comma-separated list)
# Available: grafana, json
outputs:
- grafana
- json

# Default environment filter for Loki/Prometheus queries
# (also accepts DIFFDASH_DEFAULT_ENV env var)
default_env: production

# Whether to post dashboard link as a comment on the PR
# (also accepts DIFFDASH_PR_COMMENT env var)
pr_comment: true

# Application name for dashboard queries (also accepts DIFFDASH_APP_NAME env var)
# If not set, will be inferred from git remote URL
app_name: my-service

# Custom PromQL expression for PR deployment annotations
# (also accepts DIFFDASH_PR_DEPLOY_ANNOTATION_EXPR env var)
# pr_deploy_annotation_expr: changes(deploy_timestamp{branch="$branch"}[5m]) > 0

# File filtering configuration
# Paths to ignore when scanning for observability signals
ignore_paths:
- vendor/
- lib/legacy/
- tmp/

# Only scan files within these paths (empty = scan all paths)
# include_paths:
# - app/
# - lib/

# File suffixes to exclude (defaults to test files)
excluded_suffixes:
- _spec.rb
- _test.rb

# Directories to exclude (defaults to test and config directories)
excluded_directories:
- spec
- test
- config
1 change: 1 addition & 0 deletions lib/diffdash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
end

require_relative "diffdash/version"
require_relative "diffdash/config_loader"
require_relative "diffdash/config"
require_relative "diffdash/git_context"
require_relative "diffdash/file_filter"
Expand Down
Loading
Loading