> ## Documentation Index
> Fetch the complete documentation index at: https://honeydew.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub Actions

Honeydew publishes two reusable GitHub Actions that cover both sides of a merge:

| Action                                                                                            | When it runs      | What it does                                                   |
| ------------------------------------------------------------------------------------------------- | ----------------- | -------------------------------------------------------------- |
| [Honeydew Validate Workspace](https://github.com/marketplace/actions/honeydew-validate-workspace) | On a pull request | Fails the check if the branch introduces validation errors     |
| [Honeydew Publish](https://github.com/marketplace/actions/honeydew-publish)                       | After a merge     | Publishes a domain to a BI tool, so it serves the merged model |

Make validation a required status check, and GitHub blocks any pull request that would
introduce validation errors into your semantic layer. Add publishing to the merge event, and
your BI tools update themselves.

Add both workflows to the repository described under [Prerequisites](#prerequisites).
Each section below states how its workflow decides what to act on.

<Note>
  These actions are GitHub only. For other CI/CD systems, call the API directly — see
  [CI/CD Overview](/docs/governance/ci-cd/overview).
</Note>

## Prerequisites

1. **Metadata in this repository** — the workflow must live in the same GitHub repository
   that stores your Honeydew metadata, connected through the
   [Git integration](/docs/integration/git/github).
2. **Public GraphQL API enabled** — it is not enabled by default. Contact
   [support@honeydew.ai](mailto:support@honeydew.ai) to enable it for your organization.
3. **API key and secret** — create an [API key](/docs/access-control/api-keys). Validation needs
   the **Viewer** role; publishing needs **Editor**.
4. **GitHub secrets** — store the key and secret as repository secrets (for example,
   `HONEYDEW_API_KEY` and `HONEYDEW_API_SECRET`).

Honeydew names development branches `<workspace>/<branch>` — for example, branch `q3-fixes`
of workspace `sales` lives on the Git branch `sales/q3-fixes`. Validation reads the workspace
from that name; publishing is told which workspace to publish, and runs only when that
workspace's directory changed.

## Validate a workspace

### Value

* **Catch errors before merge.** A broken attribute, metric, or YAML file fails the check
  instead of reaching the `prod` branch.
* **No infrastructure.** The action calls the [GraphQL API](/docs/integration/graphql-api)
  directly. It has no dependencies and does not check out the repository.
* **Zero configuration for detection.** The workspace and branch are detected automatically
  from the Git branch name.

### How it works

The action reloads the workspace from Git and then checks every object for validation errors:

1. Calls `reset_workspace` to reload the branch from its latest commit.
2. Checks the workspace itself for load errors (for example, a YAML parse error).
3. Checks every object — entities and their fields, domains, dynamic datasets, global
   parameters, context items, and agents.
4. Reports each error as a GitHub annotation and a job summary table, and fails the run
   (non-zero exit) if any errors are found.

<Note>
  The reload runs in the API key's own session, not a user's. It does not affect anyone
  editing the workspace in Honeydew Studio, so the action is safe to run on active branches
  and as a required status check.
</Note>

The branch naming convention decides what is validated:

| Event                                                | What is validated                                     |
| ---------------------------------------------------- | ----------------------------------------------------- |
| Pull request from a `<workspace>/<branch>` branch    | That workspace, on that Honeydew branch               |
| Push to the default Git branch (for example, `main`) | All workspaces, on the `prod` branch                  |
| Anything else                                        | Requires the explicit `workspace` and `branch` inputs |

### Configure

<Steps>
  <Step title="Add the API key as GitHub secrets">
    In the repository, go to **Settings > Secrets and variables > Actions** and add:

    * `HONEYDEW_API_KEY` — the API key name
    * `HONEYDEW_API_SECRET` — the API key secret
  </Step>

  <Step title="Add the workflow file">
    Commit a workflow file to the repository that stores your semantic-layer metadata (see the
    example below). The action runs on pull requests and reports any validation errors.
  </Step>

  <Step title="Require the check to pass before merging">
    In **Settings > Branches**, add a branch protection rule (or ruleset) for your default
    branch that requires the validation status check to pass before merging. This is what
    blocks broken changes from reaching `prod`.

    <Tip>
      If you also require approval for a specific workspace with
      [Change Approval](/docs/governance/change-approval), keep that in a **separate** ruleset. A bypass
      applies to a whole ruleset, so separating them lets the Honeydew application keep bypassing
      this validation check while remaining unable to bypass the approval requirement.
    </Tip>
  </Step>
</Steps>

### Example workflow

This workflow validates the workspace changed by a pull request, on the pull request's
source branch, before it merges. It skips branches that are not Honeydew workspace branches,
so unrelated pull requests (docs, infrastructure) are not blocked.

```yaml .github/workflows/validate-honeydew-workspaces.yml theme={null}
name: Validate Honeydew Workspace

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [main]

permissions:
  contents: read

jobs:
  validate:
    name: Honeydew validation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - id: check
        name: Is this a Honeydew workspace branch?
        env:
          HEAD_REF: ${{ github.head_ref }}
        run: |
          # Only validate branches named "<workspace>/<branch>" whose workspace
          # directory exists. Skip infra/docs branches so they aren't blocked.
          workspace="${HEAD_REF%%/*}"
          if [ "$workspace" != "$HEAD_REF" ] && [ -f "$workspace/workspace.yml" ]; then
            echo "Validating Honeydew branch '$HEAD_REF'."
            echo "run=true" >> "$GITHUB_OUTPUT"
          else
            echo "Branch '$HEAD_REF' is not a Honeydew workspace branch; skipping."
            echo "run=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Validate workspace
        if: steps.check.outputs.run == 'true'
        uses: honeydew-ai/validate-workspace-action@v1
        with:
          api-key: ${{ secrets.HONEYDEW_API_KEY }}
          api-secret: ${{ secrets.HONEYDEW_API_SECRET }}
          # workspace and branch are auto-detected from github.head_ref.
          # base-url defaults to https://api.honeydew.cloud; set it for a custom hostname.
```

<Tip>
  For more usage examples, see the
  [action on the GitHub Marketplace](https://github.com/marketplace/actions/honeydew-validate-workspace).
</Tip>

### Inputs

| Input        | Required | Default                      | Description                                                                                                                   |
| ------------ | -------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `api-key`    | yes      |                              | Honeydew API key name.                                                                                                        |
| `api-secret` | yes      |                              | Honeydew API key secret.                                                                                                      |
| `base-url`   | no       | `https://api.honeydew.cloud` | Honeydew API base URL. Set this only if your organization uses a custom hostname (see **Settings > API** in the Honeydew UI). |
| `workspace`  | no       | auto-detected                | Workspace name to validate.                                                                                                   |
| `branch`     | no       | auto-detected                | Branch name to validate. Requires `workspace`; defaults to `prod` when only `workspace` is set.                               |

To validate a specific workspace and branch instead of relying on auto-detection:

```yaml theme={null}
      - uses: honeydew-ai/validate-workspace-action@v1
        with:
          api-key: ${{ secrets.HONEYDEW_API_KEY }}
          api-secret: ${{ secrets.HONEYDEW_API_SECRET }}
          workspace: sales
          branch: q3-fixes
```

### What is validated

The action first confirms the workspace loads, then checks every object in it. If the
workspace fails to load, the workspace-level errors are reported and the per-object checks
are skipped.

* **Entities** and their fields — datasets, dataset attributes, calculated attributes,
  and metrics
* **Domains**
* **Dynamic Datasets** (perspectives)
* **Global parameters**
* **Context items**
* **Agents**

The underlying GraphQL query is documented under
[Validate a Workspace](/docs/integration/graphql-api#validate-a-workspace).

## Publish to a BI tool

Once a change merges, the `prod` branch holds the new model but your BI tools still serve the
previous one. The publish action closes that gap: it publishes a [domain](/docs/domains) to
**Power BI**, **Sigma**, **Tableau** or **ThoughtSpot**, so a merge is all it takes to update
what those tools serve.

It wraps the mutations documented under
[Publish to BI Tools](/docs/integration/graphql-api#publish-to-bi-tools), and like the validation
action it has no dependencies and does not check out the repository.

### How publishing works

1. Reloads the workspace from Git (`reset_workspace`), so the published model reflects the
   merged commit. Set `reload: 'false'` to skip this.
2. Calls the destination's publish mutation with the domain and connector you configured.
3. Writes a job summary with a link to the published object, and reports the object's ID
   where the destination returns one.

You name the `workspace` and `domain` to publish; nothing is inferred from the branch the
workflow runs on. The Honeydew `branch` defaults to `prod`, because that is what a merge
produces — set it to publish a development branch instead, for example to a staging BI
workspace before merging.

<Note>
  The destination connector must be configured in Honeydew first, from the user settings menu
  under **Power BI** / **Sigma** / **Tableau** / **ThoughtSpot** → **Settings**. A connector
  configured this way is named `default`, which is the action's default `connector-name`.
</Note>

### Publish example workflow

Add **one workflow per workspace**. Its `paths:` filter is the whole gate: GitHub runs the
workflow only when the merged pull request touched that workspace's directory, so a merge
elsewhere in a repository holding many workspaces never republishes it.

```yaml .github/workflows/publish-sales.yml theme={null}
name: Publish Honeydew — sales

on:
  pull_request:
    types: [closed]
    branches: [main]
    paths: ['sales/**']

permissions:
  contents: read

jobs:
  publish:
    if: github.event.pull_request.merged == true
    uses: ./.github/workflows/honeydew-publish.yml
    with:
      workspace: sales
      domain: sales_exec
      targets: '["powerbi", "tableau"]'
      powerbi-model-name: Sales Exec
      powerbi-group-id: ${{ vars.SALES_POWERBI_GROUP_ID }}
      tableau-existing-datasource-id: ${{ vars.SALES_TABLEAU_DATASOURCE_ID }}
    secrets: inherit
```

The publish steps live once in a reusable workflow that every per-workspace file calls, so
adding a workspace is one small file:

```yaml .github/workflows/honeydew-publish.yml theme={null}
name: Honeydew publish (reusable)

on:
  workflow_call:
    inputs:
      workspace: {required: true, type: string}
      domain: {required: true, type: string}
      targets: {required: true, type: string}   # JSON array of destinations
      # ... each destination's inputs, all optional

jobs:
  publish:
    name: ${{ inputs.domain }} → ${{ matrix.target }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false        # one destination failing must not cancel the others
      matrix:
        target: ${{ fromJSON(inputs.targets) }}
    steps:
      - uses: honeydew-ai/publish-action@v1
        with:
          api-key: ${{ secrets.HONEYDEW_API_KEY }}
          api-secret: ${{ secrets.HONEYDEW_API_SECRET }}
          workspace: ${{ inputs.workspace }}
          domain: ${{ inputs.domain }}
          target: ${{ matrix.target }}
          powerbi-model-name: ${{ inputs.powerbi-model-name }}
          powerbi-group-id: ${{ inputs.powerbi-group-id }}
          tableau-existing-datasource-id: ${{ inputs.tableau-existing-datasource-id }}
          # ... and the Sigma and ThoughtSpot inputs
```

Each destination is its own job, so the run shows which of them deployed, and one that did
not can be re-run on its own. Each job uses only the inputs of the destination it publishes to
and ignores the rest, which is what lets one step definition serve every destination.

`tableau-existing-datasource-id` is the ID of the data source an earlier run created — see
[Updating instead of duplicating](#updating-instead-of-duplicating) for where to find it.

<Tip>
  Both files are ready to copy in the action's
  [examples directory](https://github.com/honeydew-ai/publish-action/tree/main/examples).
</Tip>

<Note>
  `paths:` filters a whole workflow, so it cannot vary per matrix entry. A single workflow
  covering several workspaces has to work out which ones changed itself — with
  [`dorny/paths-filter`](https://github.com/dorny/paths-filter), for example. Prefer one
  workflow per workspace: it needs no extra dependency and no API call.
</Note>

### Updating instead of duplicating

Publishing on every merge has to update the existing object rather than create another one.
How that works depends on the destination:

| Destination     | Repeat publishes                                                                                      |
| --------------- | ----------------------------------------------------------------------------------------------------- |
| **Power BI**    | Updates the model with the same `powerbi-model-name` in the workspace.                                |
| **ThoughtSpot** | Updates the table with the same `thoughtspot-table-name`.                                             |
| **Tableau**     | Pass `tableau-existing-datasource-id` to update. A name and project ID **create** a new data source.  |
| **Sigma**       | Pass `sigma-existing-data-model-id` to update. Without it, a new data model is **created** every run. |

<Tip>
  For Sigma, run the action once to create the data model, then store the ID it reports in the
  job summary as a repository variable and pass it back on later runs. Tableau publishes return
  a data source URL but no ID — look yours up with the `tableau_honeydew_datasources` query,
  under [Publish to BI Tools](/docs/integration/graphql-api#publish-to-bi-tools).
</Tip>

### Publish inputs

Common inputs:

| Input             | Required | Default                      | Description                                                                       |
| ----------------- | -------- | ---------------------------- | --------------------------------------------------------------------------------- |
| `api-key`         | yes      |                              | Honeydew API key name.                                                            |
| `api-secret`      | yes      |                              | Honeydew API key secret.                                                          |
| `target`          | yes      |                              | `powerbi`, `sigma`, `tableau` or `thoughtspot`.                                   |
| `base-url`        | no       | `https://api.honeydew.cloud` | Honeydew API base URL. Set this only if your organization uses a custom hostname. |
| `workspace`       | yes      |                              | Workspace to publish from.                                                        |
| `branch`          | no       | `prod`                       | Honeydew branch to publish.                                                       |
| `domain`          | yes      |                              | Domain to publish.                                                                |
| `connector-name`  | no       | `default`                    | Connector configured in Honeydew for the target tool.                             |
| `reload`          | no       | `true`                       | Reload the workspace from Git before publishing.                                  |
| `fail-on-warning` | no       | `false`                      | Fail the step when the publish succeeded but a follow-up step reported an error.  |

Destination-specific inputs:

| Input                            | Destination | Required | Description                                                               |
| -------------------------------- | ----------- | -------- | ------------------------------------------------------------------------- |
| `powerbi-model-name`             | Power BI    | yes      | Semantic model to create or update.                                       |
| `powerbi-group-id`               | Power BI    | yes      | Power BI workspace to publish into.                                       |
| `sigma-connection-id`            | Sigma       | yes      | Sigma connection to the data warehouse.                                   |
| `sigma-folder-id`                | Sigma       | yes      | Sigma folder to publish into.                                             |
| `sigma-model-name`               | Sigma       | no       | Data model name. Defaults to the domain's display name.                   |
| `sigma-existing-data-model-id`   | Sigma       | no       | Data model to update. Omit to create a new one.                           |
| `sigma-tags`                     | Sigma       | no       | Comma-separated [version tags](/docs/integration/bi-tools/sigma#version-tags). |
| `tableau-datasource-name`        | Tableau     | no       | Data source to create.                                                    |
| `tableau-project-id`             | Tableau     | no       | Project to create the data source in.                                     |
| `tableau-existing-datasource-id` | Tableau     | no       | Data source to update.                                                    |
| `thoughtspot-connection-name`    | ThoughtSpot | yes      | Honeydew connection in ThoughtSpot.                                       |
| `thoughtspot-table-name`         | ThoughtSpot | no       | Table name. Defaults to the domain's display name.                        |

For Tableau, pass exactly one of `tableau-existing-datasource-id`, to update a data source, or
both `tableau-datasource-name` and `tableau-project-id`, to create one. Any other combination
fails the step.

Look up the IDs these inputs take with the queries under
[Publish to BI Tools](/docs/integration/graphql-api#publish-to-bi-tools) — `powerbi_workspaces`,
`sigma_connections`, `sigma_folders`, `tableau_projects`,
`tableau_honeydew_datasources` and `thoughtspot_connections`.

### Outputs

| Output    | Description                                                                                                                  |
| --------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `url`     | Link to the published object in the target tool.                                                                             |
| `id`      | ID of the published object, where the destination returns one.                                                               |
| `warning` | Errors from steps that ran after a successful publish, such as refreshing the Power BI model or applying Sigma version tags. |

A warning means the publish itself succeeded. Set `fail-on-warning: 'true'` to fail the step
anyway.
