This is an automated email from the ASF dual-hosted git repository.
bobbai00 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 4be9baeaa2 chore(build): add customized LICENSE to jar META-INF and
dist zips (#4367)
4be9baeaa2 is described below
commit 4be9baeaa29342e8c2a48d90e3491a98dc76fb7a
Author: Jiadong Bai <[email protected]>
AuthorDate: Fri Apr 17 14:04:08 2026 -0700
chore(build): add customized LICENSE to jar META-INF and dist zips (#4367)
### What changes were proposed in this PR?
This PR ensures that:
- Each jar's `LICENSE` describes only what is in that jar
- For `workflow-operator` jar: the LICENSE includes Apache 2.0 and 3rd
party code `mbknor-jackson-jsonschema` MIT attribution and MIT license.
- All other jars: the LICENSE is Apache 2.0 only
- the zips produced by `sbt dist` include LICENSE/NOTICE/DISCLAIMER at
the top level
### Any related issues, documentation, discussions?
Closes #4394
### How was this PR tested?
- Built dist zips and inspected jar META-INF/LICENSE content
- Verified all Texera module jars are thin jars (no dependency classes
merged in)
- Confirmed `workflow-operator` is the only jar with non-Apache classes
(`com/kjetland`)
### Was this PR authored or co-authored using generative AI tooling?
Co-authored using: Claude Code (Claude Opus 4.6)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Xinyuan Lin <[email protected]>
---
.licenserc.yaml | 1 -
bin/access-control-service.dockerfile | 3 -
bin/computing-unit-master.dockerfile | 3 -
bin/computing-unit-worker.dockerfile | 3 -
bin/config-service.dockerfile | 3 -
bin/file-service.dockerfile | 3 -
bin/texera-web-application.dockerfile | 3 -
bin/workflow-compiling-service.dockerfile | 3 -
...flow-computing-unit-managing-service.dockerfile | 3 -
build.sbt | 31 ++---
project/AddMetaInfLicenseFiles.scala | 142 +++++++++++++++++++++
11 files changed, 155 insertions(+), 43 deletions(-)
diff --git a/.licenserc.yaml b/.licenserc.yaml
index 6c07e6b008..a521c0be3b 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -11,7 +11,6 @@ header:
- '**/*.json'
- '**/*.jsonl'
- 'DESCRIPTION'
- - 'DISCLAIMER'
- 'DISCLAIMER-WIP'
- 'LICENSE'
- 'NOTICE'
diff --git a/bin/access-control-service.dockerfile
b/bin/access-control-service.dockerfile
index 0a7866a0c9..208fb4f40c 100644
--- a/bin/access-control-service.dockerfile
+++ b/bin/access-control-service.dockerfile
@@ -51,9 +51,6 @@ COPY --from=build /texera/.git /texera/.git
COPY --from=build /texera/target/access-control-service* /texera/
# Copy resources directories from build phase
COPY --from=build /texera/access-control-service/src/main/resources
/texera/access-control-service/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/access-control-service"]
EXPOSE 9096
\ No newline at end of file
diff --git a/bin/computing-unit-master.dockerfile
b/bin/computing-unit-master.dockerfile
index fa079558f4..ff0fbcff25 100644
--- a/bin/computing-unit-master.dockerfile
+++ b/bin/computing-unit-master.dockerfile
@@ -69,9 +69,6 @@ COPY --from=build /texera/common/config/src/main/resources
/texera/amber/common/
COPY --from=build /texera/amber/src/main/resources
/texera/amber/src/main/resources
# Copy code for python UDF
COPY --from=build /texera/amber/src/main/python /texera/amber/src/main/python
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/computing-unit-master"]
EXPOSE 8085
diff --git a/bin/computing-unit-worker.dockerfile
b/bin/computing-unit-worker.dockerfile
index 9fd013f384..b0a1b9f2af 100644
--- a/bin/computing-unit-worker.dockerfile
+++ b/bin/computing-unit-worker.dockerfile
@@ -68,9 +68,6 @@ COPY --from=build /texera/amber/target/amber-* /texera/amber/
# Copy resources directories from build phase
COPY --from=build /texera/amber/src/main/resources
/texera/amber/src/main/resources
COPY --from=build /texera/common/config/src/main/resources
/texera/amber/common/config/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/computing-unit-worker"]
EXPOSE 8085
\ No newline at end of file
diff --git a/bin/config-service.dockerfile b/bin/config-service.dockerfile
index f3abbcc617..a7665eb067 100644
--- a/bin/config-service.dockerfile
+++ b/bin/config-service.dockerfile
@@ -52,9 +52,6 @@ COPY --from=build /texera/target/config-service-* /texera/
# Copy resources directories from build phase
COPY --from=build /texera/common/config/src/main/resources
/texera/common/config/src/main/resources
COPY --from=build /texera/config-service/src/main/resources
/texera/config-service/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/config-service"]
EXPOSE 9094
\ No newline at end of file
diff --git a/bin/file-service.dockerfile b/bin/file-service.dockerfile
index 9663818ccd..88bdf1c6b0 100644
--- a/bin/file-service.dockerfile
+++ b/bin/file-service.dockerfile
@@ -51,9 +51,6 @@ COPY --from=build /texera/target/file-service-* /texera/
# Copy resources directories from build phase
COPY --from=build /texera/common/config/src/main/resources
/texera/common/config/src/main/resources
COPY --from=build /texera/file-service/src/main/resources
/texera/file-service/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/file-service"]
EXPOSE 9092
\ No newline at end of file
diff --git a/bin/texera-web-application.dockerfile
b/bin/texera-web-application.dockerfile
index a3bb2d85e3..fec179d8d2 100644
--- a/bin/texera-web-application.dockerfile
+++ b/bin/texera-web-application.dockerfile
@@ -67,9 +67,6 @@ COPY --from=build /texera/amber/target/amber-* /texera/amber/
# Copy resources directories from build phase
COPY --from=build /texera/amber/src/main/resources
/texera/amber/src/main/resources
COPY --from=build /texera/common/config/src/main/resources
/texera/amber/common/config/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/texera-web-application"]
EXPOSE 8080
\ No newline at end of file
diff --git a/bin/workflow-compiling-service.dockerfile
b/bin/workflow-compiling-service.dockerfile
index 291d2d52e2..440dce2777 100644
--- a/bin/workflow-compiling-service.dockerfile
+++ b/bin/workflow-compiling-service.dockerfile
@@ -52,9 +52,6 @@ COPY --from=build /texera/target/workflow-compiling-service-*
/texera/
# Copy resources directories from build phase
COPY --from=build /texera/common/config/src/main/resources
/texera/common/config/src/main/resources
COPY --from=build /texera/workflow-compiling-service/src/main/resources
/texera/workflow-compiling-service/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/workflow-compiling-service"]
EXPOSE 9090
\ No newline at end of file
diff --git a/bin/workflow-computing-unit-managing-service.dockerfile
b/bin/workflow-computing-unit-managing-service.dockerfile
index 0f0739d60c..e11dacc046 100644
--- a/bin/workflow-computing-unit-managing-service.dockerfile
+++ b/bin/workflow-computing-unit-managing-service.dockerfile
@@ -52,9 +52,6 @@ COPY --from=build
/texera/target/computing-unit-managing-service-* /texera/
# Copy resources directories from build phase
COPY --from=build /texera/common/config/src/main/resources
/texera/common/config/src/main/resources
COPY --from=build /texera/computing-unit-managing-service/src/main/resources
/texera/computing-unit-managing-service/src/main/resources
-# Copy ASF licensing files
-COPY --from=build /texera/LICENSE /texera/NOTICE /texera/DISCLAIMER-WIP
/texera/
-
CMD ["bin/computing-unit-managing-service"]
EXPOSE 8888
\ No newline at end of file
diff --git a/build.sbt b/build.sbt
index 6673408561..8e5ffd02cc 100644
--- a/build.sbt
+++ b/build.sbt
@@ -15,23 +15,12 @@
// specific language governing permissions and limitations
// under the License.
-// Copy LICENSE, NOTICE, and DISCLAIMER-WIP from the repo root into META-INF
of every JAR.
-// This ensures ASF licensing files are present in all binary artifacts.
-lazy val asfLicensingSettings = Seq(
- Compile / resourceGenerators += Def.task {
- val rootDir = (ThisBuild / baseDirectory).value
- val metaInfDir = (Compile / resourceManaged).value / "META-INF"
- val filesToCopy = Seq("LICENSE", "NOTICE", "DISCLAIMER-WIP")
- filesToCopy.flatMap { fileName =>
- val src = rootDir / fileName
- if (src.exists()) {
- val dest = metaInfDir / fileName
- IO.copyFile(src, dest)
- Seq(dest)
- } else Seq.empty
- }
- }.taskValue
-)
+// Per-module ASF licensing: each jar's META-INF/LICENSE describes only what
is in that jar.
+// Modules without vendored code get Apache 2.0 only; workflow-operator
includes mbknor attribution.
+// See project/AddMetaInfLicenseFiles.scala.
+lazy val asfLicensingSettings = AddMetaInfLicenseFiles.defaultSettings
+lazy val asfLicensingSettingsWithVendored =
AddMetaInfLicenseFiles.workflowOperatorSettings
+lazy val asfDistLicensingSettings = AddMetaInfLicenseFiles.distSettings
lazy val DAO = (project in file("common/dao")).settings(asfLicensingSettings)
lazy val Config = (project in
file("common/config")).settings(asfLicensingSettings)
@@ -41,6 +30,7 @@ lazy val Auth = (project in file("common/auth"))
lazy val ConfigService = (project in file("config-service"))
.dependsOn(Auth, Config)
.settings(asfLicensingSettings)
+ .settings(asfDistLicensingSettings)
.settings(
dependencyOverrides ++= Seq(
// override it as io.dropwizard 4 require 2.16.1 or higher
@@ -50,6 +40,7 @@ lazy val ConfigService = (project in file("config-service"))
lazy val AccessControlService = (project in file("access-control-service"))
.dependsOn(Auth, Config, DAO)
.settings(asfLicensingSettings)
+ .settings(asfDistLicensingSettings)
.settings(
dependencyOverrides ++= Seq(
// override it as io.dropwizard 4 require 2.16.1 or higher
@@ -74,6 +65,7 @@ lazy val WorkflowCore = (project in
file("common/workflow-core"))
lazy val ComputingUnitManagingService = (project in
file("computing-unit-managing-service"))
.dependsOn(WorkflowCore, Auth, Config)
.settings(asfLicensingSettings)
+ .settings(asfDistLicensingSettings)
.settings(
dependencyOverrides ++= Seq(
// override it as io.dropwizard 4 require 2.16.1 or higher
@@ -82,6 +74,7 @@ lazy val ComputingUnitManagingService = (project in
file("computing-unit-managin
)
lazy val FileService = (project in file("file-service"))
.settings(asfLicensingSettings)
+ .settings(asfDistLicensingSettings)
.dependsOn(WorkflowCore, Auth, Config)
.configs(Test)
.dependsOn(DAO % "test->test") // test scope dependency
@@ -94,10 +87,11 @@ lazy val FileService = (project in file("file-service"))
)
)
-lazy val WorkflowOperator = (project in
file("common/workflow-operator")).settings(asfLicensingSettings).dependsOn(WorkflowCore)
+lazy val WorkflowOperator = (project in
file("common/workflow-operator")).settings(asfLicensingSettingsWithVendored).dependsOn(WorkflowCore)
lazy val WorkflowCompilingService = (project in
file("workflow-compiling-service"))
.dependsOn(WorkflowOperator, Config)
.settings(asfLicensingSettings)
+ .settings(asfDistLicensingSettings)
.settings(
dependencyOverrides ++= Seq(
// override it as io.dropwizard 4 require 2.16.1 or higher
@@ -110,6 +104,7 @@ lazy val WorkflowCompilingService = (project in
file("workflow-compiling-service
lazy val WorkflowExecutionService = (project in file("amber"))
.dependsOn(WorkflowOperator, Auth, Config)
.settings(asfLicensingSettings)
+ .settings(asfDistLicensingSettings)
.settings(
dependencyOverrides ++= Seq(
"com.fasterxml.jackson.core" % "jackson-core" % "2.15.1",
diff --git a/project/AddMetaInfLicenseFiles.scala
b/project/AddMetaInfLicenseFiles.scala
new file mode 100644
index 0000000000..30f191a0db
--- /dev/null
+++ b/project/AddMetaInfLicenseFiles.scala
@@ -0,0 +1,142 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+import sbt._
+import sbt.Keys._
+import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport._
+
+/**
+ * Generates per-module LICENSE files for jar META-INF and dist zip top level.
+ *
+ * Each jar's META-INF/LICENSE describes only what is in that specific jar:
+ * - Modules without vendored code get Apache 2.0 only.
+ * - workflow-operator gets Apache 2.0 plus the mbknor-jackson-jsonschema
+ * attribution and the full MIT license text.
+ *
+ * NOTICE and DISCLAIMER-WIP are copied as-is from the repo root.
+ *
+ * See https://github.com/apache/texera/issues/4131
+ */
+object AddMetaInfLicenseFiles {
+
+ private lazy val rootDir = LocalRootProject / baseDirectory
+
+ private val ThirdPartyHeader = "THIRD-PARTY DEPENDENCIES"
+
+ /** Extract the Apache 2.0 license text (before the THIRD-PARTY section)
from root LICENSE. */
+ private def apacheLicenseText(rootDir: File): String = {
+ val lines = IO.readLines(rootDir / "LICENSE")
+ val headerIndex = lines.indexWhere(_.trim == ThirdPartyHeader)
+ val cutoffIndex =
+ if (headerIndex >= 0) {
+ // Cut at the "---" delimiter line preceding the header
+ val delimiterIndex = lines.lastIndexWhere(_.startsWith("---"),
headerIndex - 1)
+ if (delimiterIndex >= 0) delimiterIndex else headerIndex
+ } else {
+ lines.length
+ }
+ lines.take(cutoffIndex).mkString("\n").trim + "\n"
+ }
+
+ /** The vendored code section for workflow-operator
(mbknor-jackson-jsonschema). */
+ private def workflowOperatorVendoredSection(rootDir: File): String = {
+ val mitLicense = IO.read(rootDir / "licenses" / "LICENSE-MIT.txt")
+ s"""
+
|--------------------------------------------------------------------------------
+ |THIRD-PARTY DEPENDENCIES
+
|--------------------------------------------------------------------------------
+ |
+ |This jar bundles compiled code from the following third-party project.
+ |The full license text is included below.
+ |
+ |MIT License
+ |--------------------------------------
+ |
+ |This product bundles code derived from mbknor-jackson-jsonschema:
+ | - com/kjetland/jackson/jsonSchema/
+ | Copyright (c) 2016 Kjell Tore Eliassen (mbknor)
+ | Source: https://github.com/mbknor/mbknor-jackson-jsonschema
+ |
+
|--------------------------------------------------------------------------------
+ |Full text of the MIT License:
+
|--------------------------------------------------------------------------------
+ |
+ |${mitLicense.trim}
+ |""".stripMargin
+ }
+
+ private def writeToMetaInf(managed: File, fileName: String, content:
String): File = {
+ val dest = managed / "META-INF" / fileName
+ IO.write(dest, content)
+ dest
+ }
+
+ private def copyToMetaInf(managed: File, src: File, fileName: String): File
= {
+ val dest = managed / "META-INF" / fileName
+ IO.copyFile(src, dest)
+ dest
+ }
+
+ private def noticeAndDisclaimer(managed: File, rootDir: File): Seq[File] = {
+ val files = Seq(copyToMetaInf(managed, rootDir / "NOTICE", "NOTICE"))
+ val disclaimer = rootDir / "DISCLAIMER-WIP"
+ if (disclaimer.exists()) files :+ copyToMetaInf(managed, disclaimer,
"DISCLAIMER-WIP")
+ else files
+ }
+
+ /** Settings for modules WITHOUT vendored third-party code.
+ * META-INF/LICENSE contains only the Apache 2.0 license text. */
+ lazy val defaultSettings: Seq[Setting[_]] = Seq(
+ Compile / resourceGenerators += Def.task {
+ val managed = (Compile / resourceManaged).value
+ val root = rootDir.value
+ val licenseContent = apacheLicenseText(root)
+ writeToMetaInf(managed, "LICENSE", licenseContent) +:
noticeAndDisclaimer(managed, root)
+ }.taskValue
+ )
+
+ /** Settings for workflow-operator which contains vendored
mbknor-jackson-jsonschema code.
+ * META-INF/LICENSE contains Apache 2.0 plus the mbknor attribution and MIT
license text. */
+ lazy val workflowOperatorSettings: Seq[Setting[_]] = Seq(
+ Compile / resourceGenerators += Def.task {
+ val managed = (Compile / resourceManaged).value
+ val root = rootDir.value
+ val licenseContent = apacheLicenseText(root) + "\n" +
workflowOperatorVendoredSection(root)
+ writeToMetaInf(managed, "LICENSE", licenseContent) +:
noticeAndDisclaimer(managed, root)
+ }.taskValue
+ )
+
+ /** Additional settings for dist-producing modules: places the same files
+ * at the top level of the sbt-native-packager Universal zip so they
+ * appear alongside lib/ and bin/ in the distribution. */
+ lazy val distSettings: Seq[Setting[_]] = Seq(
+ Universal / mappings := {
+ val existing = (Universal / mappings).value
+ val root = rootDir.value
+ val licenseFile = root / "LICENSE"
+ val noticeFile = root / "NOTICE"
+ val disclaimerFile = root / "DISCLAIMER-WIP"
+ val reserved = Set("LICENSE", "NOTICE", "DISCLAIMER-WIP")
+ val filtered = existing.filterNot { case (_, path) =>
reserved.contains(path) }
+ val extras = Seq(
+ licenseFile -> "LICENSE",
+ noticeFile -> "NOTICE"
+ ) ++ (if (disclaimerFile.exists()) Seq(disclaimerFile ->
"DISCLAIMER-WIP") else Seq.empty)
+ filtered ++ extras
+ }
+ )
+}