Pipelines: GitHub Actions and Azure Pipelines

pbiplint runs in two pipelines with a step of its own: GitHub Actions, through the pbiplint Action, and Azure Pipelines, through the pbiplint task. Each runs the pbiplint CLI on your runner or agent, so the lint itself sends nothing anywhere. What a pipeline adds is the trip the findings make afterwards: to the run, where your team reads them, and, in GitHub unless you turn it off, to code scanning. A pipeline does not run on your machine, so "nothing leaves your machine" is not its promise. This page lists what leaves instead, step by step, and how to check each item.

In any other CI system, run the CLI as a step and gate on its exit code; the CLI page says how.

GitHub Actions

What the Action is

The pbiplint Action (opens in a new tab) is a composite action: it runs the published pbiplint CLI at a pinned version, then turns the report into a failed or passed check, annotations on the lines of the pull request, a job summary with the full ranked report, and code scanning alerts. Everything it runs is in action.yml (opens in a new tab) and the scripts in src (opens in a new tab), which import nothing outside Node.js. There is no bundled code. The README (opens in a new tab) describes each of these in detail.

Get the Action

The Action is on the GitHub Marketplace (opens in a new tab), and its source is at pbiplint/action (opens in a new tab). Choose how you refer to it by how much change you accept without review:

Each release of the Action pins a CLI version, the default of its pbiplint-version input in action.yml (opens in a new tab). Set the input to run another version.

The Action works on GitHub-hosted Ubuntu, Windows, and macOS runners. A self-hosted runner needs Node.js 20.19 or a later 20 release, or 22.12 or later.

Use the Action

name: Lint Power BI
on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read
  security-events: write # for code scanning; drop it and set upload-sarif: false otherwise

jobs:
  pbiplint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: pbiplint/action@v1
        with:
          path: Sales.pbip

The permissions block gives the job two things and nothing more. contents: read lets the checkout step read the repository. security-events: write lets GitHub's upload step send the report to code scanning; it is the only write permission the Action uses, and without it the upload fails, the step says why, and the run carries on.

The inputs a first run usually changes are path, what to lint; fail-on, the lowest severity that fails the check (error by default, none to report without failing); and upload-sarif, whether to send the report to code scanning. The README has every input and output (opens in a new tab).

For several projects in one repository, add a step for each, each with its own path and its own sarif-category, so one upload does not replace another.

On a private repository, code scanning needs GitHub Code Security. Without it the upload fails, the step says why, and the run carries on; upload-sarif: false stops it trying.

What leaves the runner, and where it goes

  1. The download. The Action runs npx to fetch the pinned pbiplint from the public npm registry (twice per run: once for the SARIF report and once for the job summary). The request names the package and its version, and carries nothing from your project. It is the only request to a host outside GitHub.
  2. The lint. pbiplint runs on the runner, reads the project path names and its pbiplint.config.json, and makes no network request of its own. The CLI page lists exactly which files it reads.
  3. The check, the annotations, and the job summary. Written to the workflow run, in your repository on GitHub. Anyone who can read the repository can see them, signed in to GitHub. On a public repository that is anyone with a GitHub account, and the findings name your tables, columns, measures, and file paths, so they are as public as the code they describe.
  4. Code scanning, when upload-sarif is on. GitHub's own upload step, github/codeql-action/upload-sarif, pinned to a commit in action.yml, sends the SARIF report to your repository's code scanning, through the GitHub API and no other host. With the report it sends the commit, the branch, the workflow and job names, the runner's workspace path, any matrix values, the tool's name, and a status report on the step: the runner's operating system and image, the inputs, and, if the step fails, the error. Code scanning alerts are visible to people with write, maintain, or admin access to the repository, and to organization owners, on public and private repositories alike; anyone who can read the repository sees the alerts that land on a pull request's lines.
  5. Nothing to pbiplint. No request goes to pbiplint.com, to McKinley Consulting, or anywhere else. There is no pbiplint server to send anything to.

