Use the WardenKit Licence Policy CLI in GitHub Actions

A complete GitHub Actions workflow that runs an open source license policy check on every pull request: installing from the lockfile, validating the policy, evaluating components, uploading the JSON result, and reporting the outcome in the job summary.

Task: Add a licence policy check to a GitHub repository that blocks a pull request on a denied licence. · Uses @wardenkit/open-source-licence-policy · Product overview · Documentation

What the repository needs first

  • licence-policy.json — the policy you authored and committed.
  • components.json — the component records, either committed or generated by an earlier step in the job.
  • @wardenkit/open-source-licence-policy in devDependencies and in the lockfile, so npm ci installs the exact reviewed version.

There is no WardenKit action to add from the GitHub Marketplace, and none is needed. The CLI is the supported integration: it is a normal dev dependency invoked with npx --no-install.

The workflow

.github/workflows/licence-policy.ymlyaml
name: licence-policy

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  licence-policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - run: npm ci

      # Pipeline defect if the policy document itself is invalid.
      - name: Validate the licence policy document
        run: |
          npx --no-install wardenkit-licence-policy validate-policy \
            --policy licence-policy.json \
            --json

      # Records which SPDX baseline decided this run.
      - name: Record the SPDX reference in use
        run: npx --no-install wardenkit-licence-policy reference --json

      - name: Evaluate the licence policy
        id: evaluate
        run: |
          npx --no-install wardenkit-licence-policy evaluate \
            --components components.json \
            --policy licence-policy.json \
            --output licence-policy-result.json \
            --json

      - name: Summarise the result
        if: always()
        run: |
          if [ -f licence-policy-result.json ]; then
            {
              echo '### Licence policy'
              echo ''
              echo '```json'
              node -e "const r=require('./licence-policy-result.json');console.log(JSON.stringify(r.aggregate,null,2))"
              echo '```'
            } >> "$GITHUB_STEP_SUMMARY"
          fi

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: licence-policy-result
          path: |
            licence-policy-result.json
          if-no-files-found: warn

What each step does, and why it is separate

StepPurposeFailure meaning
npm ciInstalls the exact evaluator version from the lockfile.Dependency problem, unrelated to licences.
validate-policyRejects an invalid policy before anything is evaluated against it.Exit 2 — your policy document is a pipeline defect.
referencePrints the pinned SPDX List version and snapshot into the job log.Rarely fails; useful when a result needs explaining later.
evaluateApplies your policy to your component records.Exit 1 — a CI-blocking outcome under your own policy. Exit 2 — malformed components.
Summarise / uploadPuts the aggregate in the job summary and keeps the JSON artefact.Runs with if: always(), so a blocked run still yields evidence.

The evaluate step is the only one that can fail for a licence reason, and it fails with exit 1 exactly when your policy's ciFailureOutcomes says it should. Everything else fails as an ordinary engineering problem, which keeps the red build legible.

Note what the job does not need: no secret, no token, no service URL and no network egress for the evaluation itself. Evaluation, policy validation and the reference command are local operations, so the check works unchanged on a self-hosted runner with no outbound access.

Common variations

Generate the component records in the job rather than committing them, by inserting your own inventory step before the evaluate step and uploading its output alongside the result:

Generated inventoryyaml
      - name: Build the component inventory
        run: node scripts/build-component-inventory.mjs > components.json

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: licence-policy-inputs
          path: components.json

Report a blocking outcome without failing the pull request while you are still tuning the policy, by tolerating the exit code and asserting on the aggregate later:

Report-only modeyaml
      - name: Evaluate the licence policy (report only)
        continue-on-error: true
        run: |
          npx --no-install wardenkit-licence-policy evaluate \
            --components components.json \
            --policy licence-policy.json \
            --output licence-policy-result.json \
            --json

Treat report-only as a migration state with an end date. A check that cannot fail is not enforcement, and a policy that is never enforced drifts from what the organization actually intends.

Run the same workflow on a schedule as well as on pull requests if your component records are generated: the dependency set can change without your repository changing, and a nightly run catches that.


Run this locally

Deterministic tooling, professional judgment

WardenKit performs deterministic technical validation, normalization and evaluation of the policy you author, over the records you supply. It does not provide legal advice, determine legal permissions or obligations, opine on licence compatibility, or certify compliance. Which licences your organization allows, denies or reviews remains your decision.

Product overview and pricing · All guides · Docs · Support

Corrections and questions: support@wardenkit.com.