GitHubActionsからdbt-core(dbt CLI)を使ってsnowflakeへSQL実行

## なにこれ
GitHubActionsでdbt-core(dbt-snowflake)を実行しSnowflakeへSQLを実行しようとしたときのメモです。

## 構成図
後日更新

## 事前準備
### GitHub
- プライベートリポジトリを作ります
- GitHubActions用に`.github/workflows`フォルダを作成します

### Snowflake
- アカウント識別子、ユーザー名、パスワードを使うので控えておきます
- アカウント識別子はコンソール画面の左下→アカウント→チェックがついてる（今使用中の）アカウントにマウスフォーカス→
「アカウント識別子をコピー」ボタンでコピーできます
- コピーされたアカウント識別子は`${Organizations_Name}.${Account_Name}`とドット綴りですが、ハイフン綴り`${Organizations_Name}-${Account_Name}`にして使用します

## ローカル環境でdbtを使う準備
### dbt install
今回はdbtをpipからインスコしたいので、まずPythonが必要です。なければインストールしてください。（ここでは省略）
次にvenv仮想環境でpipを実行し、dbt-coreとdbt-snowflakeをインストールします。

```
### 仮想環境の作成
$ python -m venv dbt-env  ### dbt-envは環境名なのでなんでも

### 仮想環境の実行
$ source dbt-env/bin/activate

### pip install 
(dbt-env)$ pip install dbt-core dbt-snowflake
```
### dbt projectの作成
`dbt init`コマンドを実行して初期設定をします。以下は今回利用するGitHubプライベートリポジトリのローカルリポジトリ上で実行します
```
(dbt-env)$ cd ./git/dbt-snowflake ### ローカルリポジトリに移動

(dbt-env)$ dbt init
# 以下対話形式でプロパティを入力していく

# dbt project名(任意のプロジェクト名)
Enter a name for your project (letters, digits, underscore): dbt-project

# snowflakeを使うので1
Which database would you like to use?
[1] snowflake
(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)
Enter a number: 1

# アカウント識別子
account (https://<this_value>.snowflakecomputing.com):${Organizations_Name}-${Account_Name}

# ユーザー名
user (dev username): dbt-user

# 認証形式(今回はパスワードでやるので1)
[1] password
[2] keypair
[3] sso
Desired authentication type option (enter a number): 1

# パスワード入力
password (dev password): 

# snowflakeで使うデフォルトロール名
role (dev role): dbt-role

# snowflakeで使うデフォルトウェアハウス名
warehouse (warehouse name): dbt-wh

# snowflakeで使うデフォルトデータベース名
database (default database that dbt will build objects in): dbt-db

# snowflakeで使うデフォルトデータベースのスキーマ名
schema (default schema that dbt will build objects in): public

# 終わりなので1
threads (1 or more) [1]: 1
```
そうすると、カレントディレクトリ配下に以下プロジェクトフォルダが出来上がります。

```
- dbt-project
| - README.md
| - analyses
| - dbt_project.yml
| - macros
| - models
  | - example
    | - my_first_dbt_model.sql
    | - my_second_dbt_model.sql
    | - schema.yml
| - seeds
| - snapshots
| - tests
```
これがdbtの味噌（そのままの意味）で、ここにSQLファイルなどを配置していきます。

## GitHubにdbtアセットをpush
上記プロジェクトフォルダの`models/`配下に必要なSQLを配置します。今回は初期で入ってきた`example/`配下のSQLをそのまま使ってテストします。
（`models/`配下に別の名前でフォルダを切りたい場合は、`dbt_project.yml`内の`model-paths`に追加するのを忘れないでください。）

## GitHubActionsワークフローファイルを作成
最初に作成した`.github/workflows/`配下にYML形式でワークフローファイルを作成します。今回はこのような記述で実行まで確認しました。

```
name: dbt Core - Snowflake

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  dbt-run:
    runs-on: ubuntu-latest

    env:
      SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
      SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
      SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
      SNOWFLAKE_ROLE: ${{ secrets.SNOWFLAKE_ROLE }}
      SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
      SNOWFLAKE_DATABASE: ${{ secrets.SNOWFLAKE_DATABASE }}
      SNOWFLAKE_SCHEMA: ${{ secrets.SNOWFLAKE_SCHEMA }}

    steps:
    # Checkout the repository
    - name: Inspect files before checkout
      run: ls -la

    - name: show current directory before checkout
      run: pwd

    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Inspect files after checkout
      run: ls -la

    - name: show current directory after checkout
      run: pwd

    - name: show all branches after checkout
      run: git branch -a

    # Set up Python
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.9'

    # Install dbt
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install dbt-core dbt-snowflake

    - name: Set up dbt profiles
      run: |
        mkdir -p ~/.dbt
        echo "
        handson:
          target: prod
          outputs:
            prod:
              type: snowflake
              account: $SNOWFLAKE_ACCOUNT
              user: $SNOWFLAKE_USER
              password: $SNOWFLAKE_PASSWORD
              role: $SNOWFLAKE_ROLE
              warehouse: $SNOWFLAKE_WAREHOUSE
              database: $SNOWFLAKE_DATABASE
              schema: $SNOWFLAKE_SCHEMA
              threads: 4
        " > ~/.dbt/profiles.yml

    ## Run dbt
    - name: Run dbt
      working-directory: ./dbt-project
      run: |
        dbt debug
        dbt compile
        dbt run

```
dbtで必要になるアカウント識別子やユーザー名、PWなどはすべて変数化し、GitHubのSecretsにぶち込みました。

