Skip to content

Commit

Permalink
Add infrastructure to check for unapproved npm dependency licenses
Browse files Browse the repository at this point in the history
A task and GitHub Actions workflow are provided here for checking the license types of npm-managed project dependencies.

On every push and pull request that affects relevant files, the CI workflow will check:

- If the dependency licenses cache is up to date
- If any of the project's dependencies have an unapproved license type.

Approval can be based on:

- Universally allowed license type
- Individual dependency
  • Loading branch information
per1234 committed Oct 8, 2024
1 parent 1b78422 commit ee49ac2
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ Thanks!

## Common Development Operations

### Dependency License Metadata

Metadata about the license types of all dependencies is cached in the repository. To update this cache, run the following command from the repository root folder:

```text
task general:cache-dep-licenses
```

The necessary **Licensed** tool can be installed by following [these instructions](https://github.com/github/licensed#as-an-executable).

Unfortunately, **Licensed** does not have Windows support.

An updated cache is also generated whenever the cache is found to be outdated by the "**Check Go Dependencies**" CI workflow and made available for download via the `dep-licenses-cache` [workflow artifact](https://docs.github.com/actions/managing-workflow-runs/downloading-workflow-artifacts).

### Running Checks

Checks and tests are set up to ensure the project content is functional and compliant with the established standards.
Expand Down
156 changes: 156 additions & 0 deletions .github/workflows/check-npm-dependencies-task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-npm-dependencies-task.md
name: Check npm Dependencies

# See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows
on:
create:
push:
paths:
- ".github/workflows/check-npm-dependencies-task.ya?ml"
- ".licenses/**"
- ".licensed.json"
- ".licensed.ya?ml"
- "Taskfile.ya?ml"
- "**/.gitmodules"
- "**/.npmrc"
- "**/package.json"
- "**/package-lock.json"
pull_request:
paths:
- ".github/workflows/check-npm-dependencies-task.ya?ml"
- ".licenses/**"
- ".licensed.json"
- ".licensed.ya?ml"
- "Taskfile.ya?ml"
- "**/.gitmodules"
- "**/.npmrc"
- "**/package.json"
- "**/package-lock.json"
schedule:
# Run periodically to catch breakage caused by external changes.
- cron: "0 8 * * WED"
workflow_dispatch:
repository_dispatch:

jobs:
run-determination:
runs-on: ubuntu-latest
permissions: {}
outputs:
result: ${{ steps.determination.outputs.result }}
steps:
- name: Determine if the rest of the workflow should run
id: determination
run: |
RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x"
# The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead.
if [[
"${{ github.event_name }}" != "create" ||
"${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX
]]; then
# Run the other jobs.
RESULT="true"
else
# There is no need to run the other jobs.
RESULT="false"
fi
echo "result=$RESULT" >> $GITHUB_OUTPUT
check-cache:
needs: run-determination
if: needs.run-determination.outputs.result == 'true'
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

# This is required to allow jonabc/setup-licensed to install licensed via Ruby gem.
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ruby # Install latest version

- name: Install licensed
uses: jonabc/setup-licensed@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json

- name: Install Task
uses: arduino/setup-task@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Update dependencies license metadata cache
run: task --silent general:cache-dep-licenses

- name: Check for outdated cache
id: diff
run: |
git add .
if ! git diff --cached --color --exit-code; then
echo
echo "::error::Dependency license metadata out of sync. See: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-go-dependencies-task.md#metadata-cache"
exit 1
fi
# Some might find it convenient to have CI generate the cache rather than setting up for it locally
- name: Upload cache to workflow artifact
if: failure() && steps.diff.outcome == 'failure'
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
include-hidden-files: true
name: dep-licenses-cache
path: .licenses/

check-deps:
needs: run-determination
if: needs.run-determination.outputs.result == 'true'
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

# This is required to allow jonabc/setup-licensed to install licensed via Ruby gem.
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ruby # Install latest version

- name: Install licensed
uses: jonabc/setup-licensed@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json

- name: Install Task
uses: arduino/setup-task@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Check for dependencies with unapproved licenses
run: task --silent general:check-dep-licenses
63 changes: 63 additions & 0 deletions .licensed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# See: https://github.com/github/licensed/blob/master/docs/configuration.md

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-dependencies/Apache-2.0/.licensed.yml
allowed:
# The following are based on https://www.apache.org/legal/resolved.html#category-a
- apache-2.0
- apache-1.1
- php-3.01
# "MX4J License" - no SPDX ID
- bsd-2-clause
- bsd-2-clause-netbsd # Deprecated ID for `bsd-2-clause`
- bsd-2-clause-views
- bsd-2-clause-freebsd # Deprecated ID for `bsd-2-clause-views`
- bsd-3-clause
- bsd-3-clause-clear
# "DOM4J License" - no SPDX ID
- postgresql
# "Eclipse Distribution License 1.0" - no SPDX ID
- mit
- x11
- isc
- smlnj
- standardml-nj # Deprecated ID for `smlnj`
# "Cup Parser Generator" - no SPDX ID
- icu
- ncsa
- w3c
# "W3C Community Contributor License Agreement" - no SPDX ID
- xnet
- zlib
# "FSF autoconf license" - no SPDX ID
- afl-3.0
# "Service+Component+Architecture+Specifications" - no SPDX ID
# "OOXML XSD ECMA License"
- ms-pl
- cc-pddc
- psf-2.0
# "Python Imaging Library Software License"
- apafml
- bsl-1.0
- ogl-uk-3.0
- wtfpl
- unicode-tou
- zpl-2.0
# "ACE license" - no SPDX ID
- upl-1.0
# "Open Grid Forum License" - no SPDX ID
# 'Google "Additional IP Rights Grant (Patents)" file' - no SPDX ID
- unlicense
- hpnd
- mulanpsl-2.0
- cc0-1.0
# The following are based on individual license text
- lgpl-2.0-or-later
- lgpl-2.0+ # Deprecated ID for `lgpl-2.0-or-later`
- lgpl-2.1-only
- lgpl-2.1 # Deprecated ID for `lgpl-2.1-only`
- lgpl-2.1-or-later
- lgpl-2.1+ # Deprecated ID for `lgpl-2.1-or-later`
- lgpl-3.0-only
- lgpl-3.0 # Deprecated ID for `lgpl-3.0-only`
- lgpl-3.0-or-later
- lgpl-3.0+ # Deprecated ID for `lgpl-3.0-or-later`
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Tests Status](https://github.com/arduino/arduino-lint-action/actions/workflows/test-javascript-jest-task.yml/badge.svg)](https://github.com/arduino/arduino-lint-action/actions/workflows/test-javascript-jest-task.yml)
[![Integration Tests Status](https://github.com/arduino/arduino-lint-action/workflows/Integration%20Tests/badge.svg)](https://github.com/arduino/arduino-lint-action/actions?workflow=Integration+Tests)
[![Check License status](https://github.com/arduino/arduino-lint-action/actions/workflows/check-license.yml/badge.svg)](https://github.com/arduino/arduino-lint-action/actions/workflows/check-license.yml)
[![Check npm Dependencies status](https://github.com/arduino/arduino-lint-action/actions/workflows/check-npm-dependencies-task.yml/badge.svg)](https://github.com/arduino/arduino-lint-action/actions/workflows/check-npm-dependencies-task.yml)
[![Check Packaging status](https://github.com/arduino/arduino-lint-action/actions/workflows/check-packaging-ncc-typescript-npm.yml/badge.svg)](https://github.com/arduino/arduino-lint-action/actions/workflows/check-packaging-ncc-typescript-npm.yml)
[![Check Prettier Formatting status](https://github.com/arduino/arduino-lint-action/actions/workflows/check-prettier-formatting-task.yml/badge.svg)](https://github.com/arduino/arduino-lint-action/actions/workflows/check-prettier-formatting-task.yml)
[![Check TypeScript Configuration status](https://github.com/arduino/arduino-lint-action/actions/workflows/check-tsconfig-task.yml/badge.svg)](https://github.com/arduino/arduino-lint-action/actions/workflows/check-tsconfig-task.yml)
Expand Down
33 changes: 33 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,33 @@ tasks:
- task: poetry:sync
- task: ts:build

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-dependencies-task/Taskfile.yml
general:cache-dep-licenses:
desc: Cache dependency license metadata
deps:
- task: general:prepare-deps
cmds:
- |
if ! which licensed &>/dev/null; then
if [[ {{OS}} == "windows" ]]; then
echo "Licensed does not have Windows support."
echo "Please use Linux/macOS or download the dependencies cache from the GitHub Actions workflow artifact."
else
echo "licensed not found or not in PATH."
echo "Please install: https://github.com/github/licensed#as-an-executable"
fi
exit 1
fi
- licensed cache

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-dependencies-task/Taskfile.yml
general:check-dep-licenses:
desc: Check for unapproved dependency licenses
deps:
- task: general:cache-dep-licenses
cmds:
- licensed status

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/spell-check-task/Taskfile.yml
general:check-spelling:
desc: Check for commonly misspelled words
Expand Down Expand Up @@ -64,6 +91,12 @@ tasks:
cmds:
- npx prettier --write .

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-npm-dependencies-task/Taskfile.yml
general:prepare-deps:
desc: Prepare project dependencies for license check
deps:
- task: npm:install-deps

js:test:
desc: |
Test the project's JavaScript/TypeScript code.
Expand Down

0 comments on commit ee49ac2

Please sign in to comment.