Learning Platform
Глоссарий Troubleshooting
Урок 21.03 · 18 мин
Начальный
Git.gitignoreSecretsenv_varSecurity

В git-репозитории dbt-проекта живёт только код: модели, тесты, YAML, макросы. Всё остальное — артефакты, локальные настройки, секреты — должно быть исключено. В этом уроке разбираемся, что и почему.

Стандартный .gitignore для dbt

Минимальный набор:

# dbt artifacts
target/
dbt_packages/
logs/

# dbt user-specific
.user.yml

# Credentials (CRITICAL)
profiles.yml
.dbt/
*.duckdb        # локальный DuckDB файл с данными

# Python venv
.venv/
venv/
__pycache__/
*.pyc

# IDE
.vscode/
.idea/
*.swp

# OS
.DS_Store
Thumbs.db

Этот файл лежит в корне проекта рядом с dbt_project.yml. После git init обычно создаётся автоматически шаблоном dbt init, но содержимое стоит проверить.


Разбор каждой строки

target/ — артефакты dbt

После каждого dbt run, dbt compile, dbt build папка target/ пересоздаётся. Внутри:

  • compiled/ — финальный SQL
  • run/ — SQL с CREATE-обёрткой
  • manifest.json — структура проекта
  • run_results.json — результаты последнего run
  • catalog.json — метаданные warehouse

Это локальные артефакты для тебя. У каждого разработчика и в CI они свои. Не должны быть в git.

Исключение: в CI manifest.json от main-билда сохраняется как production-artifact в S3 / GitHub Actions Artifacts — но это не в git-repo, а в внешнее storage. См. модуль 17 про state:modified.

dbt_packages/ — установленные пакеты

После dbt deps пакеты из packages.yml скачиваются в dbt_packages/. Это зависимости, как node_modules/ или vendor/. Не для git.

Установка воспроизводится: после git clone ты делаешь dbt deps, и dbt_packages/ восстанавливается из packages.yml + package-lock.yml.

package-lock.yml (если есть) — в git. Он содержит точные версии для воспроизводимости. dbt_packages/ — нет.

logs/ — лог-файлы

logs/dbt.log пишется каждым запуском dbt. У каждого разработчика свой, в CI свой. Логи могут содержать чувствительные данные (env_var values, PII в SELECT’ах если ты неаккуратно). Никогда не commit.

В CI логи сохраняются как artifact (S3 / GitHub Actions) для post-mortem дебага.

.user.yml — user-specific overrides

.user.yml содержит per-developer настройки dbt: какой target по умолчанию, какой schema-name pattern. Каждый разработчик заводит свой. Не commit.

profiles.yml — СЕКРЕТЫ

Это самый критичный файл. Содержит:

  • Пароли к warehouse
  • API-токены (Snowflake account ID, BigQuery service account)
  • Connection strings

НИКОГДА не commit profiles.yml в git, даже в private-репо. Причины:

  1. Утечка через git history. Один раз commit — и пароль есть в истории. Удалить из истории сложно (rewrite + force push, ломает чужие checkouts).
  2. CI/CD доступ. GitHub Actions имеют свой profiles через secrets — не из репо.
  3. Compliance. Большинство compliance-стандартов (SOC2, ISO 27001) требуют отсутствие секретов в git.

Куда тогда profiles.yml?

  • Локально у каждого разработчика — в ~/.dbt/profiles.yml. Это user-home, не в проекте.
  • В CI — генерируется на лету из CI-secrets (GitHub Actions secrets, GitLab CI variables, etc).
  • В Cloud — настраивается через UI, dbt Cloud хранит креды.

.dbt/ — иногда внутри проекта

Некоторые команды размещают profiles.yml в .dbt/ внутри проекта (для удобства multi-environment). Это OK, но всю папку — в .gitignore.

*.duckdb — локальные данные DuckDB

Если у тебя DuckDB-проект, файл jaffle_shop.duckdb (или как назвал) — это данные. Может содержать sensitive customer info. Никогда не commit.

Размер — ещё одна причина: на больших данных duckdb-файл может быть несколько ГБ.

В CI/prod используется отдельный warehouse (Snowflake/BigQuery), не локальный DuckDB.

Python и IDE

.venv/, __pycache__/, *.pyc — стандартная Python-гигиена. .vscode/, .idea/ — IDE-настройки твоей машины.

.DS_Store, Thumbs.db — мусорные файлы от macOS / Windows. Не должны быть в репо.


Что должно быть в git

