GitHub Action examples

Previewing Hashboard changes in a pull request

This action automatically generates a Hashboard build for the current branch and posts the results as a comment on the pull request.

name: hashboard-preview
on: [pull_request]
jobs:
  create-hashboard-build:
    name: "Create Hashboard build"
    runs-on: ubuntu-latest
    if: ${{ !github.event.pull_request.merged }}
    steps:
      - uses: actions/checkout@v3
      - name: "Install dependencies"
        run: pip install hashboard-cli
      - name: Create Hashboard build
        id: create-hb-build
        env:
          HASHBOARD_PROJECT_ID: ${{ secrets.HASHBOARD_PROJECT_ID }}
          HASHBOARD_ACCESS_KEY_ID: ${{ secrets.HASHBOARD_ACCESS_KEY_ID }}
          HASHBOARD_SECRET_ACCESS_KEY_TOKEN: ${{ secrets.HASHBOARD_SECRET_ACCESS_KEY_TOKEN }}
        run: |
          hb build 2>&1 | tee out.txt
          EXIT_CODE=${PIPESTATUS[0]}
          RESULT=$(cat out.txt | sed 's/`//g')
          URL=$(grep -o 'https://[^ ]*' out.txt)
          EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
          echo "url=$URL" >> $GITHUB_OUTPUT
          echo "log<<$EOF" >> $GITHUB_OUTPUT
          echo "$RESULT" >> $GITHUB_OUTPUT
          echo "$EOF" >> $GITHUB_OUTPUT
          echo "exitCode=$EXIT_CODE" >> $GITHUB_OUTPUT
      - uses: actions/github-script@v6
        name: "Comment on the pull request"
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `[Hashboard build](${{ steps.create-hb-build.outputs.url }})\n\`\`\`${{ steps.create-hb-build.outputs.log }}`
            })
      - id: set-appropriate-exit-code
        name: "Set exit code"
        run: exit "${{ steps.create-hb-build.outputs.exitCode }}"```

Deploying Hashboard changes on merge to main

This action automatically generates a Hashboard build for the current branch and applies it to your project.

name: hashboard-deploy
on:
  push:
    branches: [main]
jobs:
  create-and-apply-hashboard-build:
    name: "Create and apply Hashboard build"
    runs-on: ubuntu-latest
 
    steps:
      - uses: actions/checkout@v3
 
      - name: "Install dependencies"
        run: pip install hashboard-cli
 
      - name: Create and apply Hashboard build
        id: create-and-apply-hb-build
        env:
          HASHBOARD_PROJECT_ID: ${{ secrets.HASHBOARD_PROJECT_ID }}
          HASHBOARD_ACCESS_KEY_ID: ${{ secrets.HASHBOARD_ACCESS_KEY_ID }}
          HASHBOARD_SECRET_ACCESS_KEY_TOKEN: ${{ secrets.HASHBOARD_SECRET_ACCESS_KEY_TOKEN }}
        run: |
          if [ "${{ github.event_name }}" = "push" ]; then
            hb build && hb build apply --no-confirm
          else
            echo "No changes pushed, skipping Hashboard deploy."
          fi

Previewing dbt Core + Hashboard changes in a pull request

This action creates a new staging schema in BigQuery, runs dbt into it, and then generates a Hashboard build pointing at that schema.

name: dbt-and-hashboard-preview
on: [pull_request]
env:
  BQ_DATASET_NAME: "ci_pr_${{ github.event.pull_request.number }}"
  BQ_DATASET_DESCRIPTION: "${{ github.event.pull_request.title}} (${{github.event.pull_request.html_url}})"
jobs:
  dbt-and-hashboard-preview:
    name: "Run dbt pipeline and create Hashboard preview"
    concurrency:
      group: preview-${{ github.event.pull_request.number || github.ref }}
      cancel-in-progress: true
    runs-on: ubuntu-latest
    if: ${{ !github.event.pull_request.merged }}
    steps:
      - uses: actions/checkout@v3
 
      - name: "Install dependencies"
        run: |
          pip install dbt-core dbt-bigquery
          dbt deps
          dbt --version
 
      - id: "auth"
        name: "Authenticate with Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          credentials_json: "${{ secrets.GCP_CREDENTIALS }}"
          create_credentials_file: true
          export_environment_variables: true
 
      - name: "Set up Google Cloud SDK"
        uses: "google-github-actions/setup-gcloud@v1"
 
      - name: "Create BigQuery dataset"
        run: |
          bq rm -f -r $GCP_PROJECT:$BQ_DATASET_NAME
          bq --location=US mk --description="$BQ_DATASET_DESCRIPTION" "$GCP_PROJECT:$BQ_DATASET_NAME"
 
      - name: dbt build
        id: dbt-build
        run: |
          dbt build --target=github --full-refresh
 
      - name: Create Hashboard build
        id: create-hb-build
        env:
          HASHBOARD_PROJECT_ID: ${{ secrets.HASHBOARD_PROJECT_ID }}
          HASHBOARD_ACCESS_KEY_ID: ${{ secrets.HASHBOARD_ACCESS_KEY_ID }}
          HASHBOARD_SECRET_ACCESS_KEY_TOKEN: ${{ secrets.HASHBOARD_SECRET_ACCESS_KEY_TOKEN }}
        run: |
          hb build --dbt-artifacts=./target 2>&1 | tee out.txt
          EXIT_CODE=${PIPESTATUS[0]}
          RESULT=$(cat out.txt | sed 's/`//g')
          URL=$(grep -o 'https://[^ ]*' out.txt)
          EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
          echo "url=$URL" >> $GITHUB_OUTPUT
          echo "log<<$EOF" >> $GITHUB_OUTPUT
          echo "$RESULT" >> $GITHUB_OUTPUT
          echo "$EOF" >> $GITHUB_OUTPUT
          echo "exitCode=$EXIT_CODE" >> $GITHUB_OUTPUT
 
      - uses: actions/github-script@v6
        name: "Comment on the pull request"
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `[Hashboard build](${{ steps.create-hb-build.outputs.url }})\n\`\`\`${{ steps.create-hb-build.outputs.log }}`
            })
 
      - id: set-appropriate-exit-code
        name: "Set exit code"
        run: exit "${{ steps.create-hb-build.outputs.exitCode }}"```

Deploying dbt Core + Hashboard to production

This action runs when commits are merged to your main branch. It runs dbt against the production target and then generates and applies a Hashboard build.

name: Deploy DBT and Hashboard
 
on:
  push:
    branches: [main]
 
jobs:
  dbt-and-hashboard-preview:
    name: "Run dbt pipeline and create Hashboard preview"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
 
      - name: "Install dependencies"
        run: |
          pip install dbt-core dbt-bigquery
          dbt deps
          dbt --version
 
      - name: dbt production build
        id: dbt-build
        run: |
          dbt build --target=production
 
      - name: Create and deploy Hashboard build
        id: create-hb-build
        env:
          HASHBOARD_PROJECT_ID: ${{ secrets.HASHBOARD_PROJECT_ID }}
          HASHBOARD_ACCESS_KEY_ID: ${{ secrets.HASHBOARD_ACCESS_KEY_ID }}
          HASHBOARD_SECRET_ACCESS_KEY_TOKEN: ${{ secrets.HASHBOARD_SECRET_ACCESS_KEY_TOKEN }}
        run: |
          hb build --dbt-artifacts=./target && hb build apply --no-confirm

Previewing dbt Cloud CI + Hashboard changes in a pull request

This action triggers a dbt Cloud CI job, waits for it to complete, and then generates a Hashboard build with any dbt models configured to use the data generated by the CI job.

If your CI job defers to a production run, you should configure it to run dbt clone (opens in a new tab) as its first step for any models used by Hashboard, so that your tables are available in the newly-created CI schema.

Example of configuring a dbt Cloud CI job
name: dbt Cloud & Hashboard CI
 
on:
  pull_request:
    types: [opened, synchronize]
 
jobs:
  dbt-cloud-run:
    runs-on: ubuntu-latest
    env:
      DBT_ACCOUNT_ID: 12345  # Replace with your account ID
      DBT_JOB_ID: 123456     # Replace with your CI job ID
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Trigger DBT Cloud Job
        id: trigger-job
        run: |
          response=$(curl -X POST -s -w "\n%{http_code}" -H "Authorization: Token ${{ secrets.DBT_CLOUD_API_KEY }}" \
                       -H "Content-Type: application/json" \
                       "https://cloud.getdbt.com/api/v2/accounts/$DBT_ACCOUNT_ID/jobs/$DBT_JOB_ID/run/" \
                       -d '{"cause": "Triggered by GH action", "git_branch": "${{ github.head_ref }}", "github_pull_request_id": ${{ github.event.pull_request.number }}}')
 
          http_code=$(echo "$response" | tail -n1)
          body=$(echo "$response" | sed '$d')
          if [ $http_code -ne 200 ]; then
            echo "Error: Received HTTP status code $http_code"
            echo "Response body: $body"
            exit 1
          fi
          job_id=$(echo $body | jq -r '.data.id')
          if [ -z "$job_id" ] || [ "$job_id" = "null" ]; then
            echo "Error: Failed to extract job ID from response"
            echo "Response body: $body"
            exit 1
          fi
          echo "JOB_ID=$job_id" >> $GITHUB_OUTPUT
 
      - name: Install dbt and Hashboard CLI
        run: |
          pip install dbt
          pip install hashboard-cli
 
      - name: Wait for DBT Cloud Job to complete and artifacts to be saved
        run: |
          job_id=${{ steps.trigger-job.outputs.JOB_ID }}
          while true; do
            response=$(curl -s -w "\n%{http_code}" -H "Authorization: Token ${{ secrets.DBT_CLOUD_API_KEY }}" \
                        "https://cloud.getdbt.com/api/v2/accounts/$DBT_ACCOUNT_ID/runs/$job_id/")
 
            http_code=$(echo "$response" | tail -n1)
            body=$(echo "$response" | sed '$d')
 
            if [ $http_code -ne 200 ]; then
              echo "Error: Received HTTP status code $http_code when checking job status"
              echo "Response body: $body"
              exit 1
            fi
 
            status=$(echo $body | jq -r '.data.status')
            artifacts_saved=$(echo $body | jq -r '.data.artifacts_saved')
 
            if [ "$status" = "10" ] && [ "$artifacts_saved" = "true" ]; then
              echo "Job completed successfully and artifacts are saved"
              break
            elif [ "$status" = "10" ]; then
              echo "Job completed successfully, waiting for artifacts to be saved..."
            elif [ "$status" = "20" ]; then
              echo "Error: dbt run failed"
              exit 1
            elif [ "$status" = "30" ]; then
              echo "Error: dbt run was cancelled"
              exit 1
            elif [ "$status" = "3" ]; then
              echo "Job still running, waiting 5 seconds..."
            else
              echo "Unknown dbt run status code $status"
              exit 1
            fi
 
            sleep 5
          done
 
      - name: Create artifacts directory
        run: mkdir ci_artifacts
 
      - name: Download manifest
        run: |
          response=$(curl -s -w "\n%{http_code}" --request GET \
               -H "Authorization: Token ${{ secrets.DBT_CLOUD_API_KEY }}" \
               "https://cloud.getdbt.com/api/v2/accounts/$DBT_ACCOUNT_ID/runs/${{ steps.trigger-job.outputs.JOB_ID }}/artifacts/manifest.json")
 
          http_code=$(echo "$response" | tail -n1)
          body=$(echo "$response" | sed '$d')
 
          if [ $http_code -ne 200 ]; then
            echo "Error: Received HTTP status code $http_code when downloading manifest"
            echo "Response body: $body"
            exit 1
          fi
 
          echo "$body" > ci_artifacts/manifest.json
 
      - name: Create Hashboard build
        id: create-hb-build
        env:
          HASHBOARD_PROJECT_ID: ${{ secrets.HASHBOARD_PROJECT_ID }}
          HASHBOARD_ACCESS_KEY_ID: ${{ secrets.HASHBOARD_ACCESS_KEY_ID }}
          HASHBOARD_SECRET_ACCESS_KEY_TOKEN: ${{ secrets.HASHBOARD_ACCESS_KEY_TOKEN }}
        run: |
          hb build  --dbt-artifacts=./ci_artifacts | tee out.txt
          EXIT_CODE=${PIPESTATUS[0]}
 
          RESULT=$(cat out.txt | sed 's/`//g')
          EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
          echo "log<<$EOF" >> $GITHUB_OUTPUT
          echo "$RESULT" >> $GITHUB_OUTPUT
          echo "$EOF" >> $GITHUB_OUTPUT
          echo "exitCode=$EXIT_CODE" >> $GITHUB_OUTPUT
 
      - uses: actions/github-script@v6
        name: "Comment on the pull request"
        with:
          script: |
            const backticks = '```';
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `${backticks}\n${steps.create-hb-build.outputs.log}\n${backticks}`
            })
 
      - id: set-appropriate-exit-code
        name: "Set exit code"
        run: exit "${{ steps.create-hb-build.outputs.exitCode }}"

