This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new a9ed80e release: generate + commit binary LICENSE/NOTICE, drift-check
in CI
a9ed80e is described below
commit a9ed80ea437dcc48d82cacf44ab7f550b03cfcf1
Author: Wu Sheng <[email protected]>
AuthorDate: Thu May 21 08:32:00 2026 +0800
release: generate + commit binary LICENSE/NOTICE, drift-check in CI
The binary tarball's LICENSE/NOTICE were only ever generated into dist/
(gitignored) at package time, and the collector silently produced ZERO
packages — `pnpm list --prod` in dist/ returns nothing without a lockfile,
so the binary LICENSE listed no bundled dependencies at all. It also never
covered the UI deps Vite compiles into the static bundle.
- Rework collect-dist-licenses.mjs to enumerate the full production tree of
the bundled workspace packages (bff runtime + ui) straight from the pnpm
store — 231 third-party packages incl. monaco/echarts/vue/dompurify/d3.
Deterministic output (NOTICE year from the repo-root NOTICE; report has no
timestamp). Adds --update-reference and --check modes.
- Commit the generated binary LICENSE + NOTICE as the reviewed reference
under dist-material/release-docs/ (next to the .tpl sources).
- Add a human-reviewed license-overrides.json ([email protected]
ships an MIT LICENSE file but omits the package.json field).
- CI `dependency-license` now runs `pnpm licenses:bin:check` (regenerate +
diff against the committed reference + ASF allow/deny) instead of a full
`pnpm package`; release.sh verifies the same via --check before tarring.
- New scripts: `pnpm licenses:bin:update` / `licenses:bin:check`.
---
.github/workflows/ci.yaml | 37 +-
dist-material/release-docs/LICENSE | 474 ++++++++++++++++++++++
dist-material/release-docs/NOTICE | 18 +
dist-material/release-docs/license-overrides.json | 4 +
package.json | 2 +
scripts/collect-dist-licenses.mjs | 192 ++++++---
scripts/release.sh | 7 +-
7 files changed, 655 insertions(+), 79 deletions(-)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 78a7659..d7f22a4 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -39,15 +39,18 @@ jobs:
uses: apache/skywalking-eyes@5b7ee1731d036b5aac68f8bd3fc9e6f98ada082e
dependency-license:
- # Separate from `license-header` on purpose: header check is a quick
- # static scan; dep-license collection requires a full production build
- # (pnpm package → dist/node_modules) and then a walk of that tree to
- # produce dist/LICENSE + dist/NOTICE + dist/licenses/. We also verify
- # source vs. binary LICENSE/NOTICE differ in the expected way
- # (only the binary carries the bundled-dep summary).
+ # The binary tarball bundles the full production dependency tree — the
+ # BFF runtime deps (dist/node_modules) AND the UI deps Vite compiles
+ # into the static bundle — so ASF policy requires every one of their
+ # licenses reproduced in the binary LICENSE/NOTICE.
+ # collect-dist-licenses.mjs enumerates that tree straight from the pnpm
+ # store (no `pnpm package` needed), and this job fails if either:
+ # 1. a dep carries a Category-X / unknown license (check-dist-licenses),
or
+ # 2. the committed binary LICENSE/NOTICE reference is stale (--check) —
+ # i.e. prod deps changed without `pnpm licenses:bin:update`.
name: Dependency license
runs-on: ubuntu-latest
- timeout-minutes: 20
+ timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
@@ -58,30 +61,26 @@ jobs:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- - run: pnpm package
- - name: Collect bundled-dep LICENSE/NOTICE
- run: node scripts/collect-dist-licenses.mjs
- - name: Check bundled-dep licenses against ASF allow/deny
- run: node scripts/check-dist-licenses.mjs
+ - name: Verify binary LICENSE/NOTICE (drift + ASF allow/deny)
+ run: pnpm licenses:bin:check
- name: Compare source vs. binary LICENSE/NOTICE shape
run: |
# Source-flavored LICENSE = repo root; must NOT contain the
# bundled-dep summary section.
if grep -q 'Horizon UI Subcomponents' LICENSE; then
echo "ERROR: repo-root LICENSE contains the bundled-dep summary
section."
- echo " That section belongs only in dist/LICENSE (binary
tarball)."
+ echo " That section belongs only in the binary LICENSE."
exit 1
fi
- # Binary-flavored LICENSE = generated; must contain the summary.
- if ! grep -q 'Horizon UI Subcomponents' dist/LICENSE; then
- echo "ERROR: dist/LICENSE missing 'Horizon UI Subcomponents'
section."
- echo " collect-dist-licenses.mjs did not run or template
drift."
+ # Committed binary-flavored LICENSE must contain the summary.
+ if ! grep -q 'Horizon UI Subcomponents'
dist-material/release-docs/LICENSE; then
+ echo "ERROR: committed binary LICENSE missing 'Horizon UI
Subcomponents'."
exit 1
fi
# NOTICE: both must exist and start with the same ASF copyright line.
- test -f NOTICE && test -f dist/NOTICE
+ test -f NOTICE && test -f dist-material/release-docs/NOTICE
head -1 NOTICE | grep -q 'Apache SkyWalking Horizon UI'
- head -1 dist/NOTICE | grep -q 'Apache SkyWalking Horizon UI'
+ head -1 dist-material/release-docs/NOTICE | grep -q 'Apache
SkyWalking Horizon UI'
echo "src/bin LICENSE+NOTICE shape OK."
- name: Upload dependency report
uses: actions/upload-artifact@v4
diff --git a/dist-material/release-docs/LICENSE
b/dist-material/release-docs/LICENSE
new file mode 100644
index 0000000..2876052
--- /dev/null
+++ b/dist-material/release-docs/LICENSE
@@ -0,0 +1,474 @@
+ 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 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 Support. 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 support.
+
+ 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.
+
+
+========================================================================
+ Apache SkyWalking Horizon UI Subcomponents:
+
+ The Apache SkyWalking Horizon UI binary distribution includes a number
+ of subcomponents with separate copyright notices and license terms.
+ Your use of the source code for these subcomponents is subject to the
+ terms and conditions of the licenses listed below. The full text of
+ each license is reproduced in the licenses/ directory of this
+ distribution.
+
+ By license family, the bundled third-party software is:
+========================================================================
+
+
+--- (MPL-2.0 OR Apache-2.0) ---
+
+ * [email protected] (licenses/dompurify-3.4.5/LICENSE)
+
+--- 0BSD ---
+
+ * [email protected] (licenses/tslib-2.3.0/LICENSE.txt)
+
+--- Apache-2.0 ---
+
+ * [email protected] (licenses/d3-flame-graph-4.1.3/LICENSE)
+ * [email protected] (licenses/echarts-6.0.0/LICENSE)
+ * [email protected] (licenses/typescript-5.6.3/LICENSE.txt)
+
+--- BSD-2-Clause ---
+
+ * [email protected] (licenses/entities-7.0.1/LICENSE)
+
+--- BSD-3-Clause ---
+
+ * [email protected] (licenses/d3-ease-3.0.1/LICENSE)
+ * [email protected] (licenses/fast-uri-3.1.2/LICENSE)
+ * [email protected] (licenses/ieee754-1.2.1/LICENSE)
+ * [email protected] (licenses/light-my-request-6.6.0/LICENSE)
+ * [email protected] (licenses/normalize-wheel-es-1.2.0/LICENSE)
+ * [email protected] (licenses/rw-1.3.3/LICENSE)
+ * [email protected] (licenses/secure-json-parse-2.7.0/LICENSE.md)
+ * [email protected] (licenses/secure-json-parse-4.1.0/LICENSE)
+ * [email protected] (licenses/source-map-js-1.2.1/LICENSE)
+ * [email protected] (licenses/zrender-6.0.0/LICENSE)
+
+--- BlueOak-1.0.0 ---
+
+ * [email protected] (licenses/glob-13.0.6/LICENSE.md)
+ * [email protected] (licenses/lru-cache-11.3.6/LICENSE.md)
+ * [email protected] (licenses/minimatch-10.2.5/LICENSE.md)
+ * [email protected] (licenses/minipass-7.1.3/LICENSE.md)
+ * [email protected] (licenses/path-scurry-2.0.2/LICENSE.md)
+
+--- ISC ---
+
+ * [email protected] (licenses/d3-7.9.0/LICENSE)
+ * [email protected] (licenses/d3-array-3.2.4/LICENSE)
+ * [email protected] (licenses/d3-axis-3.0.0/LICENSE)
+ * [email protected] (licenses/d3-brush-3.0.0/LICENSE)
+ * [email protected] (licenses/d3-chord-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-color-3.1.0/LICENSE)
+ * [email protected] (licenses/d3-contour-4.0.2/LICENSE)
+ * [email protected] (licenses/d3-delaunay-6.0.4/LICENSE)
+ * [email protected] (licenses/d3-dispatch-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-drag-3.0.0/LICENSE)
+ * [email protected] (licenses/d3-dsv-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-fetch-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-force-3.0.0/LICENSE)
+ * [email protected] (licenses/d3-format-3.1.2/LICENSE)
+ * [email protected] (licenses/d3-geo-3.1.1/LICENSE)
+ * [email protected] (licenses/d3-hierarchy-3.1.2/LICENSE)
+ * [email protected] (licenses/d3-interpolate-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-path-3.1.0/LICENSE)
+ * [email protected] (licenses/d3-polygon-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-quadtree-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-random-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-scale-4.0.2/LICENSE)
+ * [email protected] (licenses/d3-scale-chromatic-3.1.0/LICENSE)
+ * [email protected] (licenses/d3-selection-3.0.0/LICENSE)
+ * [email protected] (licenses/d3-shape-3.2.0/LICENSE)
+ * [email protected] (licenses/d3-time-3.1.0/LICENSE)
+ * [email protected] (licenses/d3-time-format-4.1.0/LICENSE)
+ * [email protected] (licenses/d3-timer-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-transition-3.0.1/LICENSE)
+ * [email protected] (licenses/d3-zoom-3.0.0/LICENSE)
+ * [email protected] (licenses/delaunator-5.1.0/LICENSE)
+ * [email protected] (licenses/fastq-1.20.1/LICENSE)
+ * [email protected] (licenses/inherits-2.0.4/LICENSE)
+ * [email protected] (licenses/internmap-2.0.3/LICENSE)
+ * [email protected] (licenses/once-1.4.0/LICENSE)
+ * [email protected] (licenses/picocolors-1.1.1/LICENSE)
+ * [email protected] (licenses/semver-7.8.0/LICENSE)
+ * [email protected] (licenses/setprototypeof-1.2.0/LICENSE)
+ * [email protected] (licenses/split2-4.2.0/LICENSE)
+ * [email protected]
+ * [email protected] (licenses/wrappy-1.0.2/LICENSE)
+ * [email protected] (licenses/yaml-2.9.0/LICENSE)
+
+--- MIT ---
+
+ * @babel/[email protected]
(licenses/@babel__helper-string-parser-7.27.1/LICENSE)
+ * @babel/[email protected]
(licenses/@babel__helper-validator-identifier-7.28.5/LICENSE)
+ * @babel/[email protected] (licenses/@babel__parser-7.29.3/LICENSE)
+ * @babel/[email protected] (licenses/@babel__types-7.29.0/LICENSE)
+ * @ctrl/[email protected] (licenses/@ctrl__tinycolor-4.2.0/LICENSE)
+ * @element-plus/[email protected]
(licenses/@element-plus__icons-vue-2.3.2/LICENSE)
+ * @fastify/[email protected]
(licenses/@fastify__accept-negotiator-2.0.1/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__ajv-compiler-4.0.5/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__cookie-11.0.2/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__error-4.2.0/LICENSE)
+ * @fastify/[email protected]
(licenses/@fastify__fast-json-stringify-compiler-5.0.3/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__forwarded-3.0.1/LICENSE)
+ * @fastify/[email protected]
(licenses/@fastify__merge-json-schemas-0.2.1/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__proxy-addr-5.1.0/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__send-4.1.0/LICENSE)
+ * @fastify/[email protected] (licenses/@fastify__static-9.1.3/LICENSE)
+ * @floating-ui/[email protected] (licenses/@floating-ui__core-1.7.5/LICENSE)
+ * @floating-ui/[email protected] (licenses/@floating-ui__dom-1.7.6/LICENSE)
+ * @floating-ui/[email protected] (licenses/@floating-ui__utils-0.2.11/LICENSE)
+ * @graphql-typed-document-node/[email protected]
(licenses/@graphql-typed-document-node__core-3.2.0/LICENSE)
+ * @interactjs/[email protected] (licenses/@interactjs__actions-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__auto-scroll-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__auto-start-1.10.27/LICENSE)
+ * @interactjs/[email protected] (licenses/@interactjs__core-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__dev-tools-1.10.27/LICENSE)
+ * @interactjs/[email protected] (licenses/@interactjs__inertia-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__interact-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__interactjs-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__modifiers-1.10.27/LICENSE)
+ * @interactjs/[email protected] (licenses/@interactjs__offset-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__pointer-events-1.10.27/LICENSE)
+ * @interactjs/[email protected] (licenses/@interactjs__reflow-1.10.27/LICENSE)
+ * @interactjs/[email protected]
(licenses/@interactjs__snappers-1.10.27/LICENSE)
+ * @interactjs/[email protected] (licenses/@interactjs__utils-1.10.27/LICENSE)
+ * @intlify/[email protected] (licenses/@intlify__core-base-10.0.8/LICENSE)
+ * @intlify/[email protected]
(licenses/@intlify__message-compiler-10.0.8/LICENSE)
+ * @intlify/[email protected] (licenses/@intlify__shared-10.0.8/LICENSE)
+ * @jridgewell/[email protected]
(licenses/@jridgewell__sourcemap-codec-1.5.5/LICENSE)
+ * @lukeed/[email protected] (licenses/@lukeed__ms-2.0.2/license)
+ * @phc/[email protected] (licenses/@phc__format-1.0.0/license)
+ * @pinojs/[email protected] (licenses/@pinojs__redact-0.4.0/LICENSE)
+ * @popperjs/[email protected] (licenses/@popperjs__core-2.11.8/LICENSE.md)
+ * @tanstack/[email protected]
(licenses/@tanstack__match-sorter-utils-8.19.4/LICENSE)
+ * @tanstack/[email protected]
(licenses/@tanstack__query-core-5.100.10/LICENSE)
+ * @tanstack/[email protected]
(licenses/@tanstack__vue-query-5.100.10/LICENSE)
+ * @types/[email protected] (licenses/@types__lodash-4.17.24/LICENSE)
+ * @types/[email protected] (licenses/@types__lodash-es-4.17.12/LICENSE)
+ * @types/[email protected] (licenses/@types__trusted-types-2.0.7/LICENSE)
+ * @types/[email protected] (licenses/@types__web-bluetooth-0.0.20/LICENSE)
+ * @types/[email protected] (licenses/@types__web-bluetooth-0.0.21/LICENSE)
+ * @vue/[email protected] (licenses/@vue__compiler-core-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__compiler-dom-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__compiler-sfc-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__compiler-ssr-3.5.34/LICENSE)
+ * @vue/[email protected]
+ * @vue/[email protected] (licenses/@vue__reactivity-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__runtime-core-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__runtime-dom-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__server-renderer-3.5.34/LICENSE)
+ * @vue/[email protected] (licenses/@vue__shared-3.5.34/LICENSE)
+ * @vueuse/[email protected] (licenses/@vueuse__core-11.3.0/LICENSE)
+ * @vueuse/[email protected] (licenses/@vueuse__core-14.3.0/LICENSE)
+ * @vueuse/[email protected] (licenses/@vueuse__metadata-11.3.0/LICENSE)
+ * @vueuse/[email protected] (licenses/@vueuse__metadata-14.3.0/LICENSE)
+ * @vueuse/[email protected] (licenses/@vueuse__shared-11.3.0/LICENSE)
+ * @vueuse/[email protected] (licenses/@vueuse__shared-14.3.0/LICENSE)
+ * [email protected] (licenses/abort-controller-3.0.0/LICENSE)
+ * [email protected]
+ * [email protected] (licenses/ajv-8.20.0/LICENSE)
+ * [email protected] (licenses/ajv-formats-3.0.1/LICENSE)
+ * [email protected] (licenses/argon2-0.41.1/LICENSE)
+ * [email protected] (licenses/async-validator-4.2.5/LICENSE.md)
+ * [email protected] (licenses/atomic-sleep-1.0.0/LICENSE)
+ * [email protected] (licenses/avvio-9.2.0/LICENSE)
+ * [email protected] (licenses/balanced-match-4.0.4/LICENSE.md)
+ * [email protected] (licenses/base64-js-1.5.1/LICENSE)
+ * [email protected] (licenses/batch-processor-1.0.0/LICENSE)
+ * [email protected] (licenses/brace-expansion-5.0.6/LICENSE)
+ * [email protected] (licenses/buffer-6.0.3/LICENSE)
+ * [email protected] (licenses/chokidar-4.0.3/LICENSE)
+ * [email protected] (licenses/colorette-2.0.20/LICENSE.md)
+ * [email protected] (licenses/commander-7.2.0/LICENSE)
+ * [email protected] (licenses/content-disposition-1.1.0/LICENSE)
+ * [email protected] (licenses/cookie-1.1.1/LICENSE)
+ * [email protected] (licenses/csstype-3.2.3/LICENSE)
+ * [email protected] (licenses/dateformat-4.6.3/LICENSE)
+ * [email protected] (licenses/dayjs-1.11.20/LICENSE)
+ * [email protected] (licenses/depd-2.0.0/LICENSE)
+ * [email protected] (licenses/dequal-2.0.3/license)
+ * [email protected] (licenses/element-plus-2.14.0/LICENSE)
+ * [email protected]
(licenses/element-resize-detector-1.2.4/LICENSE)
+ * [email protected] (licenses/end-of-stream-1.4.5/LICENSE)
+ * [email protected] (licenses/escape-html-1.0.3/LICENSE)
+ * [email protected] (licenses/estree-walker-2.0.2/LICENSE)
+ * [email protected] (licenses/event-target-shim-5.0.1/LICENSE)
+ * [email protected] (licenses/events-3.3.0/LICENSE)
+ * [email protected] (licenses/fast-copy-3.0.2/LICENSE)
+ * [email protected]
(licenses/fast-decode-uri-component-1.0.1/LICENSE)
+ * [email protected] (licenses/fast-deep-equal-3.1.3/LICENSE)
+ * [email protected] (licenses/fast-json-stringify-6.4.0/LICENSE)
+ * [email protected] (licenses/fast-querystring-1.1.2/LICENSE)
+ * [email protected] (licenses/fast-safe-stringify-2.1.1/LICENSE)
+ * [email protected] (licenses/fastify-5.8.5/LICENSE)
+ * [email protected] (licenses/fastify-plugin-5.1.0/LICENSE)
+ * [email protected] (licenses/find-my-way-9.6.0/LICENSE)
+ * [email protected] (licenses/graphql-16.14.0/LICENSE)
+ * [email protected] (licenses/graphql-request-7.4.0/LICENSE)
+ * [email protected] (licenses/help-me-5.0.0/LICENSE)
+ * [email protected] (licenses/http-errors-2.0.1/LICENSE)
+ * [email protected] (licenses/iconv-lite-0.6.3/LICENSE)
+ * [email protected] (licenses/ipaddr.js-2.4.0/LICENSE)
+ * [email protected] (licenses/joycon-3.1.1/LICENSE)
+ * [email protected]
(licenses/json-schema-ref-resolver-3.0.0/LICENSE)
+ * [email protected] (licenses/json-schema-traverse-1.0.0/LICENSE)
+ * [email protected] (licenses/ldapts-8.1.7/LICENSE)
+ * [email protected] (licenses/lodash-4.18.1/LICENSE)
+ * [email protected] (licenses/lodash-es-4.18.1/LICENSE)
+ * [email protected]
+ * [email protected] (licenses/magic-string-0.30.21/LICENSE)
+ * [email protected] (licenses/marked-14.0.0/LICENSE.md)
+ * [email protected] (licenses/memoize-one-6.0.0/LICENSE)
+ * [email protected] (licenses/mime-3.0.0/LICENSE)
+ * [email protected] (licenses/minimist-1.2.8/LICENSE)
+ * [email protected]
+ * [email protected] (licenses/monaco-editor-0.55.1/LICENSE)
+ * [email protected] (licenses/nanoid-3.3.12/LICENSE)
+ * [email protected] (licenses/node-addon-api-8.7.0/LICENSE.md)
+ * [email protected] (licenses/node-gyp-build-4.8.4/LICENSE)
+ * [email protected] (licenses/on-exit-leak-free-2.1.2/LICENSE)
+ * [email protected] (licenses/pinia-2.3.1/LICENSE)
+ * [email protected] (licenses/pino-9.14.0/LICENSE)
+ * [email protected]
(licenses/pino-abstract-transport-2.0.0/LICENSE)
+ * [email protected] (licenses/pino-pretty-11.3.0/LICENSE)
+ * [email protected] (licenses/pino-std-serializers-7.1.0/LICENSE)
+ * [email protected] (licenses/postcss-8.5.14/LICENSE)
+ * [email protected] (licenses/process-0.11.10/LICENSE)
+ * [email protected] (licenses/process-warning-4.0.1/LICENSE)
+ * [email protected] (licenses/process-warning-5.0.0/LICENSE)
+ * [email protected] (licenses/pump-3.0.4/LICENSE)
+ * [email protected]
(licenses/quick-format-unescaped-4.0.4/LICENSE)
+ * [email protected] (licenses/readable-stream-4.7.0/LICENSE)
+ * [email protected] (licenses/readdirp-4.1.2/LICENSE)
+ * [email protected] (licenses/real-require-0.2.0/LICENSE.md)
+ * [email protected] (licenses/remove-accents-0.5.0/LICENSE)
+ * [email protected] (licenses/require-from-string-2.0.2/license)
+ * [email protected] (licenses/ret-0.5.0/LICENSE)
+ * [email protected] (licenses/reusify-1.1.0/LICENSE)
+ * [email protected] (licenses/rfdc-1.4.1/LICENSE)
+ * [email protected] (licenses/safe-buffer-5.2.1/LICENSE)
+ * [email protected] (licenses/safe-regex2-5.1.1/LICENSE)
+ * [email protected] (licenses/safe-stable-stringify-2.5.0/LICENSE)
+ * [email protected] (licenses/safer-buffer-2.1.2/LICENSE)
+ * [email protected] (licenses/set-cookie-parser-2.7.2/LICENSE)
+ * [email protected] (licenses/sonic-boom-4.2.1/LICENSE)
+ * [email protected] (licenses/statuses-2.0.2/LICENSE)
+ * [email protected] (licenses/string_decoder-1.3.0/LICENSE)
+ * [email protected] (licenses/strip-json-comments-3.1.1/license)
+ * [email protected] (licenses/thread-stream-3.1.0/LICENSE)
+ * [email protected] (licenses/toad-cache-3.7.0/LICENSE)
+ * [email protected] (licenses/toidentifier-1.0.1/LICENSE)
+ * [email protected] (licenses/vue-3.5.34/LICENSE)
+ * [email protected]
(licenses/vue-component-type-helpers-3.2.8/LICENSE)
+ * [email protected] (licenses/vue-demi-0.14.10/LICENSE)
+ * [email protected] (licenses/vue-grid-layout-3.0.0-beta1/LICENSE)
+ * [email protected] (licenses/vue-i18n-10.0.8/LICENSE)
+ * [email protected] (licenses/vue-router-4.6.4/LICENSE)
+ * [email protected] (licenses/zod-3.25.76/LICENSE)
+
+--- Unlicense ---
+
+ * [email protected] (licenses/robust-predicates-3.0.3/LICENSE)
diff --git a/dist-material/release-docs/NOTICE
b/dist-material/release-docs/NOTICE
new file mode 100644
index 0000000..5fe353b
--- /dev/null
+++ b/dist-material/release-docs/NOTICE
@@ -0,0 +1,18 @@
+Apache SkyWalking Horizon UI
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+========================================================================
+This binary distribution bundles third-party software, whose own NOTICE
+files (where present) are reproduced below verbatim.
+========================================================================
+
+------ [email protected] ------
+Apache ECharts
+Copyright 2017-2025 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (https://www.apache.org/).
+
diff --git a/dist-material/release-docs/license-overrides.json
b/dist-material/release-docs/license-overrides.json
new file mode 100644
index 0000000..a41c71b
--- /dev/null
+++ b/dist-material/release-docs/license-overrides.json
@@ -0,0 +1,4 @@
+{
+ "_comment": "Human-asserted SPDX license for bundled deps whose package.json
omits or mis-declares the `license` field. Each entry MUST be verified against
the package's own LICENSE/COPYING file (reproduced under dist/licenses/) before
being added. Keyed by `name@version`.",
+ "[email protected]": "MIT"
+}
diff --git a/package.json b/package.json
index 52d0b00..e7c233a 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,8 @@
"test:unit": "pnpm -r run test:unit",
"license:check": "license-eye -c .licenserc.yaml header check",
"license:fix": "license-eye -c .licenserc.yaml header fix",
+ "licenses:bin:update": "node scripts/collect-dist-licenses.mjs
--update-reference",
+ "licenses:bin:check": "node scripts/collect-dist-licenses.mjs --check &&
node scripts/check-dist-licenses.mjs",
"package": "node scripts/package.mjs",
"start": "HORIZON_CONFIG=${HORIZON_CONFIG:-./horizon.yaml} node
dist/server.js"
},
diff --git a/scripts/collect-dist-licenses.mjs
b/scripts/collect-dist-licenses.mjs
index ed8700b..9d341de 100644
--- a/scripts/collect-dist-licenses.mjs
+++ b/scripts/collect-dist-licenses.mjs
@@ -16,25 +16,34 @@
*/
/**
- * Walk the production `dist/node_modules/` tree built by
- * `scripts/package.mjs` and emit the binary-tar's LICENSE / NOTICE /
- * licenses/ subtree.
+ * Generate the BINARY distribution's LICENSE / NOTICE (+ per-dependency
+ * license texts) by enumerating the full PRODUCTION dependency tree the
+ * binary bundles:
*
- * Apache distribution rule: every bundled third-party module's license
- * is reproduced in the binary tarball. The source tarball ships only
- * first-party source (top-level LICENSE + NOTICE are enough), but the
- * binary bundles npm dependencies and therefore needs an expanded LICENSE
- * (license-family summary) and NOTICE (third-party NOTICE pass-throughs)
- * plus per-package license texts under licenses/<name>-<version>/.
+ * - the BFF runtime deps (shipped verbatim as dist/node_modules), AND
+ * - the UI's production deps (compiled into dist/static by Vite).
*
- * Output (relative to repo `dist/`):
- * LICENSE — Apache-2.0 + grouped third-party summary
- * NOTICE — ASF + concatenated third-party NOTICEs
- * licenses/<pkg>-<ver>/ — verbatim LICENSE-ish files from each dep
- * .dependency-report.json — { packages: [...] } for check-dist-licenses
+ * Both are redistributed in the binary tarball, so ASF distribution policy
+ * requires every one of their licenses to be reproduced. We read the tree
+ * via `pnpm list --prod` filtered to the bff + ui workspace packages — so
+ * this runs after a plain `pnpm install`, with NO `pnpm package` step. That
+ * lets CI regenerate and diff cheaply.
*
- * Run after `pnpm package`. Re-runs are idempotent: the script clears
- * dist/licenses/ first.
+ * Output is deterministic: the NOTICE copyright year is read from the
+ * repo-root NOTICE (not the wall clock) and the dependency report carries no
+ * timestamp, so the committed reference is byte-stable across runs/years.
+ *
+ * Modes:
+ * (default) Write dist/LICENSE, dist/NOTICE,
dist/licenses/<pkg>/,
+ * and dist/.dependency-report.json (consumed by
+ * check-dist-licenses.mjs and the release packager).
+ * --update-reference Also copy the generated LICENSE + NOTICE into
+ * dist-material/release-docs/ — the committed
+ * reference. Run after any production-dependency
+ * change, then commit the diff.
+ * --check Compare the freshly generated LICENSE + NOTICE
+ * against the committed reference and exit non-zero on
+ * any drift (the CI guard).
*/
import { execSync } from 'node:child_process';
@@ -45,7 +54,6 @@ import {
readFileSync,
readdirSync,
rmSync,
- statSync,
writeFileSync,
} from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
@@ -54,16 +62,21 @@ import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(__dirname, '..');
const distDir = resolve(repoRoot, 'dist');
-const nmDir = resolve(distDir, 'node_modules');
const licensesOutDir = resolve(distDir, 'licenses');
const templatesDir = resolve(repoRoot, 'dist-material/release-docs');
+// The committed binary LICENSE / NOTICE live next to their .tpl sources.
+const referenceDir = templatesDir;
-if (!existsSync(nmDir)) {
- console.error(
- `FATAL: ${nmDir} does not exist. Run \`pnpm package\` first.`,
- );
- process.exit(1);
-}
+const args = new Set(process.argv.slice(2));
+const CHECK = args.has('--check');
+const UPDATE_REFERENCE = args.has('--update-reference');
+
+// Workspace packages whose PRODUCTION dependencies end up in the binary
+// (bff → node_modules at runtime; ui → compiled into the static bundle).
+const BUNDLED_WORKSPACE_PACKAGES = [
+ '@skywalking-horizon-ui/bff',
+ '@skywalking-horizon-ui/ui',
+];
const LICENSE_FILE_PATTERNS = [
/^LICENSE$/i,
@@ -85,54 +98,68 @@ function pickFile(dir, patterns) {
return null;
}
-// Find every realpath-distinct package directory under dist/node_modules.
-// pnpm's layout puts true packages under
`.pnpm/<pkg>@<ver>(_<peers>)/node_modules/<pkg>/`,
-// with top-level entries being symlinks into that store. We use `pnpm list`
-// to get the canonical production dep graph and resolve each entry's
-// realpath to get the package.json we should be reading.
+// Enumerate every third-party production package reachable from the bundled
+// workspace packages. `pnpm list --prod --depth Infinity` resolves each
+// entry to its real path in the pnpm store, so license files are readable
+// straight from there — no dist/node_modules required.
function collectPackages() {
- // `pnpm list --prod --depth Infinity --json` returns the realized
- // production dep tree. We flatten it ourselves so first-party workspace
- // packages can be filtered out by name prefix.
+ const filters = BUNDLED_WORKSPACE_PACKAGES.flatMap((p) => ['--filter', p]);
const raw = execSync(
- 'pnpm list --prod --depth Infinity --json',
+ ['pnpm', 'list', '--prod', '--depth', 'Infinity', '--json',
...filters].join(' '),
{
- cwd: distDir,
- maxBuffer: 64 * 1024 * 1024,
+ cwd: repoRoot,
+ maxBuffer: 128 * 1024 * 1024,
stdio: ['ignore', 'pipe', 'inherit'],
},
).toString();
const json = JSON.parse(raw);
- // pnpm list returns an array of root packages. dist/ has exactly one.
- const root = Array.isArray(json) ? json[0] : json;
+ const roots = Array.isArray(json) ? json : [json];
- const seen = new Map(); // key: name@version → { path, name, version }
+ const seen = new Map(); // key: name@version → { name, version, path }
function walk(deps) {
if (!deps) return;
for (const [name, info] of Object.entries(deps)) {
- // Skip first-party workspace packages — they're our own code.
+ // First-party workspace packages are our own code — recurse past
+ // them so their third-party deps are still captured.
if (name.startsWith('@skywalking-horizon-ui/')) {
walk(info.dependencies);
continue;
}
const version = info.version;
const key = `${name}@${version}`;
- if (seen.has(key)) continue;
- const pkgPath = info.path;
- if (!pkgPath || !existsSync(pkgPath)) {
- console.warn(`WARN: package path missing for ${key}: ${pkgPath}`);
- continue;
+ if (!seen.has(key)) {
+ const pkgPath = info.path;
+ if (!pkgPath || !existsSync(pkgPath)) {
+ console.warn(`WARN: package path missing for ${key}: ${pkgPath}`);
+ } else {
+ seen.set(key, { name, version, path: pkgPath });
+ }
}
- seen.set(key, { name, version, path: pkgPath });
walk(info.dependencies);
}
}
- walk(root.dependencies);
+ for (const root of roots) walk(root.dependencies);
+
return Array.from(seen.values()).sort((a, b) =>
a.name === b.name ? a.version.localeCompare(b.version) :
a.name.localeCompare(b.name),
);
}
+// Human-asserted SPDX licenses for deps whose package.json omits or
+// mis-declares `license`. Verified against each package's own LICENSE file.
+const licenseOverrides = (() => {
+ const p = join(templatesDir, 'license-overrides.json');
+ if (!existsSync(p)) return {};
+ try {
+ const { _comment, ...rest } = JSON.parse(readFileSync(p, 'utf8'));
+ void _comment;
+ return rest;
+ } catch (e) {
+ console.warn(`WARN: cannot parse ${p}: ${e.message}`);
+ return {};
+ }
+})();
+
function normalizeLicense(pkgJson) {
const lic = pkgJson.license;
if (typeof lic === 'string') return lic;
@@ -140,7 +167,6 @@ function normalizeLicense(pkgJson) {
return lic.type;
}
if (Array.isArray(pkgJson.licenses)) {
- // Deprecated form. Join SPDX-style.
return pkgJson.licenses.map((l) => l.type || l).filter(Boolean).join(' OR
');
}
return 'UNKNOWN';
@@ -157,9 +183,23 @@ function readPkgJson(pkgPath) {
}
}
+// Read the copyright year from the committed root NOTICE so the generated
+// binary NOTICE is reproducible (a wall-clock year would drift every Jan 1
+// and break the CI diff).
+function noticeYear() {
+ try {
+ const txt = readFileSync(resolve(repoRoot, 'NOTICE'), 'utf8');
+ const m = txt.match(/Copyright\s+(\d{4})/);
+ if (m) return m[1];
+ } catch {
+ /* fall through */
+ }
+ return String(new Date().getUTCFullYear());
+}
+
const packages = collectPackages();
-// Reset output directory
+mkdirSync(distDir, { recursive: true });
rmSync(licensesOutDir, { recursive: true, force: true });
mkdirSync(licensesOutDir, { recursive: true });
@@ -170,7 +210,7 @@ const noticePieces = [];
for (const pkg of packages) {
const pj = readPkgJson(pkg.path);
if (!pj) continue;
- const license = normalizeLicense(pj);
+ const license = licenseOverrides[`${pkg.name}@${pkg.version}`] ??
normalizeLicense(pj);
const homepage = pj.homepage || pj.repository?.url || pj.repository || '';
const entry = {
name: pkg.name,
@@ -200,10 +240,7 @@ for (const pkg of packages) {
cpSync(noticeFile, dest);
entry.noticeFile = relative(distDir, dest);
noticePieces.push(
- `------ ${pkg.name}@${pkg.version} ------\n${readFileSync(
- noticeFile,
- 'utf8',
- ).trim()}\n`,
+ `------ ${pkg.name}@${pkg.version} ------\n${readFileSync(noticeFile,
'utf8').trim()}\n`,
);
}
@@ -229,9 +266,8 @@ writeFileSync(join(distDir, 'LICENSE'), licenseOut);
// Render NOTICE.tpl → dist/NOTICE
const noticeTpl = readFileSync(join(templatesDir, 'NOTICE.tpl'), 'utf8');
-const year = new Date().getUTCFullYear();
const noticeOut = noticeTpl
- .replace('{{ .Year }}', String(year))
+ .replace('{{ .Year }}', noticeYear())
.replace(
'{{ .Notices }}',
noticePieces.length > 0
@@ -240,12 +276,12 @@ const noticeOut = noticeTpl
);
writeFileSync(join(distDir, 'NOTICE'), noticeOut);
-// Machine-readable report for the check step.
+// Machine-readable report for check-dist-licenses.mjs. No timestamp — the
+// file is an input to a deterministic diff, not an audit log.
writeFileSync(
join(distDir, '.dependency-report.json'),
JSON.stringify(
{
- generatedAt: new Date().toISOString(),
packageCount: report.length,
packages: report,
byLicense: Object.fromEntries(
@@ -254,10 +290,50 @@ writeFileSync(
},
null,
2,
- ),
+ ) + '\n',
);
console.log(
`Wrote dist/LICENSE, dist/NOTICE, dist/licenses/ (${report.length} packages,
` +
`${sortedLicenses.length} license families).`,
);
+
+// ── Reference sync / drift check ────────────────────────────────────────
+const refLicense = join(referenceDir, 'LICENSE');
+const refNotice = join(referenceDir, 'NOTICE');
+
+if (UPDATE_REFERENCE) {
+ cpSync(join(distDir, 'LICENSE'), refLicense);
+ cpSync(join(distDir, 'NOTICE'), refNotice);
+ console.log(
+ `Updated committed reference: ${relative(repoRoot, refLicense)},
${relative(repoRoot, refNotice)}.`,
+ );
+}
+
+if (CHECK) {
+ const drift = [];
+ for (const [label, generated, reference] of [
+ ['LICENSE', join(distDir, 'LICENSE'), refLicense],
+ ['NOTICE', join(distDir, 'NOTICE'), refNotice],
+ ]) {
+ if (!existsSync(reference)) {
+ drift.push(`${label}: committed reference ${relative(repoRoot,
reference)} is missing.`);
+ continue;
+ }
+ if (readFileSync(generated, 'utf8') !== readFileSync(reference, 'utf8')) {
+ drift.push(`${label}: differs from ${relative(repoRoot, reference)}.`);
+ }
+ }
+ if (drift.length > 0) {
+ console.error('');
+ console.error('Binary LICENSE / NOTICE drift detected:');
+ for (const d of drift) console.error(` - ${d}`);
+ console.error('');
+ console.error(
+ 'Production dependencies changed without regenerating the committed
reference.',
+ );
+ console.error('Run: pnpm licenses:bin:update then commit
dist-material/release-docs/.');
+ process.exit(1);
+ }
+ console.log('Binary LICENSE / NOTICE match the committed reference.');
+}
diff --git a/scripts/release.sh b/scripts/release.sh
index d21f5a6..51b3b57 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -310,8 +310,11 @@ cd "${CLONE_DIR}"
pnpm install --frozen-lockfile
pnpm package
# The packager left dist/server.js + dist/node_modules + dist/static + …
-# Now layer in LICENSE/NOTICE + per-dep license texts.
-node "${CLONE_DIR}/scripts/collect-dist-licenses.mjs"
+# Now layer in LICENSE/NOTICE + per-dep license texts. `--check` regenerates
+# dist/LICENSE + dist/NOTICE AND aborts if they drift from the committed
+# reference (dist-material/release-docs/) — so the binary ships exactly the
+# reviewed bytes. check-dist-licenses then enforces the ASF allow/deny list.
+node "${CLONE_DIR}/scripts/collect-dist-licenses.mjs" --check
node "${CLONE_DIR}/scripts/check-dist-licenses.mjs"
# Stage the binary contents under a clean folder name so the tar root