-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add license validation for repositories #5343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
65c71a5
Add OSI-approved license validator for HACS action
claude 24b7110
Trim SPDX license list test fixture to minimum
claude b0318e6
Use proxy fixture instead of inline SPDX payload in license tests
claude 2a0d92e
Apply suggestions from code review
ludeeus 2ad6253
Short-circuit license validation before SPDX list download
claude fe5e2eb
Merge branch 'main' into claude/osi-license-validation-5tnyi2
ludeeus 76b6347
Merge branch 'main' into claude/osi-license-validation-5tnyi2
ludeeus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| from ..utils.json import json_loads | ||
| from .base import ActionValidationBase, ValidationException | ||
|
|
||
| if TYPE_CHECKING: | ||
| from ..repositories.base import HacsRepository | ||
|
|
||
| # renovate: datasource=github-tags depName=spdx/license-list-data | ||
| SPDX_LICENSE_LIST_COMMIT = "c4a7237ec8f4654e867546f9f409749300f1bf4c" # v3.28.0 | ||
|
|
||
| SPDX_LICENSE_LIST_URL = ( | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/" | ||
| f"{SPDX_LICENSE_LIST_COMMIT}/json/licenses.json" | ||
| ) | ||
|
|
||
|
|
||
| async def async_setup_validator(repository: HacsRepository) -> Validator: | ||
| """Set up this validator.""" | ||
| return Validator(repository=repository) | ||
|
|
||
|
|
||
| class Validator(ActionValidationBase): | ||
| """Validate the repository.""" | ||
|
|
||
| more_info = "https://hacs.xyz/docs/publish/include#check-license" | ||
| allow_fork = False | ||
|
|
||
| async def async_validate(self) -> None: | ||
| """Validate the repository.""" | ||
| if (license_info := self.repository.repository_object.attributes.get("license")) is None: | ||
| raise ValidationException("The repository has no license") | ||
|
|
||
| result = await self.hacs.async_download_file(SPDX_LICENSE_LIST_URL, handle_rate_limit=True) | ||
| if result is None: | ||
| raise ValidationException("Could not fetch the SPDX license list") | ||
|
ludeeus marked this conversation as resolved.
|
||
|
|
||
| osi_approved = { | ||
| entry["licenseId"] | ||
| for entry in json_loads(result)["licenses"] | ||
| if entry.get("isOsiApproved") | ||
| } | ||
|
ludeeus marked this conversation as resolved.
|
||
|
|
||
| spdx_id = license_info.get("spdx_id") | ||
| if not spdx_id or spdx_id == "NOASSERTION" or spdx_id not in osi_approved: | ||
| raise ValidationException( | ||
| f"The repository does not have an OSI-approved license (detected: '{spdx_id}')" | ||
| ) | ||
|
ludeeus marked this conversation as resolved.
Outdated
|
||
|
|
||
| self.repository.logger.debug( | ||
| "The repository has an OSI-approved license: %s", license_info.get("name") | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
...nt.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "licenseListVersion": "3.28.0", | ||
| "licenses": [ | ||
| { | ||
| "licenseId": "CC0-1.0", | ||
| "name": "Creative Commons Zero v1.0 Universal", | ||
| "isDeprecatedLicenseId": false, | ||
| "isOsiApproved": false | ||
| }, | ||
| { | ||
| "licenseId": "GPL-3.0", | ||
| "name": "GNU General Public License v3.0 only", | ||
| "isDeprecatedLicenseId": true, | ||
| "isOsiApproved": true | ||
| }, | ||
| { | ||
| "licenseId": "MIT", | ||
| "name": "MIT License", | ||
| "isDeprecatedLicenseId": false, | ||
| "isOsiApproved": true | ||
| } | ||
| ], | ||
| "releaseDate": "2025-05-30" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
tests/snapshots/api-usage/tests/validate/test_licensetest-repository-missing-spdx-id.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_repository_missing_spdx_id": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1, | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json": 1 | ||
| } | ||
| } |
9 changes: 9 additions & 0 deletions
9
tests/snapshots/api-usage/tests/validate/test_licensetest-repository-no-license.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_repository_no_license": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1 | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
tests/snapshots/api-usage/tests/validate/test_licensetest-repository-non-osi-license.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_repository_non_osi_license": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1, | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json": 1 | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
...ts/api-usage/tests/validate/test_licensetest-repository-osi-approved-license-gpl-3-0.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_repository_osi_approved_license[GPL-3.0]": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1, | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json": 1 | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
...pshots/api-usage/tests/validate/test_licensetest-repository-osi-approved-license-mit.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_repository_osi_approved_license[MIT]": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1, | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json": 1 | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
.../snapshots/api-usage/tests/validate/test_licensetest-repository-unrecognized-license.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_repository_unrecognized_license": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1, | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json": 1 | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
.../snapshots/api-usage/tests/validate/test_licensetest-spdx-license-list-fetch-failure.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tests/validate/test_license.py::test_spdx_license_list_fetch_failure": { | ||
| "https://api.github.com/repos/hacs/integration": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/contents/hacs.json": 1, | ||
| "https://api.github.com/repos/hacs/integration/git/trees/main": 1, | ||
| "https://api.github.com/repos/hacs/integration/releases": 1, | ||
| "https://raw.githubusercontent.com/spdx/license-list-data/c4a7237ec8f4654e867546f9f409749300f1bf4c/json/licenses.json": 1 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| from unittest.mock import MagicMock | ||
|
|
||
| import pytest | ||
|
|
||
| from custom_components.hacs.validate.license import SPDX_LICENSE_LIST_URL, Validator | ||
|
|
||
| from tests.common import MockedResponse, ResponseMocker | ||
|
|
||
|
|
||
| async def test_repository_no_license(repository): | ||
| repository.repository_object = MagicMock() | ||
| repository.repository_object.attributes = {"license": None} | ||
| check = Validator(repository) | ||
| await check.execute_validation() | ||
| assert check.failed | ||
|
|
||
|
|
||
| async def test_repository_unrecognized_license(repository): | ||
| repository.repository_object = MagicMock() | ||
| repository.repository_object.attributes = { | ||
| "license": {"key": "other", "name": "Other", "spdx_id": "NOASSERTION"}, | ||
| } | ||
| check = Validator(repository) | ||
| await check.execute_validation() | ||
| assert check.failed | ||
|
|
||
|
|
||
| async def test_repository_missing_spdx_id(repository): | ||
| repository.repository_object = MagicMock() | ||
| repository.repository_object.attributes = { | ||
| "license": {"key": "other", "name": "Other"}, | ||
| } | ||
| check = Validator(repository) | ||
| await check.execute_validation() | ||
| assert check.failed | ||
|
|
||
|
|
||
| async def test_repository_non_osi_license(repository): | ||
| repository.repository_object = MagicMock() | ||
| repository.repository_object.attributes = { | ||
| "license": { | ||
| "key": "cc0-1.0", | ||
| "name": "Creative Commons Zero v1.0 Universal", | ||
| "spdx_id": "CC0-1.0", | ||
| }, | ||
| } | ||
| check = Validator(repository) | ||
| await check.execute_validation() | ||
| assert check.failed | ||
|
|
||
|
|
||
| async def test_spdx_license_list_fetch_failure(repository, response_mocker: ResponseMocker): | ||
| response_mocker.add(SPDX_LICENSE_LIST_URL, MockedResponse(status=500)) | ||
| repository.repository_object = MagicMock() | ||
| repository.repository_object.attributes = { | ||
| "license": {"key": "mit", "name": "MIT License", "spdx_id": "MIT"}, | ||
| } | ||
| check = Validator(repository) | ||
| await check.execute_validation() | ||
| assert check.failed | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("spdx_id", ["MIT", "GPL-3.0"]) | ||
| async def test_repository_osi_approved_license(repository, spdx_id): | ||
| repository.repository_object = MagicMock() | ||
| repository.repository_object.attributes = { | ||
| "license": { | ||
| "key": spdx_id.lower(), | ||
| "name": f"License {spdx_id}", | ||
| "spdx_id": spdx_id, | ||
| }, | ||
| } | ||
| check = Validator(repository) | ||
| await check.execute_validation() | ||
| assert not check.failed |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this cached?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not currently, but we can store a set of IDs.