Deploying dbt Cloud + Hashboard changes to production

This action runs when commits are merged to your main branch. It triggers a production dbt Cloud deploy job, waits for it to complete, and then generates and applies a Hashboard build.

name: Deploy DBT and Hashboard
 
on:
  push:
    branches: [main]
 
jobs:
  dbt-cloud-run:
    runs-on: ubuntu-latest
    env:
      DBT_ACCOUNT_ID: 12345  # Replace with your account ID
      DBT_JOB_ID: 123456     # Replace with your prod deploy job ID
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
 
      - name: Trigger DBT Cloud Deployment
        id: trigger-job
        run: |
          response=$(curl -X POST -s -w "\n%{http_code}" -H "Authorization: Token ${{ secrets.DBT_CLOUD_API_KEY }}" \
                       -H "Content-Type: application/json" \
                       "https://cloud.getdbt.com/api/v2/accounts/$DBT_ACCOUNT_ID/jobs/$DBT_JOB_ID/run/" \
                       -d '{"cause": "Production deploy from GitHub Action"}')
 
          http_code=$(echo "$response" | tail -n1)
          body=$(echo "$response" | sed '$d')
 
          if [ $http_code -ne 200 ]; then
            echo "Error: Received HTTP status code $http_code"
            echo "Response body: $body"
            exit 1
          fi
 
          job_id=$(echo $body | jq -r '.data.id')
          if [ -z "$job_id" ] || [ "$job_id" = "null" ]; then
            echo "Error: Failed to extract job ID from response"
            echo "Response body: $body"
            exit 1
          fi
 
          echo "JOB_ID=$job_id" >> $GITHUB_OUTPUT
 
      - name: "Install Python 3.9"
        id: install-python
        uses: actions/setup-python@v4
        with:
          python-version: "3.11"
 
      - name: Install Hashboard CLI
        run: |
          pip install dbt
          pip install hashboard-cli
 
      - name: Wait for DBT Cloud Job to complete and artifacts to be saved
        run: |
          job_id=${{ steps.trigger-job.outputs.JOB_ID }}
          while true; do
            response=$(curl -s -w "\n%{http_code}" -H "Authorization: Token ${{ secrets.DBT_CLOUD_API_KEY }}" \
                        "https://cloud.getdbt.com/api/v2/accounts/$DBT_ACCOUNT_ID/runs/$job_id/")
 
            http_code=$(echo "$response" | tail -n1)
            body=$(echo "$response" | sed '$d')
 
            if [ $http_code -ne 200 ]; then
              echo "Error: Received HTTP status code $http_code when checking job status"
              echo "Response body: $body"
              exit 1
            fi
 
            status=$(echo $body | jq -r '.data.status')
            artifacts_saved=$(echo $body | jq -r '.data.artifacts_saved')
 
            if [ "$status" = "10" ] && [ "$artifacts_saved" = "true" ]; then
              echo "Job completed successfully and artifacts are saved"
              break
            elif [ "$status" = "10" ]; then
              echo "Job completed successfully, waiting for artifacts to be saved..."
            elif [ "$status" = "20" ]; then
              echo "Error: dbt run failed"
              exit 1
            elif [ "$status" = "30" ]; then
              echo "Error: dbt run was cancelled"
              exit 1
            elif [ "$status" = "3" ]; then
              echo "Job still running, waiting 5 seconds..."
            else
              echo "Unknown dbt run status code $status"
              exit 1
            fi
 
            sleep 5
          done
 
      - name: Create artifacts directory
        run: mkdir ci_artifacts
 
      - name: Download manifest
        run: |
          response=$(curl -s -w "\n%{http_code}" --request GET \
               -H "Authorization: Token ${{ secrets.DBT_CLOUD_API_KEY }}" \
               "https://cloud.getdbt.com/api/v2/accounts/$DBT_ACCOUNT_ID/runs/${{ steps.trigger-job.outputs.JOB_ID }}/artifacts/manifest.json")
 
          http_code=$(echo "$response" | tail -n1)
          body=$(echo "$response" | sed '$d')
 
          if [ $http_code -ne 200 ]; then
            echo "Error: Received HTTP status code $http_code when downloading manifest"
            echo "Response body: $body"
            exit 1
          fi
 
          echo "$body" > ci_artifacts/manifest.json
 
      - name: Create and apply Hashboard build
        id: create-hb-build
        env:
          HASHBOARD_PROJECT_ID: ${{ secrets.HASHBOARD_PROJECT_ID }}
          HASHBOARD_ACCESS_KEY_ID: ${{ secrets.HASHBOARD_ACCESS_KEY_ID }}
          HASHBOARD_SECRET_ACCESS_KEY_TOKEN: ${{ secrets.HASHBOARD_ACCESS_KEY_TOKEN }}
        run: |
          hb build  --dbt-artifacts=./ci_artifacts && hb build apply --no-confirm