When changing the licence for OneGeo Suite, it is important to review dependency compatibility with the Affero GPL. Given the implications, is it also possible to ensure that our dependencies still have a compliant licence?
Licence compatibility
Licence compatibility includes two key elements to consider:
- The project itself (e.g., a Python module, a function library, etc.);
- A derivative work (e.g., a complete piece of software such as Firefox). Derivative works of a project may be modifications to the project’s code or products that incorporate the project.
Licence compatibility is possible when the derivative work has a licence that does not violate the rules of the project’s licence.
“Copyleft” licences require derivative works to be redistributed under the same licence (or a compatible one) to protect the freedoms of the code and the user. Conversely, permissive licences allow relicensing, and it is also possible to create a proprietary product by integrating components under this type of licence. For example, Sony bases the PlayStation’s proprietary operating system on FreeBSD, which is free software under the FreeBSD licence.
In this diagram, public-domain code can be integrated into an MIT-licensed product, which can itself be integrated into a BSD-licensed product, which can itself be integrated into an Apache 2.0-licensed product, and so on up to the Affero GPL 3 licence.
Two additional categories are introduced by this diagram:
- Weakly protective: it implies that a modification to the code must be under the same licence (and therefore remain free). However, the project can be integrated into a product under another licence (even proprietary).
- Network Protective: the GPL protects users of the machine. In client-server applications, the server side protects the server user, i.e., the system administrator. GPL compliance exists when the OneGeo Suite system administrator has access to the modified source code of the product, but the user of the client side does not. Network Protective licences also make it possible to protect the freedom of client users.
Pay close attention to licence versions: for example, the Apache 2.0 licence is compatible with the LGPL licence, whereas version 1.1 is not.
To guide you, here are a few incompatibilities worth highlighting:
- Apache 1.1 or MPL 1.1 (Mozilla Public Licence) are not compatible with GPL licences, whereas an explicit compatibility clause exists in version 2.0 of those same licences;
- CC-BY-3.0 prevents changing the licence, so it cannot be changed to AGPL. Version 4.0 explicitly allows changing the licence to GPL 3.0 and therefore to AGPL 3.0;
- Original BSD licence: it required including a copyright notice in the documentation of the final product, which can be problematic when a product contains many components under this licence.
Check compliance
In practice, licence compliance can be checked using DevOps.
To illustrate our projects, mainly in Python and JavaScript, we present two simple tools that can help you. In these examples, we will use them from the command line to compare dependency licences against an approved list of licences.
Checking licences in Python
The “pylic” tool analyses all Python modules installed in the virtual environment and compares their licences with a section of pyproject.toml. If it finds a module with an unapproved licence, it exits with an error that can be used in CI.
Let us start by installing the program:
$ pip install pylic
Next, we tell “pylic” which licences are compatible with our software by providing it with a list of AGPL 3.0-compatible licences in the tool.pylic section of our pyproject.toml:
$ cat << EOF >> pyproject.toml
[tool.pylic]
safe_licenses = [
"Apache Software License",
"BSD License",
"BSD",
"MIT License",
"MIT",
"Mozilla Public License 2.0 (MPL 2.0)",
"GNU Library or Lesser General Public License (LGPL)",
"GNU Lesser General Public License v3 or later (LGPLv3+)",
"Python Software Foundation License",
"Historical Permission Notice and Disclaimer (HPND)"
]
EOF
You will notice that these licences sometimes have very similar names. Indeed, “pylic” relies on the name declared by the Python module maintainer (in setup.cfg/setup.py, etc.), which is not standardised. We must declare both identifiers “MIT” and “MIT License” as “safe”, whereas we could have used SPDX licence identifiers in the Python module configuration.
Let us go back to “pylic” and run a check:
$ pylic check
✨ All licenses ok ✨
What can be done in the event of non-compliance?
By commenting out the “Apache Software License” entry in pyproject.toml, we get this message:
$ pylic check
Found unsafe licenses:
nltk (3.8.1): Apache Software License
phonenumbers (8.13.8): Apache Software License
importlib-metadata (6.1.0): Apache Software License
bleach (6.0.0): Apache Software License
cryptography (42.0.1): Apache Software License
regex (2023.12.25): Apache Software License
django-onegeo-suite (1.0.2): Apache Software License
requests (2.31.0): Apache Software License
packaging (23.2): Apache Software License
pyOpenSSL (24.0.0): Apache Software License
tzdata (2023.4): Apache Software License
elasticsearch (7.17.9): Apache Software License
async-timeout (4.0.3): Apache Software License
josepy (1.14.0): Apache Software License
django-onegeo-rproxy-mapstore2 (1.0.0b2): Apache Software License
Quite simple, is it not?
Checking licences in JavaScript
Similarly, you can check the licences of JavaScript projects with license-checker. ItIt is still not aware of existing licences, so you will need to build the list manually.
Usage is fairly straightforward:
$ npx license-checker --onlyAllow "CC-BY-4.0;ISC;Apache-2.0;BSD-3-Clause;Custom: https://github.com/tmcw/jsonlint;BSD-2-Clause;Unlicense;UNLICENSED;BSD;Public Domain;CC0-1.0;MPL-2.0" --production
Package “@fortawesome/fontawesome-common-types@0.2.36” is licensed under “MIT” which is not permitted by the –onlyAllow flag. Exiting.
You get errors for each unauthorised licence.
The “–onlyAllow” option lists the allowed licences separated by semicolons (“;”). The “–production” option excludes licences for modules in the “devDependencies” section of package.json.
Also remember to correct the “license” section of package.json so as not to confuse the tool:
"license": "AGPL-3.0-only",
I also recommend adding a script to package.json to automate the checks:
cat package.json
{
[...]
"scripts": {
[...] "lint": "vue-cli-service lint",
"license-checker": "license-checker --onlyAllow \"MIT;CC-BY-4.0;ISC;Apache-2.0;BSD-3-Clause;Custom: https://github.com/tmcw/jsonlint;BSD-2-Clause;Unlicense;UNLICENSED;BSD;Public Domain;CC0-1.0;MPL-2.0\" --production"
},
You can then run simply:
$ npm run license-checker
Conclusion
There are probably more sophisticated tools for checking the licence compatibility of external libraries used by your project, notably by using the OSADL licence compatibility matrix: https://www.osadl.org/html/CompatMatrix.html
However, for a lightweight and quick use in CI, these two tools will do the job, while still investing a bit of time to review any new licences that may appear over the lifetime of your project.
Sources:
https://janelia-flyem.github.io/licenses.html: compatibility image
https://www.gnu.org/licenses/license-list.html: information on GPL compatibility
https://www.apache.org/licenses/GPL-compatibility.html: Apache 2.0 and GPL compatibility
https://medium.com/@iharmandeep/easily-check-licenses-of-your-npm-dependencies-in-your-ci-pipeline-9102150d6907: using license-checker
Author: Sébastien DA ROCHA