Checks that files have the correct SPDX headers according to a given configuration.
- Preface
- Usage
- Configuration
- License check
- Copyright check
- Outputs
- File patterns
- Organizing rules
- Ignoring files
- Contributing
- License
What are SPDX IDs?
- An easy way to label your source code’s licenses
- Needs only one new comment line per file
- Human-readable and machine readable
This action enables to configure checks for the following SPDX headers:
For better user experience in PRs, this repository also includes a Github Composite Action that checks the SPDX headers and posts the results into a comment in the PR. Read the PR comments section for more information.
Create a configuration file check-spdx-headers.config.yml
at the root of your repository, containing the rules that you want to enforce.
Note
Using a configuration file is optional. You can also use the action inputs to define the configuration.
# SPDX headers configuration
rules:
- name: "Source code"
headers:
- files:
- "**/*.js"
license: "Apache-2.0"
copyright: "My Company"
ignore:
- "**/node_modules/**"
reporter: "text"
log: "debug"
Example of a GitHub Actions workflow file:
name: Check SPDX headers
on: push
jobs:
check-spdx-headers:
name: Check SPDX headers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check
id: check-spdx-headers
uses: Telefonica/check-spdx-headers@v1
That's it! The action will check the SPDX headers of your files according to the configuration file on every push.
This repository also includes a Github Composite Action that uses this action to check the SPDX headers and post the results in a comment in the PR.
The composite action accepts the same inputs as the main action, except for:
reporter
- Thereporter
option is always set tomarkdown
.fail-on-not-valid
- Thefail-on-not-valid
option is always set tofalse
to get the results and post them. After that, it automatically fails the workflow by forcing an error if there are files with incorrect headers.
Here you have an example of a GitHub Actions workflow file using the check-and-comment
action:
name: Check SPDX headers
on: pull_request
permissions:
contents: read
pull-requests: write
statuses: write
jobs:
check-spdx-headers:
name: Check SPDX Headers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check SPDX headers
uses: Telefonica/check-spdx-headers/.github/actions/check-and-comment@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
The configuration file is a YAML file that must be placed at the root of your repository by default (you can also change the path by using the action inputs). It can contain the following properties:
rules
: List of rules to enforce. Each rule must contain the following properties:name
: Name of the rule. Useful to identify the rule in the output.headers
: List of files/headers to check, containing the following properties:files
: File glob pattern to match the files to check or list of glob patterns. Further info in the File patterns section.license
: License or list of approved licenses that the files must have. Approved licenses may be simple SPDX license identifiers like MIT, plus-ranges like EPL-2.0+, or licenses with exceptions like Apache-2.0 WITH LLVM. They may not be compound expressions using AND or OR. Further info in the License section.copyright
: Copyright or list of approved copyrights that the files must have. They can be simple strings or regular expressions. Further info in the Copyright section.ignore
: File glob pattern or list of file glob patterns to ignore in this specific headers check.
ignore
: File glob pattern or list of file glob patterns to ignore in all headers checks in this rule.
ignore
: File glob pattern or list of file glob patterns to ignore in all rules.reporter
: Reporter to use. Possible values aretext
,markdown
andjson
. Default istext
. Further info in the Reporters section.log
: Log level to use. Possible values aresilly
,debug
,info
,warning
anderror
. Default isinfo
. This option enables logs for the headers check. You can also enable logs for the action itself (useful if you find any problem while the action is loading the configuration, for example) by setting theACTIONS_STEP_DEBUG
secret totrue
.failOnNotValid
: Boolean value to determine if the action should fail (exit code 1) when a file does not have the correct headers. Default istrue
. You can disable it and get the results in the output if you want to send the results to a Github comment in a PR, for example. See the Outputs section for more info.
The action also allows to set the configuration by using inputs. When defined, they will override the values in the configuration file. The inputs are:
-
config-file
: Path to the configuration file. Default ischeck-spdx-headers.config.yml
. -
reporter
: Reporter to use. Possible values aretext
,markdown
andjson
. Default istext
. -
log
: Log level to use. Possible values aresilly
,debug
,info
,warning
anderror
. Default isinfo
. -
fail-on-not-valid
: Boolean value to determine if the action should fail when a file does not have the correct headers. Default istrue
. -
ignore
: Multiline string with file glob pattern or list of file glob patterns to ignore in all headers checks expressed as a JSON array.
Example:ignore: | [ "**/node_modules/**", "**/dist/**" ]
-
rules
: Multiline string with rules to enforce expressed as a JSON. Read the Configuration File section for more info. NOTE: Using YAML is not recommended in Github action inputs because it might cause problems with the YAML parser due to indentation issues. So, it is better to use JSON, which is also a valid YAML. Example:rules: | [ "name": "Source code", "headers": [ { "files": [ "**/*.js" ], "license": "Apache-2.0" } ] ]
-
config
: Multiline string with the whole configuration expressed as a JSON object as in the configuration file. It will extend the values defined in the configuration file. Any config value that is defined in other inputs will override the values here. Example:config: | { "rules": [ { "name": "Source code", "headers": [ { "files": [ "**/*.js" ], "license": "Apache-2.0" } ] } ], "reporter": "markdown", "ignore": [ "**/node_modules/**", "**/dist/**" ], "log": "debug" }
Warning
Note that some properties are defined in camelCase in the configuration file, while they are defined in kebab-case in the inputs. This is because the configuration file tries to follow NodeJs conventions in order to pass the values directly to the underlying library, while the inputs follow a GitHub Actions convention.
Tip
Note that you can use the inputs to override the values in the configuration file, or to define the whole configuration if you don't want to use a file.
So, you can use the configuration file, the inputs, or both. The action will merge the values in the following order:
- Values in the configuration file.
- Values in the
config
input. - Values in the
rules
input. - Values in the
ignore
input. - The rest of the inputs.
Example of a complex configuration using both the configuration file and the inputs:
# Configuration file
rules:
- name: "Source code"
headers:
- files:
- "**/*.js"
license:
- "Apache-2.0"
copyright: "My Company"
ignore: "**/node_modules/**"
# GitHub Actions workflow file with inputs
name: Check SPDX headers
on: push
jobs:
check-spdx-headers:
name: Check SPDX headers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check
id: check-spdx-headers
uses: Telefonica/check-spdx-headers@v1
with:
config-file: "spdx.config.yml"
# Properties with preference over values defined in any other place
reporter: "markdown"
log: "debug"
fail-on-not-valid: true
# Properties overriding the values in the configuration file and in the "config" input
ignore: |
["**/dist/**", "**/node_modules/**"]
rules: |
[
{
"name": "Source code",
"headers": [
{
"files": [
"**/*.js"
],
"license": "Apache-2.0"
}
]
}
]
# This will extend the values in the configuration file
config: |
{
"ignore": [
"**/node_modules/**"
]
}
The license check is done by comparing the license in the SPDX header of the file with the approved licenses defined in the configuration. The approved licenses can be simple SPDX license identifiers like MIT, plus-ranges like EPL-2.0+, or licenses with exceptions like Apache-2.0 WITH LLVM. They may not be compound expressions using AND or OR.
You can define different licenses in different rules, and even in the same rule. The action will check if the license in the file satisfies any of the approved licenses.
Example of a configuration file with different licenses for different files:
rules:
- name: "Source code"
headers:
- files:
- "**/*.js"
- "**/*.ts"
- "src/**"
license: "Apache-2.0"
ignore:
- "*.config.js"
- name: "Config files"
headers:
- files:
- "**/*.yml"
- "*.config.js"
license:
- "MIT"
- "Apache-2.0"
In this example, the action will check:
- That the files with the extensions
.js
(except files in the root folder matching*.config.js
) and.ts
, and the files in thesrc
directory have the licenseApache-2.0
. - That the files with the extensions
.yml
and those in the root folder matching*.config.js
have the licensesMIT
orApache-2.0
.
Note
Read the SPDX License List to get the correct identifiers for the licenses you want to use, and the SPDX License Expressions to get more information about the license expressions.
You can also check the documentation of the [spdx-satisfies]spdx-satisfies library, which is used by this action to check the licenses.
The copyright check is done by comparing the copyright in the SPDX header of the file with the approved copyrights defined in the configuration. The approved copyrights can be:
- Simple strings.
- Regular expressions.
You can define different copyrights in different rules, and even in the same rule. The action will check if the copyright in the file satisfies any of the approved copyrights.
Example of a configuration file enabling to use different copyrights (just for the sake of the example, because it could be also done by using a single regular expression):
rules:
- name: "Copyright"
headers:
- files:
- "**/*.ts"
- "**/*.js"
- "**/*.yml"
copyright:
- "\\d{4}(\\s-\\s\\d{4})? Telefónica Innovación Digital and contributors"
- "\\d{4}(\\s-\\s\\d{4})? Telefónica Innovación Digital"
- "\\d{4}(\\s-\\s\\d{4})? Telefónica"
The action returns the following outputs:
valid
: A boolean value indicating if all files have the correct headers according to the configuration. It will betrue
if all files have the correct headers, andfalse
otherwise.report
: A report containing details about the result of the headers check. The report can be returned in different formats, that can be defined by using thereporter
configuration property. The possible values are:text
: Generates a text report. This is the default reporter.markdown
: Generates a markdown report. This is very useful if you want to send the results to a GitHub comment in a PR, for example.json
: Generates a JSON report. This is useful if you want to process the results in a script, for example. Note that Github Actions outputs are always strings, so you will need to parse the JSON in your workflow. The JSON report contains all details about the headers check, including the files that have failed the check, details about the errors, the rule that produced them, etc.
File patterns are used to match the files that will be checked. They are defined using the glob pattern syntax.
The action uses the glob package to match the files. So, take into account next considerations:
- The glob patterns are matched using the default option
dot: false
. That means that the patterns will not match files or directories that start with a dot unless the pattern explicitly starts with a dot.
Important
You have to explicitly include the dot in the pattern if you want to match files or directories that start with a dot. For example, to match files in the ".github" directory, you have to use the pattern ".github/**"
.
Rules are very flexible and can be organized in different ways. You can define as many rules as you want, and each rule can contain as many set of headers and files as you need. Each headers check can enforce the license header, the copyright header, or both.
So, in general terms, you should organize the rules in order to have a clear and easy-to-maintain configuration, and also a clear and easy-to-understand report about the results. Anyway, headers will be enforced no matter how you organize the rules, so this is only a suggestion.
Here you have a pair of examples about how to organize the rules. Note that both examples will enforce the same headers, but they are organized differently:
- By file type: You can define one different rule for each type of file in your repository. For example, one rule for the source code files, another rule for the test files and another one for the configuration files.
rules: - name: "Source code" headers: - files: - "src/**/*.js" - "src/**/*.ts" license: "MPL-2.0" ignore: - "**/*.config.js" - files: - "utils/**/*.js" license: "Apache-2.0" ignore: - "utils/file-to-ignore.js" - name: "Tests" headers: - files: - "**/*.test.js" - "**/*.spec.js" license: "MPL-2.0" ignore: - "test/fixtures/**" - name: "Config files" headers: - files: - "**/*.yml" - "*.config.js" license: - "MIT" - "Apache-2.0" - name: "Copyright" headers: - files: - "**/*.ts" - "**/*.js" - "**/*.yml" copyright: "My Company"
- By license: You can define one rule for each license that you want to enforce in your repository. For example, one rule for the MPL-2.0 license, another rule for the Apache-2.0 license, and another one for files that can have the MIT or the Apache-2.0 license.
rules: - name: "MPL-2.0" headers: - files: - "src/**/*.js" - "src/**/*.ts" license: "MPL-2.0" ignore: - "**/*.config.js" - files: - "test/*.test.js" - "test/*.spec.js" license: "MPL-2.0" ignore: - "test/fixtures/**" - name: "Apache-2.0" headers: - files: - "utils/**/*.js" license: "Apache-2.0" ignore: - "utils/file-to-ignore.js" - name: "Apache-2.0 or MIT" headers: - files: - "**/*.yml" - "**/*.config.js" license: - "MIT" - "Apache-2.0" - name: "Copyright" headers: - files: - "**/*.ts" - "**/*.js" - "**/*.yml" copyright: "My Company"
Tip
Note that in both examples the configuration for the Copyright header is the same for the sake of simplicity, but the same mechanism can be applied to the Copyright header. You could combine it with the License header in the existing rules or define different rules for it.
Files can be ignored in three different levels:
- In the headers check of a specific rule: Then the files will be ignored only in that check.
- In a specific rule: Then the files will be ignored in all checks of that rule.
- In the global configuration: Then the files will be ignored in all checks of all rules.
Important
The ignore patterns are merged. That means that if you define an ignore pattern in the global configuration, it will be added to all rules, no matter if you define it also in a specific rule or in a specific headers check. The result will be the union of all ignore patterns.
Ignore patterns are added in the following order:
- Ignore patterns in the global configuration.
- Ignore patterns in a specific rule.
- Ignore patterns in a specific headers check.
Please read our Contributing Guidelines for details on how to contribute to this project before submitting a pull request.
This project is licensed under the Apache-2.0 License - see the LICENSE file for details. Read the Apache-2.0 FAQ at https://www.apache.org/foundation/license-faq.html