On a self-hosted runner the list is the same, with the runner inside your network. The npm download is still the one request beyond GitHub, and it follows npm's own settings: set NPM_CONFIG_REGISTRY on the job, or put an .npmrc with a registry= line at the top of the repository, and the Action's npx fetches pbiplint from your registry mirror instead. A firm that blocks the public registry can use the Action that way.

Check the Action

Azure Pipelines

What the task is

The pbiplint task (opens in a new tab) runs the published pbiplint CLI at a pinned version, then reports findings as build issues with their file, line, and rule (the agent keeps at most 10 errors and 10 warnings per step), attaches the full ranked report to the run's Extensions tab, publishes the SARIF report as a build artifact, and fails the step on findings. It is the scripts in task (opens in a new tab), which import nothing outside Node.js, in an extension on the Visual Studio Marketplace (opens in a new tab).

For organizations that cannot install an extension, the plain YAML route (opens in a new tab) runs the same CLI from a script step, with no extension at all.

Get the task

The extension is on the Visual Studio Marketplace (opens in a new tab), and its source and README are at pbiplint/azure-pipelines (opens in a new tab).

Use the task

trigger:
  branches:
    include: [main]

pool:
  vmImage: ubuntu-latest

steps:
  - task: pbiplint@1
    inputs:
      path: Sales.pbip

The inputs are the Action's, spelled the way Azure Pipelines allows: path, failOn, and publishSarif are the ones a first run usually changes, and the README has every input (opens in a new tab). For several projects in one repository, add a step for each with its own path and sarifCategory.

The plain YAML route is one bash step: copy examples/plain.yml (opens in a new tab) to azure-pipelines.yml and set the path. It runs the same pinned CLI, attaches the same report to the run, publishes the same SARIF artifact, and fails the step the same way. It leaves out the build issues and the output variables, which need the task's script.

Advanced Security. Teams with GitHub Advanced Security for Azure DevOps, a paid add-on, can send the findings to its code scanning alerts by adding Microsoft's AdvancedSecurity-Publish@1 (opens in a new tab) step after pbiplint's. The task's README has the YAML (opens in a new tab). It needs pbiplint 0.2.4 or later, and it has not been run against the service itself, since that needs the paid add-on.

What leaves the agent, and where it goes

  1. The extension and the download. The organization installs the extension from the Visual Studio Marketplace (opens in a new tab) once. On each run, the task runs npx to fetch the pinned pbiplint from the public npm registry, a request that carries nothing from your project. The plain YAML route skips the extension and makes the same request.
  2. The lint. As in GitHub Actions: on the agent, reading only the path and its config, with no network request of its own.
  3. Build issues and the run summary. Written to the run, in your Azure DevOps organization. Anyone with permission to view the pipeline's builds can see them, which a project's Readers have by default. Azure DevOps no longer allows public projects: none can be created, and the ones left become private in 2027.
  4. The SARIF artifact. Kept with the run, for as long as the project's retention settings keep the run's artifacts.
  5. Advanced Security, only when a team adds Microsoft's step. That step sends the SARIF report to the organization's Advanced Security, where people with permission to view its alerts, a project's Contributors by default, can see them.
  6. Nothing to pbiplint, as above.

On a self-hosted agent the list is the same, with the agent inside your network, and the npm download is the one request beyond Azure DevOps. The task passes the agent's environment to npx, so NPM_CONFIG_REGISTRY on the pipeline, or an .npmrc with a registry= line at the top of the repository, points it at your registry mirror. Behind a proxy, set HTTPS_PROXY or npm's own proxy setting: the agent's proxy configuration is not passed to npx.

Check the task

Both pipelines

Each pipeline runs the same CLI, so the CLI page applies to the lint step of either: what it reads, and how to check that it sends nothing. The pbiplint Privacy Promise says, in a sentence, what this page lists in full.

If you ever find either pipeline sending something this page does not list, that is a security bug. Report it privately, as the security policy (opens in a new tab) describes.