From 8751f98f944edcbf7bf70f4c60151c8436d162d5 Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 17:21:53 -0300 Subject: [PATCH 1/7] Make integrated NDI packaging explicit opt-in --- devolay-java/build.gradle.kts | 25 ++++++++++++++++++++----- devolay-natives/build.gradle.kts | 9 +++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/devolay-java/build.gradle.kts b/devolay-java/build.gradle.kts index 2e847e6..4d814cf 100644 --- a/devolay-java/build.gradle.kts +++ b/devolay-java/build.gradle.kts @@ -81,6 +81,13 @@ val nativeAndroidDependency: Configuration by configurations.creating val prebuiltNativeArtifacts = providers.gradleProperty("prebuiltNativeArtifacts") +// Packaging proprietary NDI runtime binaries is explicitly opt-in. +// Normal builds and public Maven publications remain runtime-separated. +val enableIntegratedNdi = + providers.gradleProperty("enableIntegratedNdi") + .map { it.toBoolean() } + .orElse(false) + dependencies { if (prebuiltNativeArtifacts.isPresent) { val prebuiltNativeJar = @@ -97,7 +104,10 @@ dependencies { project(":devolay-natives", "nativeArtifacts")) } - ndiDesktopDependency(project(":devolay-natives", "integratedNdiArtifacts")) + if (enableIntegratedNdi.get()) { + ndiDesktopDependency( + project(":devolay-natives", "integratedNdiArtifacts")) + } nativeAndroidDependency(project(":devolay-natives", "androidArtifacts")) } @@ -107,10 +117,15 @@ tasks.jar { } tasks.named("integratedJar") { - dependsOn(ndiDesktopDependency) - dependsOn(nativeDesktopDependency) - from(ndiDesktopDependency.map { zipTree(it) }) - from(nativeDesktopDependency.map { zipTree(it) }) + // Integrated runtime packaging must be explicitly requested. + enabled = enableIntegratedNdi.get() + + if (enableIntegratedNdi.get()) { + dependsOn(ndiDesktopDependency) + dependsOn(nativeDesktopDependency) + from(ndiDesktopDependency.map { zipTree(it) }) + from(nativeDesktopDependency.map { zipTree(it) }) + } } val androidAar by tasks.registering(Zip::class) { diff --git a/devolay-natives/build.gradle.kts b/devolay-natives/build.gradle.kts index a2cd901..bae8f09 100644 --- a/devolay-natives/build.gradle.kts +++ b/devolay-natives/build.gradle.kts @@ -240,6 +240,12 @@ fun locateNdiIncludes(): Path { } } +// Integrated NDI runtime packaging is explicitly opt-in. +val enableIntegratedNdi = + providers.gradleProperty("enableIntegratedNdi") + .map { it.toBoolean() } + .orElse(false) + // Add artifacts for devolay-java to depend on val assembleNativeArtifacts by tasks.registering(Jar::class) { archiveBaseName.set("devolay-native-artifacts") @@ -267,7 +273,9 @@ val assembleNativeArtifacts by tasks.registering(Jar::class) { val assembleIntegratedNDIArtifacts by tasks.registering(Jar::class) { archiveBaseName.set("ndi-lib-artifacts") destinationDirectory.set(temporaryDir) + enabled = enableIntegratedNdi.get() + if (enableIntegratedNdi.get()) { components.withType(ComponentWithBinaries::class).forEach { component -> (component as ComponentWithBinaries).binaries.whenElementFinalized(ComponentWithOutputs::class.java) { if (this is ComponentWithNativeRuntime && this.isOptimized) { @@ -369,6 +377,7 @@ val assembleIntegratedNDIArtifacts by tasks.registering(Jar::class) { } } } + } } val assembleAndroidArtifacts by tasks.registering(Copy::class) { From 84ce601cd269257ad4be159d72d9635d85cfb3b9 Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 17:30:32 -0300 Subject: [PATCH 2/7] Add integrated NDI SDK resolver and CI guard --- devolay-natives/build.gradle.kts | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/devolay-natives/build.gradle.kts b/devolay-natives/build.gradle.kts index bae8f09..f894eb2 100644 --- a/devolay-natives/build.gradle.kts +++ b/devolay-natives/build.gradle.kts @@ -240,12 +240,71 @@ fun locateNdiIncludes(): Path { } } +// Resolve a complete NDI SDK for optional integrated packaging. +// Vendored MIT headers are intentionally not considered a complete SDK here. +fun locateIntegratedNdiSdkRoot(platform: String): Path? { + val explicitProperty = System.getProperty("ndiSdk") + if (!explicitProperty.isNullOrBlank()) { + return file(explicitProperty).toPath() + } + + val environmentPath = System.getenv("NDI_SDK_DIR") + if (!environmentPath.isNullOrBlank()) { + return file(environmentPath).toPath() + } + + if (platform == "macos" && + file("/Library/NDI SDK for Apple").exists()) { + return file("/Library/NDI SDK for Apple").toPath() + } + + val checkoutCandidate = when (platform) { + "windows" -> file("../NDI SDK for Windows").toPath() + "macos" -> file("../NDI SDK for Apple").toPath() + "linux" -> file("../NDI SDK for Linux").toPath() + else -> null + } + + return checkoutCandidate?.takeIf { Files.exists(it) } +} + +fun requireIntegratedNdiFile( + path: Path, + description: String, + platform: String, + architecture: String): Path { + if (!Files.exists(path) || !Files.isRegularFile(path)) { + throw GradleException( + "Integrated NDI packaging requested, but $description was not found at " + + "$path for $platform/$architecture.") + } + return path +} + // Integrated NDI runtime packaging is explicitly opt-in. val enableIntegratedNdi = providers.gradleProperty("enableIntegratedNdi") .map { it.toBoolean() } .orElse(false) +// Public CI must not package proprietary NDI runtime binaries accidentally. +// Controlled/private CI may override this guard explicitly. +val allowIntegratedNdiInCi = + providers.gradleProperty("allowIntegratedNdiInCi") + .map { it.toBoolean() } + .orElse(false) + +val runningInCi = + System.getenv("CI")?.equals("true", ignoreCase = true) == true + +if (enableIntegratedNdi.get() && + runningInCi && + !allowIntegratedNdiInCi.get()) { + throw GradleException( + "Integrated NDI packaging is disabled in CI by default. " + + "Use -PallowIntegratedNdiInCi=true only in a controlled environment.") +} + // Add artifacts for devolay-java to depend on val assembleNativeArtifacts by tasks.registering(Jar::class) { archiveBaseName.set("devolay-native-artifacts") From 1433f60faccf47e66c5ed79dca7df3dd8aae3c54 Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 17:41:31 -0300 Subject: [PATCH 3/7] Modernize integrated NDI packaging for 64-bit desktop --- devolay-natives/build.gradle.kts | 230 +++++++++++++++++++------------ 1 file changed, 141 insertions(+), 89 deletions(-) diff --git a/devolay-natives/build.gradle.kts b/devolay-natives/build.gradle.kts index f894eb2..baced6e 100644 --- a/devolay-natives/build.gradle.kts +++ b/devolay-natives/build.gradle.kts @@ -335,108 +335,160 @@ val assembleIntegratedNDIArtifacts by tasks.registering(Jar::class) { enabled = enableIntegratedNdi.get() if (enableIntegratedNdi.get()) { - components.withType(ComponentWithBinaries::class).forEach { component -> - (component as ComponentWithBinaries).binaries.whenElementFinalized(ComponentWithOutputs::class.java) { - if (this is ComponentWithNativeRuntime && this.isOptimized) { - val machine = this.targetMachine - - var nativeLibPath: Path? = null; - val nativeLicensePaths: MutableList = mutableListOf(); - var nativeLibName: String? = null; - - // Skip android binaries because we package them separately - if (machine.operatingSystemFamily.name == "android") { - return@whenElementFinalized - } - - if (machine.operatingSystemFamily.name == "windows") { - nativeLibName = "ndi.dll" - when (machine.architecture.name) { - "x86" -> { - nativeLibPath = file("../NDI SDK for Windows/Bin/x86/Processing.NDI.Lib.x86.dll").toPath() - nativeLicensePaths.add(file("../NDI SDK for Windows/Bin/x86/Processing.NDI.Lib.Licenses.txt").toPath()) - } - "x86-64" -> { - nativeLibPath = file("../NDI SDK for Windows/Bin/x64/Processing.NDI.Lib.x64.dll").toPath() - nativeLicensePaths.add(file("../NDI SDK for Windows/Bin/x64/Processing.NDI.Lib.Licenses.txt").toPath()) - } + components.withType(ComponentWithBinaries::class).forEach { component -> + (component as ComponentWithBinaries).binaries.whenElementFinalized(ComponentWithOutputs::class.java) { + if (this is ComponentWithNativeRuntime && this.isOptimized) { + val machine = this.targetMachine + val platform = machine.operatingSystemFamily.name + val architecture = machine.architecture.name + + // Android has its own packaging path below. + if (platform == "android") { + return@whenElementFinalized } - } else if (machine.operatingSystemFamily.name == "macos") { - nativeLibName = "libndi.dylib" - - val ndiSdkRoot: Path? = when { - System.getProperty("ndiSdk") != null -> - file(System.getProperty("ndiSdk")).toPath() - System.getenv("NDI_SDK_DIR") != null -> - file(System.getenv("NDI_SDK_DIR")).toPath() - - OperatingSystem.current().isMacOsX && - file("/Library/NDI SDK for Apple").exists() -> - file("/Library/NDI SDK for Apple").toPath() + // Modern integrated desktop support is intentionally 64-bit. + // Legacy 32-bit native targets remain available to the normal + // Devolay build, but are not packaged with an NDI runtime. + val supportedIntegratedTarget = + (platform == "windows" && + architecture == "x86-64") || + (platform == "macos" && + (architecture == "x86-64" || + architecture == "aarch64")) || + (platform == "linux" && + architecture == "x86-64") + + if (!supportedIntegratedTarget) { + return@whenElementFinalized + } - file("../NDI SDK for Apple").exists() -> - file("../NDI SDK for Apple").toPath() + val ndiSdkRoot = + locateIntegratedNdiSdkRoot(platform) + ?: throw GradleException( + "Integrated NDI packaging requested for " + + "$platform/$architecture, but no complete " + + "NDI SDK was found. Set -DndiSdk= or " + + "NDI_SDK_DIR=.") + + val (nativeLibName, nativeLibPath, nativeLicensePath) = + when (platform) { + "windows" -> { + val runtime = + requireIntegratedNdiFile( + ndiSdkRoot.resolve( + "Bin/x64/Processing.NDI.Lib.x64.dll"), + "NDI runtime", + platform, + architecture) + + val license = + requireIntegratedNdiFile( + ndiSdkRoot.resolve( + "Bin/x64/Processing.NDI.Lib.Licenses.txt"), + "NDI runtime license file", + platform, + architecture) + + Triple("ndi.dll", runtime, license) + } + + "macos" -> { + // Current NDI SDK for Apple ships a universal + // libndi.dylib for Intel and Apple Silicon. + val runtime = + requireIntegratedNdiFile( + ndiSdkRoot.resolve( + "lib/macOS/libndi.dylib"), + "NDI runtime", + platform, + architecture) + + val license = + requireIntegratedNdiFile( + ndiSdkRoot.resolve( + "licenses/libndi_licenses.txt"), + "NDI runtime license file", + platform, + architecture) + + Triple("libndi.dylib", runtime, license) + } + + "linux" -> { + val runtimeDirectory = + ndiSdkRoot.resolve( + "lib/x86_64-linux-gnu") + + if (!Files.exists(runtimeDirectory) || + !Files.isDirectory(runtimeDirectory)) { + throw GradleException( + "Integrated NDI packaging requested, but " + + "the NDI runtime directory was not found at " + + "$runtimeDirectory for " + + "$platform/$architecture.") + } + + // Linux SDK distributions may provide libndi.so + // symlinks plus a versioned regular binary. + // Package the actual binary, not a symlink. + val runtime = + Files.newDirectoryStream( + runtimeDirectory).use { entries -> + entries.asSequence() + .firstOrNull { + Files.isRegularFile(it) && + !Files.isSymbolicLink(it) && + it.fileName.toString() + .startsWith("libndi.so") && + Files.size(it) > 10 * 1000 + } + } ?: throw GradleException( + "Integrated NDI packaging requested, but " + + "no regular libndi.so runtime binary " + + "was found in $runtimeDirectory for " + + "$platform/$architecture.") + + val license = + requireIntegratedNdiFile( + ndiSdkRoot.resolve( + "licenses/libndi_licenses.txt"), + "NDI runtime license file", + platform, + architecture) + + Triple("libndi.so", runtime, license) + } + + else -> return@whenElementFinalized + } - else -> null - } + println( + "Adding NDI lib from $nativeLibPath " + + "to integrated build.") - // Current NDI SDK for Apple ships libndi.dylib as a - // universal binary supporting Intel and Apple Silicon. - if (machine.architecture.name == "x86-64" || - machine.architecture.name == "aarch64") { - if (ndiSdkRoot != null) { - nativeLibPath = - ndiSdkRoot.resolve("lib/macOS/libndi.dylib") - nativeLicensePaths.add( - ndiSdkRoot.resolve("licenses/libndi_licenses.txt") - ) + from(nativeLibPath) { + rename { + nativeLibName } + into( + "natives/" + + platform + + "/" + + architecture) } - } else if (machine.operatingSystemFamily.name == "linux") { - nativeLibName = "libndi.so" - nativeLicensePaths.add(file("../NDI SDK for Linux/licenses/libndi_licenses.txt").toPath()) - // The linux libs have 2 symlinks and 1 regular file in the lib folder, find the regular file - var nativeLibParentPath: Path? = null; - when (machine.architecture.name) { - "x86" -> { - nativeLibParentPath = file("../NDI SDK for Linux/lib/i686-linux-gnu").toPath() - } - "x86-64" -> { - nativeLibParentPath = file("../NDI SDK for Linux/lib/x86_64-linux-gnu").toPath() - } - } - if (nativeLibParentPath != null && Files.exists(nativeLibParentPath)) { - nativeLibPath = Files.walk(nativeLibParentPath).filter { - Files.isRegularFile(it) && Files.size(it) > 10 * 1000 - }.findFirst().orElse(null) - } - } - if (nativeLibPath != null) { - if (Files.exists(nativeLibPath)) { - println("Adding NDI lib from " + nativeLibPath.toString() + " to integrated build.") - from(nativeLibPath!!) { - rename { - nativeLibName!! - } - into("natives/" + machine.operatingSystemFamily.name + "/" + machine.architecture.name) - } - nativeLicensePaths.forEach { - from(it) { - into("natives/" + machine.operatingSystemFamily.name + "/" + machine.architecture.name) - } - } - } else { - System.err.println("Could not find NDI lib in expected location (" + nativeLibPath.toString() + ") for OS \"" + machine.operatingSystemFamily.name + "\" and arch \"" + machine.architecture.name + "\". No integrated builds available."); + from(nativeLicensePath) { + into( + "natives/" + + platform + + "/" + + architecture) } - } else { - System.err.println("No NDI path specified for OS \"" + machine.operatingSystemFamily.name + "\" and arch \"" + machine.architecture.name + "\", no integrated builds available.") } } } } - } } val assembleAndroidArtifacts by tasks.registering(Copy::class) { From 23ccfcec2f4adb9a2bc1c646632ffbfe069a2eac Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 18:03:36 -0300 Subject: [PATCH 4/7] Document NDI distribution and release policy --- README.md | 68 +++++++++----- THIRD_PARTY_NOTICES.md | 70 +++++++++++++++ docs/RELEASING.md | 196 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 313 insertions(+), 21 deletions(-) create mode 100644 THIRD_PARTY_NOTICES.md create mode 100644 docs/RELEASING.md diff --git a/README.md b/README.md index a632544..f7d41ec 100644 --- a/README.md +++ b/README.md @@ -118,27 +118,47 @@ The same JNI build was also validated against an installed NDI 6.0.1 runtime, de The original Devolay architecture supported an `integrated` artifact containing both Devolay JNI binaries and NDI runtime binaries. -This fork preserves the ability to generate integrated builds locally. +This fork preserves that capability as an explicit opt-in for local development, testing, and controlled application packaging. -On macOS, integrated build discovery supports: +Integrated packaging must be requested explicitly: + +```bash +./gradlew -PenableIntegratedNdi=true :devolay-java:integratedJar +``` + +A complete NDI SDK is resolved in this order: ```text --DndiSdk -NDI_SDK_DIR -/Library/NDI SDK for Apple -checkout-local NDI SDK for Apple +-DndiSdk= +NDI_SDK_DIR= +recognized platform installation, where available +checkout-local SDK fallback ``` -The current universal NDI macOS library can be packaged for both: +The maintained 64-bit desktop integrated targets are: + +| Platform | Architecture | Status | +|---|---|---| +| macOS | x86-64 | validated with NDI SDK 6.3.2 | +| macOS | aarch64 / Apple Silicon | validated with NDI SDK 6.3.2 | +| Windows | x86-64 | supported by the integrated build configuration; platform validation pending | +| Linux | x86-64 | supported by the integrated build configuration; platform validation pending | + +Legacy 32-bit native targets remain part of the inherited Devolay build configuration, but this fork does not promote them as maintained integrated NDI targets. + +When integrated packaging is requested, both the NDI runtime binary and its accompanying license file are required. If either is missing, the build fails instead of producing an incomplete integrated artifact. + +Integrated NDI packaging is disabled in CI by default. A deliberately controlled environment may override that guard with: ```text -macos/x86-64 -macos/aarch64 +-PallowIntegratedNdiInCi=true ``` -Integrated builds are intended for controlled application development and packaging. +Android follows a separate packaging path and is not covered by this desktop integrated-build policy. + +**This fork does not publish integrated NDI runtime binaries to Maven Central or as public GitHub Actions artifacts.** -**This fork does not publish integrated NDI runtime binaries to Maven Central.** +The public Maven artifact remains runtime-separated. See the licensing section below. @@ -343,7 +363,7 @@ For macOS: ```bash export NDI_SDK_DIR="/Library/NDI SDK for Apple" -./gradlew :devolay-java:integratedJar +./gradlew -PenableIntegratedNdi=true :devolay-java:integratedJar ``` The resulting artifact is generated under: @@ -451,26 +471,32 @@ The NDI SDK documentation permits header files to be included in open-source pro NDI runtime binaries are **not** licensed under the Devolay Apache License. -They remain subject to the NDI SDK License Agreement and applicable third-party license terms. +They remain subject to the current NDI SDK License Agreement, NDI distribution requirements, and applicable third-party license terms. + +This fork deliberately keeps its public library distribution runtime-separated: + +- public Maven artifacts do not contain NDI runtime binaries; +- integrated NDI packaging is explicit opt-in; +- integrated packaging requires the accompanying NDI license file; +- integrated packaging is disabled in CI by default; +- integrated artifacts are not published to Maven Central or as public GitHub Actions artifacts. -The public Maven Central artifacts produced by this fork do not redistribute those runtime binaries. +The local integrated build is retained for development, testing, and controlled application-packaging workflows. -Local integrated builds may contain NDI binaries obtained from a locally installed SDK. Anyone distributing an application containing those binaries is responsible for complying with the current NDI SDK License Agreement, redistribution requirements, trademark requirements, and applicable third-party rights. +Anyone distributing an application that contains NDI runtime binaries is responsible for reviewing and complying with the current NDI SDK License Agreement, software-distribution requirements, identification requirements, trademark requirements, and applicable third-party rights. -Refer to the current documentation and license materials distributed by NDI before redistributing an integrated application. +NDI licensing and distribution requirements may change independently of Devolay. Review the current official NDI materials before releasing an application containing NDI runtime binaries. ## NDI trademark requirements Applications using NDI should follow the current identification and trademark requirements published by Vizrt NDI AB. -The official NDI information and developer resources are available at: - -```text -ndi.video -``` +Official NDI information, SDK downloads, licensing materials, and developer resources are available at [ndi.video](https://ndi.video/). NDI® is a registered trademark of Vizrt NDI AB. +Devolay and this community-maintained fork are independent projects and are not affiliated with or endorsed by Vizrt NDI AB. + ## Original project This repository is derived from and remains technically indebted to the original Devolay project by Walker Knapp: diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..ed98868 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,70 @@ +# Third-Party Notices + +This document summarizes third-party licensing and distribution considerations relevant to the community-maintained Devolay fork. + +## Devolay + +Devolay was originally created by Walker Knapp. + +The Devolay source code is distributed under the Apache License 2.0. The original project history, copyright notices, attribution, and license are preserved by this fork. + +See `LICENSE` for the complete Apache License 2.0 text. + +## NDI SDK headers + +This repository contains selected NDI SDK header files required to compile the open-source JNI binding. + +Those files retain the copyright and license notices supplied with them by Vizrt NDI AB. The vendored headers that permit open-source redistribution retain their applicable MIT license notices in the files themselves. + +Those header licenses do not place the proprietary NDI runtime or other NDI SDK components under the Devolay Apache License. + +## NDI runtime binaries + +NDI runtime binaries are proprietary components of the NDI SDK and are not licensed under the Devolay Apache License. + +Examples include platform runtime libraries such as: + +- `Processing.NDI.Lib.x64.dll` +- `libndi.dylib` +- `libndi.so` + +NDI runtime binaries are not stored in this repository and are not included in the standard public Maven artifact produced by this fork. + +The standard Devolay distribution uses runtime-separated loading, allowing the NDI Runtime to be installed or supplied independently according to the applicable NDI terms. + +## Optional integrated packaging + +This fork preserves the historical Devolay integrated-build capability for local development, testing, and controlled application packaging. + +Integrated NDI packaging is explicit opt-in through the Gradle property `enableIntegratedNdi`. + +The integrated build resolves a complete NDI SDK supplied or installed by the developer and requires both the platform runtime binary and its accompanying NDI license file. + +The maintained integrated desktop configuration targets: + +- macOS x86-64; +- macOS aarch64 / Apple Silicon; +- Windows x86-64; +- Linux x86-64. + +macOS x86-64 and Apple Silicon packaging have been directly validated with NDI SDK 6.3.2. Windows x86-64 and Linux x86-64 remain subject to platform-specific validation. + +Integrated packaging is disabled in CI by default. Controlled CI may override this guard explicitly using `allowIntegratedNdiInCi`. + +This fork does not publish integrated NDI runtime binaries to Maven Central or as public GitHub Actions artifacts. + +## Application distribution + +The NDI SDK License Agreement and SDK documentation govern distribution of applications containing NDI runtime binaries. + +A developer or distributor using the local integrated build is responsible for ensuring that the resulting application satisfies the current NDI SDK License Agreement, software-distribution requirements, identification requirements, trademark requirements, applicable third-party licenses, and any required end-user license terms. + +NDI licensing requirements may change independently of this project. The current NDI materials should therefore be reviewed before every product release. + +Official information is available at https://ndi.video/. + +## Trademark + +NDI® is a registered trademark of Vizrt NDI AB. + +Devolay and this community-maintained fork are independent projects and are not affiliated with or endorsed by Vizrt NDI AB. diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..df4761f --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,196 @@ +# Devolay Release Checklist + +This checklist applies to releases of the community-maintained Devolay fork. + +The public distribution model is runtime-separated by default. + +The normal Maven artifact contains the Devolay Java API and Devolay JNI native libraries. It must not contain proprietary NDI runtime binaries. + +## 1. Verify repository state + +Confirm that the release branch is current and the working tree is clean. + +Run `git status` and review the recent commit history before beginning a release. + +## 2. Verify current NDI SDK and licensing + +Before every public release: + +1. Check the current NDI SDK version at the official NDI developer site. +2. Review the current NDI SDK License Agreement. +3. Review the current SDK software-distribution requirements. +4. Review current NDI identification and trademark requirements. +5. Compare the current SDK with the headers vendored by Devolay. + +Do not assume that conclusions made for an earlier release remain valid for a later NDI SDK or license revision. + +The NDI SDK License Agreement requires product releases to use a sufficiently current SDK when a newer version is available. The current official materials must therefore be checked as part of every release. + +## 3. Verify normal build separation + +Run: + + ./gradlew clean build + +The normal build must succeed without discovering or adding an NDI runtime to an integrated artifact. + +Messages indicating that `libndi`, `Processing.NDI.Lib`, or another NDI runtime is being added during a normal build are release blockers. + +## 4. Verify Maven Local publication + +Run: + + ./gradlew clean :devolay-java:publishDevolayPublicationToMavenLocal + +Inspect the generated standard artifact. + +It must contain the applicable Devolay JNI native libraries. + +It must not contain: + +- `libndi.dylib`; +- `libndi.so`; +- `Processing.NDI.Lib.x64.dll`; +- `ndi.dll`; +- an integrated NDI classifier. + +## 5. Verify external Maven consumer + +Create or use an independent consumer project resolving: + + io.github.vicvalentim:devolay: + +The consumer test should verify that it can: + +1. resolve Devolay from Maven Local; +2. load the appropriate Devolay JNI library; +3. load an independently installed NDI Runtime; +4. report the NDI runtime version; +5. create a `DevolaySender`. + +This test must not depend on the Devolay repository's build classpath. + +## 6. Verify optional integrated packaging + +Integrated packaging must remain explicit opt-in. + +Without the flag: + + ./gradlew clean :devolay-java:integratedJar + +no integrated JAR should be produced. + +With explicit opt-in: + + ./gradlew clean -PenableIntegratedNdi=true :devolay-java:integratedJar + +the integrated artifact may be generated from a complete locally installed or explicitly supplied NDI SDK. + +Both the runtime binary and corresponding NDI license file are mandatory. Missing either must fail the build. + +## 7. Verify integrated CI guard + +Integrated NDI packaging must fail in CI by default. + +A deliberately controlled/private environment may enable it with both: + + -PenableIntegratedNdi=true + -PallowIntegratedNdiInCi=true + +The override must never be added to ordinary public publication workflows. + +## 8. Desktop integrated target status + +The maintained 64-bit desktop integrated configuration covers: + +- macOS x86-64; +- macOS aarch64 / Apple Silicon; +- Windows x86-64; +- Linux x86-64. + +A target must not be documented as validated until it has been tested on the corresponding operating system with a current NDI SDK. + +Legacy 32-bit native Devolay targets do not imply maintained 32-bit integrated NDI support. + +Android follows a separate build and packaging path and must be reviewed separately before any public Android artifact is introduced. + +## 9. macOS validation + +On Apple Silicon, verify the current architecture with `uname -m`. + +Build the Apple Silicon target and run the sender example against the current NDI Runtime. + +Confirm actual discovery and reception with an NDI receiver such as NDI Monitor. + +When testing the integrated build, inspect the resulting JAR and confirm the expected macOS Intel and Apple Silicon runtime, license, and Devolay JNI entries. + +## 10. Windows and Linux validation + +Before claiming validated integrated support for Windows x86-64 or Linux x86-64: + +1. use a complete current NDI SDK for that platform; +2. run the integrated build on that operating system; +3. inspect the resulting package; +4. run an actual Devolay sender or receiver; +5. verify communication with another NDI application; +6. record the SDK version used for validation. + +Do not substitute cross-compilation success for runtime validation. + +## 11. Maven Central staging + +Run the Maven Central staging workflow before any public release upload. + +The staging pipeline must verify: + +- Linux native build; +- Windows native build; +- macOS Intel native build; +- macOS Apple Silicon native build; +- universal public native artifact assembly; +- source and Javadoc artifacts; +- POM validation; +- signatures; +- Maven Central/JReleaser validation; +- absence of proprietary NDI runtime binaries. + +Any validation failure is a release blocker. + +## 12. Signing + +Confirm that the release signing key is valid, unexpired, correctly configured in GitHub Actions, and discoverable by the required validation infrastructure. + +Never commit signing keys, private-key material, passwords, passphrases, or credentials to the repository. + +## 13. Maven Central credentials + +Maven Central publication credentials must remain in GitHub Actions secrets or protected environment secrets. + +Central User Tokens, bearer tokens, usernames, passwords, and signing credentials must never be stored in repository files. + +Keep public Central upload workflows disabled while preparing or auditing a release. + +## 14. Final release gate + +Before enabling a public Maven Central upload, confirm all of the following: + +- repository build passes; +- working tree and release commit are known; +- current NDI SDK version has been checked; +- current NDI licensing and distribution documentation has been reviewed; +- normal build performs no integrated NDI discovery; +- standard Maven artifact contains no NDI runtime binary; +- integrated packaging remains explicit opt-in; +- missing integrated runtime fails the build; +- missing integrated license file fails the build; +- integrated CI guard is active; +- README licensing information is current; +- `THIRD_PARTY_NOTICES.md` is current; +- Maven Local publication passes; +- external consumer test passes; +- applicable runtime tests pass; +- Central staging validation passes; +- signing is valid; +- Maven Central credentials are current. + +Only after all release gates pass should the Maven Central release workflow be enabled. From 07e0a957e55b440963ab27f5c044df21476a6cc8 Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 18:26:39 -0300 Subject: [PATCH 5/7] Add Apache-2.0 modification notices --- .github/workflows/g++-faker.py | 2 ++ .github/workflows/gradle.yml | 2 ++ .gitignore | 2 ++ README.md | 2 ++ build.gradle.kts | 2 ++ devolay-java/build.gradle.kts | 2 ++ devolay-java/src/main/java/me/walkerknapp/devolay/Devolay.java | 2 ++ .../src/main/java/me/walkerknapp/devolay/DevolayFrameSync.java | 2 ++ .../src/main/java/me/walkerknapp/devolay/DevolaySender.java | 2 ++ devolay-natives/build.gradle.kts | 2 ++ devolay-natives/gradle/toolchains.gradle.kts | 2 ++ devolay-natives/src/main/cpp/devolay.cpp | 2 ++ examples/build.gradle | 2 ++ 13 files changed, 26 insertions(+) diff --git a/.github/workflows/g++-faker.py b/.github/workflows/g++-faker.py index e63a67b..09af08a 100755 --- a/.github/workflows/g++-faker.py +++ b/.github/workflows/g++-faker.py @@ -1,4 +1,6 @@ #!/bin/python3 +# This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + import subprocess import sys diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 62faf90..ec2dc42 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,3 +1,5 @@ +# This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + name: Java CI with Gradle concurrency: diff --git a/.gitignore b/.gitignore index ff3ac56..ea08836 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + */build/ /.gradle/ /.idea/ diff --git a/README.md b/README.md index f7d41ec..9119958 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Devolay — Community-Maintained Fork +> **Modification notice:** This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + Devolay is a Java binding for the NDI® SDK, providing access to NDI video, audio, metadata, discovery, sending, and receiving from Java applications through JNI. This repository is a community-maintained fork of the original `WalkerKnapp/devolay` project created by Walker Knapp. It preserves the original Java API and package namespace while modernizing the native build, current NDI compatibility, Apple Silicon support, CI, and Maven publication infrastructure. diff --git a/build.gradle.kts b/build.gradle.kts index e3ded64..86c6833 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + plugins { idea } diff --git a/devolay-java/build.gradle.kts b/devolay-java/build.gradle.kts index 4d814cf..7f384c5 100644 --- a/devolay-java/build.gradle.kts +++ b/devolay-java/build.gradle.kts @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.internal.jvm.Jvm diff --git a/devolay-java/src/main/java/me/walkerknapp/devolay/Devolay.java b/devolay-java/src/main/java/me/walkerknapp/devolay/Devolay.java index 15d0384..e252f84 100644 --- a/devolay-java/src/main/java/me/walkerknapp/devolay/Devolay.java +++ b/devolay-java/src/main/java/me/walkerknapp/devolay/Devolay.java @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + package me.walkerknapp.devolay; import java.io.IOException; diff --git a/devolay-java/src/main/java/me/walkerknapp/devolay/DevolayFrameSync.java b/devolay-java/src/main/java/me/walkerknapp/devolay/DevolayFrameSync.java index a76f454..ff42386 100644 --- a/devolay-java/src/main/java/me/walkerknapp/devolay/DevolayFrameSync.java +++ b/devolay-java/src/main/java/me/walkerknapp/devolay/DevolayFrameSync.java @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + package me.walkerknapp.devolay; public class DevolayFrameSync extends DevolayFrameCleaner implements AutoCloseable { diff --git a/devolay-java/src/main/java/me/walkerknapp/devolay/DevolaySender.java b/devolay-java/src/main/java/me/walkerknapp/devolay/DevolaySender.java index b0b3e50..d1144b7 100644 --- a/devolay-java/src/main/java/me/walkerknapp/devolay/DevolaySender.java +++ b/devolay-java/src/main/java/me/walkerknapp/devolay/DevolaySender.java @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + package me.walkerknapp.devolay; /** diff --git a/devolay-natives/build.gradle.kts b/devolay-natives/build.gradle.kts index baced6e..8fabb2d 100644 --- a/devolay-natives/build.gradle.kts +++ b/devolay-natives/build.gradle.kts @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + import de.undercouch.gradle.tasks.download.Download import org.gradle.internal.jvm.Jvm import org.gradle.internal.os.OperatingSystem diff --git a/devolay-natives/gradle/toolchains.gradle.kts b/devolay-natives/gradle/toolchains.gradle.kts index 1f12be5..e714358 100644 --- a/devolay-natives/gradle/toolchains.gradle.kts +++ b/devolay-natives/gradle/toolchains.gradle.kts @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + import org.gradle.internal.os.OperatingSystem import org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath import org.gradle.nativeplatform.toolchain.internal.ToolType diff --git a/devolay-natives/src/main/cpp/devolay.cpp b/devolay-natives/src/main/cpp/devolay.cpp index ad97cc3..0ec7658 100644 --- a/devolay-natives/src/main/cpp/devolay.cpp +++ b/devolay-natives/src/main/cpp/devolay.cpp @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + #include "devolay.h" #include diff --git a/examples/build.gradle b/examples/build.gradle index 45cff4a..89d90db 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -1,3 +1,5 @@ +// This file has been modified from the original WalkerKnapp/devolay version by the vicvalentim/devolay community-maintained fork (2026). + plugins { id 'java' id 'application' From 1135bc76acd07393348fc4d1ed1bf1422f734e44 Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 18:36:20 -0300 Subject: [PATCH 6/7] Simplify CI and remove legacy publishing workflow --- .github/workflows/gradle-publish.yml | 44 ----- .github/workflows/gradle.yml | 236 +++++++++++++++++---------- 2 files changed, 150 insertions(+), 130 deletions(-) delete mode 100644 .github/workflows/gradle-publish.yml diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml deleted file mode 100644 index 00ddf1b..0000000 --- a/.github/workflows/gradle-publish.yml +++ /dev/null @@ -1,44 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created -# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle - -name: Gradle Package - -on: - release: - types: [created] - -jobs: - build: - - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - server-id: github # Value of the distributionManagement/repository/id field of the pom.xml - settings-path: ${{ github.workspace }} # location for the settings.xml file - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - - - name: Build with Gradle - run: ./gradlew build - - # The USERNAME and TOKEN need to correspond to the credentials environment variables used in - # the publishing section of your build.gradle - - name: Publish to GitHub Packages - run: ./gradlew publish - env: - USERNAME: ${{ github.actor }} - TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ec2dc42..6d42c23 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,97 +2,161 @@ name: Java CI with Gradle -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true +on: + push: + pull_request: permissions: contents: read -on: [ push, pull_request ] +concurrency: + group: java-ci-${{ github.ref }} + cancel-in-progress: true + +env: + JAVA_VERSION: "11" + NATIVE_JAR: >- + devolay-natives/build/tmp/assembleNativeArtifacts/devolay-native-artifacts.jar jobs: - build: - runs-on: ubuntu-latest + linux-windows: + name: Linux and Windows native build + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + cache: gradle + + - name: Install native toolchains + run: | + set -euo pipefail + + sudo apt-get update + sudo apt-get install -y \ + gcc-multilib \ + g++-multilib \ + mingw-w64 \ + unzip + + chmod +x gradlew + chmod +x .github/workflows/g++-faker.py + + sudo ln -sf \ + "$GITHUB_WORKSPACE/.github/workflows/g++-faker.py" \ + /usr/bin/i686-w64-mingw32-g++-faker + + sudo ln -sf \ + "$GITHUB_WORKSPACE/.github/workflows/g++-faker.py" \ + /usr/bin/x86_64-w64-mingw32-g++-faker + + - name: Build Linux and Windows natives + run: | + set -euo pipefail + + ./gradlew \ + --no-daemon \ + :devolay-natives:assembleNativeArtifacts \ + --rerun-tasks + + - name: Validate Linux and Windows natives + run: | + set -euo pipefail + + test -f "$NATIVE_JAR" + + entries="$(jar tf "$NATIVE_JAR")" + printf '%s\n' "$entries" + + expected_directories=( + "natives/linux/x86" + "natives/linux/x86-64" + "natives/windows/x86" + "natives/windows/x86-64" + ) + + for directory in "${expected_directories[@]}"; do + if ! grep -Eq \ + "^${directory}/[^/]+\.(so|dll)$" \ + <<<"$entries"; then + echo "Missing native binary in ${directory}" >&2 + exit 1 + fi + done + + if grep -Eiq \ + 'libndi|integrated|\.dwarf$|\.debug$' \ + <<<"$entries"; then + echo "Forbidden file found in native artifact." >&2 + exit 1 + fi + + macos: + name: macOS and Java build + runs-on: macos-15 steps: - - name: Install Dependencies - run: sudo apt update && sudo apt install -y mingw-w64 bison flex texinfo unzip help2man libtool-bin libncurses5-dev libncursesw5-dev p7zip-full cmake python3 - - - name: Install innoextract - run: wget https://constexpr.org/innoextract/files/innoextract-1.9-linux.tar.xz && sudo tar --directory=/opt -xvf innoextract-1.9-linux.tar.xz - - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - - - name: Setup Android NDK - id: setup-ndk - uses: nttld/setup-ndk@v1 - with: - ndk-version: r25c - link-to-sdk: true - local-cache: true - - - name: Finish NDK Setup - run: | - cd "$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin" - rm llvm-strip - ln -s llvm-objcopy llvm-strip - rm ld - ln -s lld ld - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - - - name: Set up JDK 11 - uses: actions/setup-java@v5 - with: - java-version: '11' - distribution: 'temurin' - - - uses: actions/checkout@v7 - - - name: Create dummy mingw - run: | - sudo ln -sf "$(pwd)/.github/workflows/g++-faker.py" "/usr/bin/i686-w64-mingw32-g++-faker" - sudo ln -sf "$(pwd)/.github/workflows/g++-faker.py" "/usr/bin/x86_64-w64-mingw32-g++-faker" - - - uses: actions/cache@v4 - id: cachetoolchain - with: - path: osxcross - key: ${{ runner.os }}-osxtoolchain - - # Setup osxcross, as detailed here: https://github.com/andrew-d/docker-osxcross/blob/master/Dockerfile - - name: Setup OSXCross - if: steps.cachetoolchain.outputs.cache-hit != 'true' - run: | - git clone -n https://github.com/tpoechtrager/osxcross.git - cd osxcross - git checkout 364703ca0962c4a12688daf8758802a5df9e3221 - sudo apt update - sudo ./tools/get_dependencies.sh - curl -L -o ./tarballs/MacOSX10.15.sdk.tar.xz https://github.com/xorrior/osxsdk/raw/master/MacOSX10.15.sdk.tar.xz - PORTABLE=true UNATTENDED=true ./build.sh - PORTABLE=true UNATTENDED=true ./build_binutils.sh - - name: Add OSXCross to path - run: | - export PATH=$PATH:./osxcross/target/bin - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew publishToMavenLocal "-DandroidNdk=$ANDROID_NDK_HOME" - env: - PGP_KEY: ${{ secrets.PGP_KEY }} - PGP_KEY_ID: ${{ secrets.PGP_KEY_ID }} - PGP_PASSWORD: ${{ secrets.PGP_PASSWORD }} - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - - - name: Upload build artifacts - uses: actions/upload-artifact@v7 - with: - name: build-artifacts - path: devolay-java/build/libs + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + cache: gradle + + - name: Build + run: | + set -euo pipefail + + chmod +x gradlew + + ./gradlew \ + --no-daemon \ + clean \ + build \ + 2>&1 | tee build.log + + - name: Verify runtime-separated build + run: | + set -euo pipefail + + if grep -Eq \ + 'Adding NDI lib|Could not find NDI lib|No NDI path specified' \ + build.log; then + echo "Unexpected integrated NDI activity in normal build." >&2 + exit 1 + fi + + test -f "$NATIVE_JAR" + + entries="$(jar tf "$NATIVE_JAR")" + printf '%s\n' "$entries" + + expected_directories=( + "natives/macos/x86-64" + "natives/macos/aarch64" + ) + + for directory in "${expected_directories[@]}"; do + if ! grep -Eq \ + "^${directory}/[^/]+\.dylib$" \ + <<<"$entries"; then + echo "Missing native binary in ${directory}" >&2 + exit 1 + fi + done + + if grep -Eiq \ + 'libndi|integrated|\.dwarf$|\.debug$' \ + <<<"$entries"; then + echo "Forbidden file found in native artifact." >&2 + exit 1 + fi From d3a16addc66ca3beb1916206458ea1e5a8d7e881 Mon Sep 17 00:00:00 2001 From: zivito Date: Fri, 7 Aug 2026 18:56:07 -0300 Subject: [PATCH 7/7] Clarify build requirements and staging policy --- .github/workflows/central-staging.yml | 15 --------------- README.md | 6 ++++-- docs/RELEASING.md | 2 ++ 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/central-staging.yml b/.github/workflows/central-staging.yml index c395957..ba99add 100644 --- a/.github/workflows/central-staging.yml +++ b/.github/workflows/central-staging.yml @@ -3,21 +3,6 @@ name: Maven Central staging on: workflow_dispatch: - push: - branches: - - feature/central-publishing - paths: - - ".github/workflows/central-staging.yml" - - '.github/workflows/g\+\+-faker.py' - - "build.gradle.kts" - - "settings.gradle.kts" - - "gradle/**" - - "gradlew" - - "gradlew.bat" - - "devolay-java/**" - - "devolay-natives/**" - - "jreleaser.yml" - pull_request: paths: - ".github/workflows/central-staging.yml" diff --git a/README.md b/README.md index 9119958..a2a45b2 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ The fork modernizes several parts of the original build infrastructure, includin - current GitHub Actions versions; - Maven Central Publisher API support; - bearer-token authentication; -- platform-specific native build jobs; +- multi-platform native build jobs; - universal desktop native artifact assembly; - source and Javadoc publication; - Gradle Module Metadata; @@ -325,7 +325,9 @@ C/C++ toolchain appropriate for the target platform The project retains Java 8 source compatibility. -The CI build currently uses Java 11, and Apple Silicon validation has also been performed with Java 17. +The repository currently uses the WalkerKnapp Gradle 7.2cc wrapper for its native build toolchain. Run this wrapper with JDK 11. Java 17 is supported for running and validating Devolay applications, but it is not the supported build JVM for the current Gradle wrapper. + +The CI build therefore uses JDK 11. Apple Silicon runtime validation has also been performed with Java 17. ### Standard build diff --git a/docs/RELEASING.md b/docs/RELEASING.md index df4761f..45243a8 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -6,6 +6,8 @@ The public distribution model is runtime-separated by default. The normal Maven artifact contains the Devolay Java API and Devolay JNI native libraries. It must not contain proprietary NDI runtime binaries. +The current repository uses the WalkerKnapp Gradle 7.2cc wrapper for native build support. Release and verification commands in this checklist must be run with JDK 11 unless the Gradle/native build infrastructure is explicitly upgraded in a later release. This build-JVM requirement does not change Devolay's Java 8 source compatibility. + ## 1. Verify repository state Confirm that the release branch is current and the working tree is clean.