diff options
Diffstat
31 files changed, 40 insertions, 616 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2e427c..4783219 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD name: CI @@ -35,7 +35,6 @@ jobs: - name: Tests run: | - tests/licenses-json.py tests/oci-tasks.py tests/package.py diff --git a/.github/workflows/legal.yml b/.github/workflows/legal.yml index 2886f52..b08329a 100644 --- a/.github/workflows/legal.yml +++ b/.github/workflows/legal.yml @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD name: Legal diff --git a/.gitignore b/.gitignore index 605a430..a56ba84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD __pycache__/ *.py[cod] diff --git a/LICENSE b/LICENSE index d645695..7d70db4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,12 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Copyright (C) 2026 Nikolay Govorov + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/LICENSES/0BSD.txt b/LICENSES/0BSD.txt new file mode 120000 --- /dev/null +++ b/LICENSES/0BSD.txt @@ -0,0 +1 @@ +../LICENSE
\ No newline at end of file diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt deleted file mode 120000 --- a/LICENSES/Apache-2.0.txt +++ /dev/null @@ -1 +0,0 @@ -../LICENSE
\ No newline at end of file diff --git a/README.md b/README.md index 941b3fd..057d394 100644 --- a/README.md +++ b/README.md @@ -123,22 +123,6 @@ that cannot be installed as portable tools belong in `[bootstrap.packages]`. licensing metadata and canonical SPDX copyright headers. In Rust projects it also runs a pinned `cargo deny check`. -The separate `licenses-json` task uses cargo-about to generate a deterministic, -embeddable JSON bundle for a Rust binary. It accepts every license declared by -the dependency graph because policy enforcement remains the responsibility of -`cargo deny`. Repeat `--target` to produce one bundle for all supported targets: - -```console -mise run licenses-json -- \ - --manifest-path crates/server/Cargo.toml \ - --output crates/server/licenses.json \ - --target x86_64-unknown-linux-gnu \ - --target aarch64-unknown-linux-gnu -``` - -Use `--check` with the same arguments in CI to verify that a committed bundle is -up to date, or `--offline` when all dependency sources are already cached. - ### Sign-off policy `tasks/signoff.py` verifies that: @@ -194,5 +178,5 @@ Remember, AI agents should make software better, not worse. ## Licensing -Unless noted otherwise, software and configuration are licensed under Apache-2.0. +Unless noted otherwise, software and configuration are licensed under 0BSD. Documentation is licensed under CC-BY-4.0. diff --git a/REUSE.toml b/REUSE.toml index f29e237..e4b6255 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD version = 1 @@ -23,4 +23,4 @@ path = [ "mise.lock", ] SPDX-FileCopyrightText = "2026 Nikolay Govorov" -SPDX-License-Identifier = "Apache-2.0" +SPDX-License-Identifier = "0BSD" diff --git a/config/cla-unsupported-commits b/config/cla-unsupported-commits index 726ab62..3b38c1c 100644 --- a/config/cla-unsupported-commits +++ b/config/cla-unsupported-commits @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # # External contributions retained in history but not covered by a versioned CLA. diff --git a/config/signoff-approved-emails b/config/signoff-approved-emails index dd67622..234b671 100644 --- a/config/signoff-approved-emails +++ b/config/signoff-approved-emails @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # # One exact author or committer email per line. diff --git a/mise.toml b/mise.toml index b41041f..449df01 100644 --- a/mise.toml +++ b/mise.toml @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD min_version = "2026.7.5" diff --git a/tasks/chart.py b/tasks/chart.py index 622ea1e..f7333a8 100755 --- a/tasks/chart.py +++ b/tasks/chart.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # fmt: off #MISE description="Lint, package, and optionally publish a Helm chart" #MISE tools={"pipx"="1.16.7","python"="3.14.7","helm"="4.1.1"} diff --git a/tasks/container.py b/tasks/container.py index ac6595c..c68482b 100755 --- a/tasks/container.py +++ b/tasks/container.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # fmt: off #MISE description="Build and optionally publish an OCI container image" #MISE tools={"pipx"="1.16.7","python"="3.14.7"} diff --git a/tasks/libs/__init__.py b/tasks/libs/__init__.py index 2c09e13..951b296 100644 --- a/tasks/libs/__init__.py +++ b/tasks/libs/__init__.py @@ -1,2 +1,2 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD diff --git a/tasks/libs/apk.py b/tasks/libs/apk.py index c1b1d3c..ae2efc3 100644 --- a/tasks/libs/apk.py +++ b/tasks/libs/apk.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tasks/libs/apt.py b/tasks/libs/apt.py index b79d378..8952900 100644 --- a/tasks/libs/apt.py +++ b/tasks/libs/apt.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tasks/libs/common.py b/tasks/libs/common.py index dcf5658..e1373e6 100644 --- a/tasks/libs/common.py +++ b/tasks/libs/common.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tasks/libs/repository.py b/tasks/libs/repository.py index 51b035a..772e1ab 100644 --- a/tasks/libs/repository.py +++ b/tasks/libs/repository.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tasks/libs/rpm.py b/tasks/libs/rpm.py index 580b949..390446e 100644 --- a/tasks/libs/rpm.py +++ b/tasks/libs/rpm.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tasks/libs/storage.py b/tasks/libs/storage.py index e0bfddf..7bd89d2 100644 --- a/tasks/libs/storage.py +++ b/tasks/libs/storage.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tasks/licenses-json.py b/tasks/licenses-json.py deleted file mode 100755 --- a/tasks/licenses-json.py +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env -S pipx run --backend pip -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 -# fmt: off -#MISE description="Generate embeddable Rust dependency license JSON" -#MISE tools={"pipx"="1.16.7","python"="3.14.7","cargo:cargo-about"="0.8.4"} -# fmt: on -# /// script -# requires-python = ">=3.11" -# dependencies = ["shellous==0.42.0"] -# /// - -from __future__ import annotations - -import argparse -import json -import re -import sys -import tempfile -from collections.abc import Sequence -from pathlib import Path -from typing import Any - -sys.dont_write_bytecode = True - -from libs.common import TaskError, capture, require_command, run, task_main - -TASK = "licenses-json" -# 0.8.4 intentionally matches the license-file deduplication behavior of the -# legacy in-tree generator. Keep the task tool pin and normalizer in sync. -SPDX_TOKEN = re.compile(r"[A-Za-z0-9][A-Za-z0-9.+:-]*|[()]") -SPDX_OPERATORS = {"AND", "OR", "WITH"} - - -def license_requirements(expression: str) -> list[str]: - """Return SPDX requirements in expression order, matching the legacy generator.""" - normalized = expression.replace("/", " OR ") - tokens = SPDX_TOKEN.findall(normalized) - requirements: list[str] = [] - - index = 0 - while index < len(tokens): - token = tokens[index] - if token in {"(", ")", "AND", "OR"}: - index += 1 - continue - if token == "WITH": - raise TaskError(f"{TASK}: invalid SPDX expression: {expression}") - - requirement = token - if index + 1 < len(tokens) and tokens[index + 1] == "WITH": - if index + 2 >= len(tokens) or tokens[index + 2] in SPDX_OPERATORS | { - "(", - ")", - }: - raise TaskError(f"{TASK}: invalid SPDX expression: {expression}") - requirement = f"{token} WITH {tokens[index + 2]}" - index += 2 - - if requirement not in requirements: - requirements.append(requirement) - index += 1 - - return requirements - - -def cargo_about_config(metadata: dict[str, Any]) -> str: - accepted_by_crate: dict[str, list[str]] = {} - for package in metadata.get("packages", []): - expression = package.get("license") - if not expression: - continue - - accepted = accepted_by_crate.setdefault(package["name"], []) - for requirement in license_requirements(expression): - if requirement not in accepted: - accepted.append(requirement) - - lines = [ - "accepted = []", - "private = { ignore = true }", - "ignore-build-dependencies = true", - "ignore-dev-dependencies = true", - "ignore-transitive-dependencies = false", - ] - for name, accepted in sorted(accepted_by_crate.items()): - lines.extend( - ("", f"[{json.dumps(name)}]", f"accepted = {json.dumps(accepted)}") - ) - - return "\n".join(lines) + "\n" - - -def normalized_output(report: dict[str, Any]) -> str: - licenses: list[dict[str, Any]] = [] - for source in report.get("licenses", []): - used_by = [ - { - "crate": { - "name": usage["crate"]["name"], - "version": usage["crate"]["version"], - "repository": usage["crate"].get("repository"), - } - } - for usage in source["used_by"] - ] - used_by.sort(key=lambda usage: len(usage["crate"]["name"])) - licenses.append( - { - "name": source["name"], - "id": source["id"], - "first_of_kind": False, - "text": source["text"], - "used_by": used_by, - } - ) - - licenses.sort(key=lambda license_: license_["id"]) - - overview_by_id: dict[str, dict[str, Any]] = {} - for license_ in licenses: - first = license_["id"] not in overview_by_id - license_["first_of_kind"] = first - overview = overview_by_id.setdefault( - license_["id"], - { - "count": 0, - "name": license_["name"], - "id": license_["id"], - }, - ) - overview["count"] += len(license_["used_by"]) - - overview = sorted(overview_by_id.values(), key=lambda item: item["name"]) - output = {"overview": overview, "licenses": licenses} - return json.dumps(output, ensure_ascii=False, indent=2) + "\n" - - -async def cargo_metadata(manifest_path: Path, offline: bool) -> dict[str, Any]: - arguments: list[str | Path] = [ - "cargo", - "metadata", - "--format-version", - "1", - "--locked", - "--manifest-path", - manifest_path, - ] - if offline: - arguments.append("--offline") - - output = await capture(arguments) - try: - return json.loads(output) - except json.JSONDecodeError as error: - raise TaskError( - f"{TASK}: cargo metadata returned invalid JSON: {error}" - ) from error - - -async def generate( - manifest_path: Path, - targets: Sequence[str], - offline: bool, -) -> str: - metadata = await cargo_metadata(manifest_path, offline) - - with tempfile.TemporaryDirectory(prefix=f"{TASK}-") as temporary: - work = Path(temporary) - config = work / "about.toml" - report = work / "report.json" - config.write_text(cargo_about_config(metadata), encoding="utf-8") - - arguments: list[str | Path] = [ - "cargo-about", - "generate", - "--config", - config, - "--manifest-path", - manifest_path, - "--format", - "json", - "--locked", - "--output-file", - report, - ] - if offline: - arguments.append("--offline") - for target in targets: - arguments.extend(("--target", target)) - - quiet_arguments = ["cargo-about", "-L", "off", *arguments[1:]] - result = await capture.result(quiet_arguments) - if result.exit_code != 0: - # Repeat with diagnostics enabled only on failure. cargo-about 0.8.4 - # otherwise reports harmless scanner errors for deprecated SPDX IDs. - await run(arguments) - try: - raw_report = json.loads(report.read_text(encoding="utf-8")) - except (OSError, json.JSONDecodeError) as error: - raise TaskError( - f"{TASK}: cargo-about returned invalid JSON: {error}" - ) from error - - return normalized_output(raw_report) - - -async def main(args: Sequence[str]) -> None: - command = argparse.ArgumentParser(prog="mise run licenses-json --") - command.add_argument( - "--manifest-path", - default=Path("Cargo.toml"), - type=Path, - ) - command.add_argument("--output", required=True, type=Path) - command.add_argument("--target", action="append", required=True) - command.add_argument("--offline", action="store_true") - command.add_argument("--check", action="store_true") - arguments = command.parse_args(args) - - require_command("cargo", TASK) - require_command("cargo-about", TASK) - if not arguments.manifest_path.is_file(): - raise TaskError(f"{TASK}: manifest not found: {arguments.manifest_path}") - - output = await generate( - arguments.manifest_path.resolve(), - arguments.target, - arguments.offline, - ) - if arguments.check: - if not arguments.output.is_file(): - raise TaskError(f"{TASK}: output not found: {arguments.output}") - if arguments.output.read_text(encoding="utf-8") != output: - raise TaskError( - f"{TASK}: {arguments.output} is out of date; regenerate it without --check" - ) - return - - arguments.output.parent.mkdir(parents=True, exist_ok=True) - if ( - not arguments.output.is_file() - or arguments.output.read_text(encoding="utf-8") != output - ): - arguments.output.write_text(output, encoding="utf-8") - - -if __name__ == "__main__": - task_main(TASK, main, sys.argv[1:]) diff --git a/tasks/licenses.py b/tasks/licenses.py index f8a4bcc..18504a2 100755 --- a/tasks/licenses.py +++ b/tasks/licenses.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # fmt: off #MISE description="Verify repository licensing metadata" #MISE tools={"pipx"="1.16.7","python"="3.14.7","pipx:reuse"="6.2.0","aqua:EmbarkStudios/cargo-deny"="0.19.0"} diff --git a/tasks/package.py b/tasks/package.py index 184b3f2..9856b26 100755 --- a/tasks/package.py +++ b/tasks/package.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # fmt: off #MISE description="Build release archives and signed Linux packages" #MISE tools={"pipx"="1.16.7","python"="3.14.7","nfpm"="2.47.0"} diff --git a/tasks/publish.py b/tasks/publish.py index 0040720..75c5299 100755 --- a/tasks/publish.py +++ b/tasks/publish.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # fmt: off #MISE description="Publish signed package repositories to shared S3 storage" #MISE tools={"pipx"="1.16.7","python"="3.14.7"} diff --git a/tasks/signoff.py b/tasks/signoff.py index fd4e20e..a3d7e6a 100755 --- a/tasks/signoff.py +++ b/tasks/signoff.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # fmt: off #MISE description="Verify contributor identities and CLA acceptance trailers" #MISE tools={"pipx"="1.16.7","python"="3.14.7"} diff --git a/tests/fakes/boto3.py b/tests/fakes/boto3.py index 957d9a6..8ddbf86 100644 --- a/tests/fakes/boto3.py +++ b/tests/fakes/boto3.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD from __future__ import annotations diff --git a/tests/licenses-json.py b/tests/licenses-json.py deleted file mode 100755 --- a/tests/licenses-json.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env -S pipx run --backend pip -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 -# /// script -# requires-python = ">=3.11" -# dependencies = ["shellous==0.42.0"] -# /// - -from __future__ import annotations - -import importlib.util -import json -import sys -from pathlib import Path -from types import ModuleType - - -def load_task() -> ModuleType: - path = Path(__file__).parents[1] / "tasks/licenses-json.py" - sys.path.insert(0, str(path.parent)) - spec = importlib.util.spec_from_file_location("licenses_json", path) - assert spec is not None and spec.loader is not None - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module - - -def main() -> None: - task = load_task() - - assert task.license_requirements("MIT/Apache-2.0") == ["MIT", "Apache-2.0"] - assert task.license_requirements( - "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" - ) == ["Apache-2.0 WITH LLVM-exception", "Apache-2.0", "MIT"] - - config = task.cargo_about_config( - { - "packages": [ - {"name": "dual", "license": "MIT OR Apache-2.0"}, - {"name": "dual", "license": "MIT/Apache-2.0"}, - {"name": "private", "license": None}, - ] - } - ) - assert "accepted = []\nprivate = { ignore = true }" in config - assert '["dual"]\naccepted = ["MIT", "Apache-2.0"]' in config - assert '["private"]' not in config - - output = json.loads( - task.normalized_output( - { - "overview": [], - "licenses": [ - { - "name": "MIT License", - "id": "MIT", - "first_of_kind": True, - "source_path": "/tmp/LICENSE", - "text": "MIT text", - "used_by": [ - { - "crate": { - "name": "long-name", - "version": "2.0.0", - "repository": None, - "manifest_path": "/tmp/Cargo.toml", - }, - "path": None, - }, - { - "crate": { - "name": "short", - "version": "1.0.0", - "repository": "https://example.invalid/short", - }, - "path": None, - }, - ], - }, - { - "name": "Apache License 2.0", - "id": "Apache-2.0", - "first_of_kind": False, - "text": "Apache text", - "used_by": [ - { - "crate": { - "name": "dependency", - "version": "3.0.0", - "repository": None, - } - } - ], - }, - ], - "crates": [{"package": {"manifest_path": "/tmp/Cargo.toml"}}], - } - ) - ) - - assert output["overview"] == [ - {"count": 1, "name": "Apache License 2.0", "id": "Apache-2.0"}, - {"count": 2, "name": "MIT License", "id": "MIT"}, - ] - assert [license_["id"] for license_ in output["licenses"]] == [ - "Apache-2.0", - "MIT", - ] - assert all(license_["first_of_kind"] for license_ in output["licenses"]) - assert [usage["crate"]["name"] for usage in output["licenses"][1]["used_by"]] == [ - "short", - "long-name", - ] - assert "source_path" not in output["licenses"][1] - assert "manifest_path" not in output["licenses"][1]["used_by"][0]["crate"] - assert "crates" not in output - - -if __name__ == "__main__": - main() diff --git a/tests/oci-tasks.py b/tests/oci-tasks.py index 4bc2ebb..b236394 100755 --- a/tests/oci-tasks.py +++ b/tests/oci-tasks.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # /// script # requires-python = ">=3.11" # dependencies = ["shellous==0.42.0"] diff --git a/tests/package-integration.py b/tests/package-integration.py index 5a26a97..25212b5 100755 --- a/tests/package-integration.py +++ b/tests/package-integration.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # /// script # requires-python = ">=3.11" # dependencies = ["shellous==0.42.0"] diff --git a/tests/package.py b/tests/package.py index 4d2c9a2..5a2c756 100755 --- a/tests/package.py +++ b/tests/package.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # /// script # requires-python = ">=3.11" # dependencies = ["shellous==0.42.0"] diff --git a/tests/publish-integration.py b/tests/publish-integration.py index d59a0af..db2ee9c 100755 --- a/tests/publish-integration.py +++ b/tests/publish-integration.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S pipx run --backend pip # SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: 0BSD # /// script # requires-python = ">=3.11" # dependencies = ["shellous==0.42.0"] |