В git коммитятся:

  • dbt_project.yml — конфиг проекта
  • packages.yml — зависимости (для воспроизводимости через dbt deps)
  • package-lock.yml — pinned versions (если используется)
  • models/**/*.sql — модели
  • models/**/*.yml — YAML с тестами и descriptions
  • tests/**/*.sql — singular тесты
  • macros/**/*.sql — макросы
  • seeds/**/*.csv — сидовые CSV (но осторожно с большими и с PII)
  • snapshots/**/*.sql — снэпшоты
  • analyses/**/*.sql — analysis files
  • README.md — описание проекта
  • .gitignore — сам файл
  • .pre-commit-config.yaml — pre-commit конфиг
  • .github/workflows/*.yml или .gitlab-ci.yml — CI пайплайны
  • dbt-project-evaluator/ или другие quality конфиги
  • docs/ — кастомные docs-блоки (для {% docs %})

В сумме — только код проекта и его декларативные конфиги.


Работа с секретами через env_var

Чтобы profiles.yml не содержал реальные пароли, dbt предоставляет функцию env_var():

# ~/.dbt/profiles.yml
jaffle_shop:
  target: dev
  outputs:
    dev:
      type: duckdb
      path: './jaffle_shop.duckdb'
    prod:
      type: snowflake
      account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}"
      user: "{{ env_var('SNOWFLAKE_USER') }}"
      password: "{{ env_var('SNOWFLAKE_PASSWORD') }}"
      warehouse: "{{ env_var('SNOWFLAKE_WAREHOUSE') }}"
      database: "{{ env_var('SNOWFLAKE_DATABASE') }}"
      schema: "{{ env_var('SNOWFLAKE_SCHEMA') }}"
      threads: 8

Теперь profiles.yml сам по себе не содержит секреты — только указания «возьми из env-переменной».

Локально перед dbt run:

export SNOWFLAKE_ACCOUNT='xy12345.us-east-1'
export SNOWFLAKE_USER='dbt_user'
export SNOWFLAKE_PASSWORD='secret_password_here'

dbt run --target prod

Или через .env файл + direnv / python-dotenv:

# .env (тоже в .gitignore!)
SNOWFLAKE_ACCOUNT=xy12345.us-east-1
SNOWFLAKE_USER=dbt_user
SNOWFLAKE_PASSWORD=secret_password_here
source .env && dbt run --target prod

В CI секреты передаются через CI-secrets:

# .github/workflows/dbt.yml
- name: Run dbt
  env:
    SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
    SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
    SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
  run: |
    dbt build --target prod

GitHub Actions secrets — это encrypted storage, который инъектится в env только во время билда. Никогда не появляется в логах в plain text.


env_var в моделях

Иногда env_var нужен в моделях (не только в profiles). Например, фильтр по дате endpoint:

{{ config(materialized='table') }}

select * from {{ ref('stg_orders') }}
where order_date >= '{{ env_var("DATE_FROM", "2020-01-01") }}'

env_var('KEY', 'default') — второй аргумент это default, если переменной нет. Без default — dbt упадёт при отсутствии переменной.

Внимание: значение env_var попадает в compiled SQL в plain text! Если ты используешь env_var('DB_PASSWORD') в модели и compile — увидишь пароль в target/compiled/.../*.sql. Это не для секретов.

Правило: env_var для секретов — только в profiles.yml. В моделях — только нечувствительные значения (даты, флаги, configs).

Для секретов в моделях используют dbt vars через CLI:

dbt run --vars '{"api_key": "secret"}'

Хотя это всё равно попадёт в compiled SQL. Чистый путь — секреты живут в warehouse-side secrets (Snowflake Secret Functions, BigQuery Secret Manager) и читаются из SQL напрямую.


Что делать если случайно закоммитил profiles.yml

Сценарий: ты сделал git add . && git commit -m "..." не глядя, и потом понял, что profiles.yml попал в коммит.

Если ещё НЕ push-нул

# Откатить последний коммит
git reset HEAD~1

# Добавить в .gitignore
echo "profiles.yml" >> .gitignore

# Закоммитить .gitignore
git add .gitignore
git commit -m "chore: add profiles.yml to gitignore"

Файл остаётся локально, в коммите его нет. ОК.

Если уже push-нул

Тут хуже. Файл в git history.

# Удалить файл из истории через git filter-repo (новый инструмент)
pip install git-filter-repo
git filter-repo --invert-paths --path profiles.yml

# Force push (ВНИМАНИЕ: переписывает историю)
git push origin --force --all

КРИТИЧНО: после этого ОБЯЗАТЕЛЬНО смени все пароли, попавшие в commit. История git кэшируется (GitHub web UI, локальные клоны коллег, mirroring), и предположить, что пароль никто не увидел — нельзя. Always rotate.

Альтернатива (для GitHub):

  1. Делай через secrets scanning — GitHub сам найдёт известные форматы паролей в commits.
  2. Используй GitGuardian или подобный — alerting при commit’ах с secrets.

Pre-commit для предотвращения

Pre-commit hook до коммита проверяет:

repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']

detect-secrets сканирует diff на pattern’ы паролей, API ключей, AWS keys. Если что-то нашёл — commit не создаётся.

pip install pre-commit
pre-commit install

Теперь каждый git commit проверяет diff на secrets. Если find — фейл, фикс, retry. Это лучший способ не закоммитить случайно.


Большие и чувствительные seeds

seeds/ коммитятся в git. Но:

  1. Не больше 1 МБ на файл. Большие CSV замедляют git и pull. Если seed >1 МБ — это уже source, не seed. Перенести в warehouse.

  2. Без PII в seeds. Customer emails, phone numbers — в seeds недопустимо. Это test fixtures с фейковыми данными.

  3. С header row. dbt seed читает первую строку как имена колонок.

  4. UTF-8 без BOM. Excel часто экспортирует с BOM, dbt падает с ошибкой парсинга. Открой в текст-редакторе, пересохрани без BOM.


Попробуй сам

  1. Открой .gitignore в своём dbt-проекте. Проверь, что там есть все ключевые строки: target/, dbt_packages/, logs/, profiles.yml, *.duckdb.

  2. Запусти git status. Убедись, что среди untracked нет target/ или dbt_packages/.

  3. Создай .env файл с тестовыми переменными, добавь в .gitignore, проверь, что не виден в git status.

  4. Установи pre-commit:

    pip install pre-commit detect-secrets
    pre-commit install

    Создай .pre-commit-config.yaml с detect-secrets. Попробуй закоммитить файл с фальшивым «паролем» — увидишь блок.

  5. Перенеси profiles.yml из проекта в ~/.dbt/profiles.yml. Проверь, что dbt debug всё ещё работает.


Чек-лист

  • В .gitignore: target/, dbt_packages/, logs/, profiles.yml, *.duckdb, .venv/, IDE-файлы.
  • В git: только код проекта + декларативные конфиги (dbt_project.yml, packages.yml, package-lock.yml).
  • Секреты — через env_var() в profiles.yml. Никогда в git.
  • В CI секреты через GitHub Actions secrets / GitLab CI variables.
  • В моделях — env_var только для нечувствительных значений (дат, флагов).
  • Случайно закоммитил secret? Откат + rotate password — ОБЯЗАТЕЛЬНО.
  • Pre-commit + detect-secrets — для предотвращения.
  • Seeds: менее 1 МБ, без PII, UTF-8 без BOM.
.gitignore: шаблоны и лучшие практики
Проверка знанийKnowledge check
Аналитик закоммитил profiles.yml с production-паролем Snowflake. Push сделан 30 минут назад. Какой алгоритм восстановления?
ОтветAnswer
Действия в порядке: 1) НЕМЕДЛЕННО: rotate Snowflake password. Старый считается скомпрометированным. Создать новый через Snowflake UI или ADMIN.USERS.ALTER USER. Это первое — не последнее. Даже если успеешь переписать историю за 5 минут, никаких гарантий, что пароль никто не видел: GitHub-кэш, локальные клоны коллег, корпоративный mirror, GitHub secrets-scanning могло уже задетектить (хорошо!), но если не запушено в repo с защитой — могло утечь. 2) Удалить файл из текущего state: git rm profiles.yml; добавить в .gitignore: echo profiles.yml >> .gitignore; commit и push с новым (rotated) паролем. 3) Удалить из истории git: pip install git-filter-repo; git filter-repo --invert-paths --path profiles.yml; затем git push --force origin --all. Внимание: force push ломает чужие checkouts (коллеги должны переклонировать). 4) Уведомить команду в Slack: 'profiles.yml попал в commit, rotated Snowflake password X, force push сделан на main, пожалуйста переклонируйте репо'. 5) Поставить pre-commit + detect-secrets чтобы это не повторилось: pre-commit install. 6) Если есть policy compliance — задокументировать инцидент. Никогда не делай вид, что ничего не было: лучше открыто признать, отрегулировать, поставить guardrails. 30 минут на GitHub — это уже секреты прокэшированы поисковыми ботами (Shodan, etc.).

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 6. Какие папки и файлы ОБЯЗАТЕЛЬНО должны быть в .gitignore dbt-проекта?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 3