From 5421cbdaba2635cb617f6b5d2de880e53a93b1b7 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 30 Sep 2026 10:51:04 -0500 Subject: [PATCH 1/3] ci: pin settings-safe QQQ orb 0.6.8 Use reviewed release tooling for QRun-IO/qqq-all#10; keep release versions unchanged. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 89ea8fb..2ca7723 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - qqq-orb: kingsrook/qqq-orb@0.5.0 + qqq-orb: kingsrook/qqq-orb@0.6.8 commands: configure-package-install: -- 2.54.0 From 8ed5d6b2efb8f37c0ce586dd2873180dbe681a5d Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 30 Sep 2026 20:17:46 -0500 Subject: [PATCH 2/3] fix: align first-party Apache-2.0 declarations Refs QRun-IO/qqq#923. --- CLAUDE.md | 5 +++-- README.md | 2 +- pom.xml | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e24dc79..958bb95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,5 +15,6 @@ Key orientation facts (details and caveats in the dossier): README's `io.qrun:qbit-bom` coordinates do not exist on Maven Central. - v1.6.0 imports `com.kingsrook.qqq:qqq-bom-pom:0.40.0` (Java 21, junit-bom 6.0.1); develop pins `0.41.0-SNAPSHOT`, which no longer resolves (qqq renumbered to 4.0.x). -- Known licensing inconsistency: LICENSE/NOTICE = Apache-2.0, pom `` = AGPL-3.0 - (shipped to Central in 1.6.0), README says Proprietary. See dossier "Maturity & risks". +- First-party license declarations in the current source are Apache-2.0, consistent + with LICENSE and NOTICE. Previously published artifacts retain their original + metadata; publish a new version for declaration corrections. diff --git a/README.md b/README.md index e36cff2..003dc49 100644 --- a/README.md +++ b/README.md @@ -34,4 +34,4 @@ The parent can be published only after its imported final QQQ BOM is available. ## License -See [LICENSE](LICENSE) and [NOTICE](NOTICE). +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE). diff --git a/pom.xml b/pom.xml index a4b566c..ae78997 100644 --- a/pom.xml +++ b/pom.xml @@ -15,8 +15,8 @@ https://github.com/Kingsrook/qbit-bom - GNU Affero General Public License v3.0 - https://www.gnu.org/licenses/agpl-3.0-standalone.html + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 repo -- 2.54.0 From 33ecfdc07766fab16c8a7532d46b2cd341638927 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 30 Sep 2026 23:40:11 -0500 Subject: [PATCH 3/3] fix(#944): package consumer project notices --- README.md | 28 ++++++++++++++++++ pom.xml | 48 +++++++++++++++++++++++++++++++ scripts/check-packaged-notices.py | 39 +++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 scripts/check-packaged-notices.py diff --git a/README.md b/README.md index 003dc49..648265c 100644 --- a/README.md +++ b/README.md @@ -34,4 +34,32 @@ The parent can be published only after its imported final QQQ BOM is available. ## License +### Packaged notices + +The packaging change in this source tree copies each consumer's own root +`LICENSE` and `NOTICE` into `META-INF` in main/test archives and Maven shared +archive resources used by source/Javadoc archives. Existing main/test resources +remain included. Consumers must adopt a newly published parent version to receive +this change; the existing public 2.0.0 artifact is unchanged. + +The default `qbit.noticesDirectory` is `${project.basedir}`. In a nested module +whose files live at the repository root, set it to `${project.basedir}/..` (adjust +for the actual layout). Do not point it at the build-parent checkout: these are +the consuming project's notices. If a child overrides `` or +``, retain the inherited shared archive resource directory. + +After packaging the consumer, explicitly check every produced archive using +Python 3 and the guard from this checkout: + +```bash +mvn -Prelease -DskipTests -Dgpg.skip=true clean package +python3 /path/to/qbit-bom/scripts/check-packaged-notices.py --root . target/*.jar +``` + +For a multi-module consumer, pass each module's actual JAR paths and its repository +root. The guard rejects missing archives, missing/duplicate entries and changed +notice bytes. Packaging-only checks skip runtime tests; existing tests, review +and release requirements still apply. The publishing workflow does not invoke +this guard automatically. + Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE). diff --git a/pom.xml b/pom.xml index ae78997..f64c987 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,8 @@ 21 UTF-8 UTF-8 + + ${project.basedir} 2.0.0 @@ -69,6 +71,52 @@ + + + src/main/resources + + + ${project.build.directory}/maven-shared-archive-resources + + + + + src/test/resources + + + ${project.build.directory}/maven-shared-archive-resources + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.4.0 + + + package-root-notices + generate-resources + + copy-resources + + + ${project.build.directory}/maven-shared-archive-resources/META-INF + + + ${qbit.noticesDirectory} + false + + LICENSE + NOTICE + + + + + + + + diff --git a/scripts/check-packaged-notices.py b/scripts/check-packaged-notices.py new file mode 100644 index 0000000..e583d31 --- /dev/null +++ b/scripts/check-packaged-notices.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""Verify exact project notices in explicitly listed Maven archives.""" + +import argparse +from pathlib import Path +import sys +from zipfile import BadZipFile, ZipFile + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True, + help="consumer repository containing LICENSE and NOTICE") + parser.add_argument("--prefix", default="META-INF", + help="directory containing the project's archive copies") + parser.add_argument("archives", type=Path, nargs="+") + args = parser.parse_args() + expected = {name: (args.root / name).read_bytes() for name in ("LICENSE", "NOTICE")} + failures = [] + for archive in args.archives: + try: + with ZipFile(archive) as jar: + for name, content in expected.items(): + entry = f"{args.prefix.rstrip('/')}/{name}" + if jar.namelist().count(entry) != 1: + failures.append(f"{archive}: expected exactly one {entry}") + elif jar.read(entry) != content: + failures.append(f"{archive}: {entry} differs from root {name}") + except (OSError, BadZipFile) as error: + failures.append(f"{archive}: {error}") + if failures: + print("\n".join(failures), file=sys.stderr) + return 1 + print(f"PASS: exact LICENSE/NOTICE in {len(args.archives)} archives") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) -- 2.54.0