（補足）Set up dbt profilesセクションについて
上記接続情報は`.dbt/profiles.yml`に記載するのですが、そのままGitに上げるとうまく変数を解決出来ませんでした。
やむを得ず一度echoし環境変数を解決した状態で`profiles.yml`を作成する手順にすることでうまくいきました。いいやり方があれば教えて下さい。

## 発火
mainブランチへPushすることで発火します。
```
Run dbt debug
07:01:45  Running with dbt=1.8.3
07:01:45  dbt version: 1.8.3
07:01:45  python version: 3.9.19
07:01:45  python path: /opt/hostedtoolcache/Python/3.9.19/x64/bin/python
07:01:45  os info: Linux-6.5.0-1022-azure-x86_64-with-glibc2.35
07:01:46  Using profiles dir at /home/runner/.dbt
07:01:46  Using profiles.yml file at /home/runner/.dbt/profiles.yml
07:01:46  Using dbt_project.yml file at /home/runner/work/dbt-snowflake/dbt-snowflake/dbt-project/dbt_project.yml
07:01:46  adapter type: snowflake
07:01:46  adapter version: 1.8.3
07:01:46  Configuration:
07:01:46    profiles.yml file [OK found and valid]
07:01:46    dbt_project.yml file [OK found and valid]
07:01:46  Required dependencies:
07:01:46   - git [OK found]

07:01:46  Connection:
07:01:46    account: ***
07:01:46    user: ***
07:01:46    database: ***
07:01:46    warehouse: ***
07:01:46    role: ***
07:01:46    schema: ***
07:01:46    authenticator: None
07:01:46    oauth_client_id: None
07:01:46    query_tag: None
07:01:46    client_session_keep_alive: False
07:01:46    host: None
07:01:46    port: None
07:01:46    proxy_host: None
07:01:46    proxy_port: None
07:01:46    protocol: None
07:01:46    connect_retries: 1
07:01:46    connect_timeout: None
07:01:46    retry_on_database_errors: False
07:01:46    retry_all: False
07:01:46    insecure_mode: False
07:01:46    reuse_connections: None
07:01:46  Registered adapter: snowflake=1.8.3
07:01:48    Connection test: [OK connection ok]

07:01:48  All checks passed!
07:01:52  Running with dbt=1.8.3
07:01:52  Registered adapter: snowflake=1.8.3
07:01:52  Unable to do partial parsing because saved manifest not found. Starting full parse.
07:01:54  Found 2 models, 4 data tests, 446 macros
07:01:54  
07:01:56  Concurrency: 4 threads (target='prod')
07:01:56  
07:02:03  Running with dbt=1.8.3
07:02:03  Registered adapter: snowflake=1.8.3
07:02:03  Found 2 models, 4 data tests, 446 macros
07:02:03  
07:02:06  Concurrency: 4 threads (target='prod')
07:02:06  
07:02:06  1 of 2 START sql table model ***.my_first_dbt_model ......................... [RUN]
07:02:09  1 of 2 OK created sql table model ***.my_first_dbt_model .................... [SUCCESS 1 in 2.32s]
07:02:09  2 of 2 START sql view model ***.my_second_dbt_model ......................... [RUN]
07:02:10  2 of 2 OK created sql view model ***.my_second_dbt_model .................... [SUCCESS 1 in 1.64s]
07:02:10  
07:02:10  Finished running 1 table model, 1 view model in 0 hours 0 minutes and 6.95 seconds (6.95s).
07:02:10  
07:02:10  Completed successfully
07:02:10  
07:02:10  Done. PASS=2 WARN=0 ERROR=0 SKIP=0 TOTAL=2
```
無事に実行までこぎつけられました。

## さいごに
ここまでやっておいてアレなのですが、通常SnowflakeはIP制限が設けられているので、GitHubActionsからSQLを打つことはないと思います。
ということで現在、CodeBuildから固定GIPを通じてSQLを実行する手順を検証しており、終わったら書きます。