Le CI/CD pour les packages dbt exécute la suite de tests d’intégration sur chaque combinaison supportée de warehouse et de version dbt, détectant les échecs spécifiques aux adapters et les changements breaking issus des releases dbt Core. GitHub Actions avec une stratégie de matrice est l’approche standard.
Tests matriciels avec GitHub Actions
La stratégie de matrice exécute la même suite de tests sur plusieurs dimensions — warehouses et versions dbt — en parallèle :
name: CIon: [pull_request]
jobs: test: runs-on: ubuntu-latest strategy: matrix: warehouse: [snowflake, bigquery, postgres] dbt-version: ['1.9.0', '1.11.0'] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install dbt-${{ matrix.warehouse }}==${{ matrix.dbt-version }} - run: | cd integration_tests/ dbt deps dbt seed --target ${{ matrix.warehouse }} dbt run --target ${{ matrix.warehouse }} dbt test --target ${{ matrix.warehouse }} env: SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }} SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }} SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }} BIGQUERY_KEYFILE: ${{ secrets.BIGQUERY_KEYFILE }}Cette configuration crée 6 jobs parallèles (3 warehouses x 2 versions dbt). Chaque job installe l’adapter dbt approprié, exécute la suite complète de tests d’intégration et rapporte le résultat indépendamment.
Ce que la matrice détecte
Chaque dimension détecte des problèmes différents :
La dimension warehouse détecte :
- Les différences de syntaxe SQL (
SAFE_DIVIDEde BigQuery vsCASE WHEN ... = 0) - Les implémentations dispatch manquantes pour des adapters spécifiques
- Les différences de système de types (
INT64de BigQuery vsNUMBERde Snowflake) - Les différences de noms de fonctions (ordre des arguments
DATEADDselon les dialectes)
La dimension de version dbt détecte :
- Les fonctionnalités dépréciées que vous utilisez encore
- Les changements de comportement dans les macros intégrées
- Les nouvelles exigences ou changements de configuration dbt Core
- La compatibilité avec dbt Core 1.x et Fusion 2.x
Sans la matrice, vous livrez un package testé uniquement contre une seule combinaison. Les utilisateurs sur d’autres combinaisons deviennent votre équipe QA.
Configuration des profils
Le projet de test d’intégration a besoin de profils connectant à chaque warehouse. Ceux-ci résident dans integration_tests/profiles.yml ou sont configurés via des variables d’environnement :
integration_tests: target: postgres outputs: postgres: type: postgres host: "{{ env_var('POSTGRES_HOST', 'localhost') }}" user: "{{ env_var('POSTGRES_USER', 'dbt_test') }}" password: "{{ env_var('POSTGRES_PASSWORD') }}" port: 5432 dbname: dbt_test schema: dbt_test
snowflake: type: snowflake account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}" user: "{{ env_var('SNOWFLAKE_USER') }}" password: "{{ env_var('SNOWFLAKE_PASSWORD') }}" role: TRANSFORMER database: DBT_TEST warehouse: COMPUTE_WH schema: dbt_test
bigquery: type: bigquery method: service-account project: "{{ env_var('BIGQUERY_PROJECT', 'my-test-project') }}" dataset: dbt_test keyfile: "{{ env_var('BIGQUERY_KEYFILE') }}"Chaque output correspond à une valeur dans la liste warehouse de la matrice. Le flag --target dans l’étape CI sélectionne le profil à utiliser.
Gestion des credentials
Stockez les credentials warehouse sous forme de GitHub Secrets. Ne commitez jamais des credentials, des chaînes de connexion ou des clés de compte de service dans le dépôt.
Pour BigQuery, le fichier de clé de compte de service nécessite un traitement particulier car c’est un fichier JSON, pas une simple chaîne :
steps: - name: Write BigQuery keyfile if: matrix.warehouse == 'bigquery' run: echo '${{ secrets.BIGQUERY_KEYFILE_JSON }}' > /tmp/bigquery-keyfile.json env: BIGQUERY_KEYFILE: /tmp/bigquery-keyfile.jsonStockez l’intégralité de la clé JSON comme secret (BIGQUERY_KEYFILE_JSON) et écrivez-la dans un fichier temporaire dans l’étape CI. Définissez BIGQUERY_KEYFILE au chemin du fichier pour que dbt puisse le lire.
Pour Snowflake, l’authentification par paire de clés est plus sécurisée que l’authentification par mot de passe en CI :
snowflake: type: snowflake account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}" user: "{{ env_var('SNOWFLAKE_USER') }}" private_key_path: "{{ env_var('SNOWFLAKE_KEY_PATH') }}" role: TRANSFORMER database: DBT_TEST warehouse: COMPUTE_WH schema: dbt_testIsolation des schémas en CI
Plusieurs exécutions CI touchant le même warehouse peuvent entrer en collision si elles écrivent dans le même schéma. Utilisez des noms de schémas dynamiques basés sur l’ID d’exécution :
- run: | cd integration_tests/ dbt deps dbt seed --target ${{ matrix.warehouse }} --vars "{'my_package_schema': 'ci_${{ github.run_id }}'}" dbt run --target ${{ matrix.warehouse }} dbt test --target ${{ matrix.warehouse }}Ou configurez le profil pour inclure l’ID d’exécution dans le schéma :
schema: "dbt_ci_{{ env_var('GITHUB_RUN_ID', 'local') }}"Cela empêche les exécutions parallèles de se marcher dessus.
Contrôle des coûts
Exécuter des tests d’intégration sur trois warehouses à chaque PR peut générer des coûts. Quelques stratégies pour garder les factures sous contrôle :
- Utiliser la configuration de warehouse/slot la plus petite. Les seeds de tests d’intégration sont minuscules — pas besoin de puissance de calcul.
- N’exécuter la matrice complète que sur les PRs vers main. Les push sur les branches de fonctionnalités peuvent n’exécuter qu’un seul adapter (ex. : Postgres) pour un retour rapide, avec la matrice complète comme barrière de merge.
- Nettoyer après les exécutions. Supprimez le schéma CI à la fin du workflow pour éviter les coûts de stockage.
- name: Cleanup if: always() run: | cd integration_tests/ dbt run-operation drop_schema --args "{'schema': 'ci_${{ github.run_id }}'}" --target ${{ matrix.warehouse }}Au-delà des pull requests
Le CI sur les pull requests est le minimum. Pour les packages matures, ajoutez :
- Exécutions nocturnes contre les dernières pré-releases dbt. Détecte les problèmes de compatibilité avant qu’une release dbt passe en GA, vous donnant le temps de corriger avant que les utilisateurs ne signalent les problèmes.
- Automatisation des releases. Quand vous taguez une nouvelle version, exécutez automatiquement la matrice complète et ne créez la release GitHub qu’en cas de succès de tous les tests. Cela évite de publier une version défaillante.
- Génération de changelog. Des outils comme
git-cliffpeuvent auto-générer des changelogs à partir des messages de commit conventionnels, réduisant l’effort manuel de maintenance duCHANGELOG.md.
on: push: tags: - 'v*'
jobs: release: runs-on: ubuntu-latest steps: # Exécuter d'abord la matrice complète de tests # Puis créer la release GitHub uniquement en cas de succès - uses: softprops/action-gh-release@v1 with: generate_release_notes: trueLe script hubcap du Hub prend en compte les nouvelles releases GitHub en moins d’une heure, de sorte qu’un workflow de release réussi signifie que votre mise à jour est disponible pour les utilisateurs avec un délai minimal.