[misc] Share one license collection script across the UBI images (#8055)

* [self-hosted] Add a UBI image variant for the combined server

OpenShift and other Red Hat environments expect UBI-based images that run
as an arbitrary non-root UID. The proxy and rootless client already ship
-ubi variants; this adds the same for netbird-server, published as
<version>-ubi and ubi-latest for amd64 and arm64.

* [self-hosted] Check the license output path before creating temp files

The existing-output exit ran before the cleanup trap was registered, so it
left the two mktemp files behind.

* [self-hosted] Certify the netbird-server UBI image

Adds netbird-server to the Red Hat certification components. Its Partner
Connect component ID goes in the REDHAT_CERT_ID_NETBIRD_SERVER repository
variable.

* [misc] Share one license collection script across the UBI images

The client, proxy and combined images each carried a near-identical copy of
collect-licenses.sh, and the signal and relay variants would add two more.
The copies differed only in the Go package, build tags, component license
and the proxy's web licenses, which are now options of one script in
release_files/. The client gains the staged write the others already had.
This commit is contained in:
Nicolas Frati
2026-10-06 10:25:57 +02:00
committed by GitHub
parent d6340ba0de
commit ab79aebd88
5 changed files with 41 additions and 172 deletions
+1 -1
View File
@@ -199,7 +199,7 @@ jobs:
with:
node-version: '22'
- name: Install proxy web dependencies for license collection
# proxy/collect-licenses.sh reads the UI's license terms from node_modules.
# release_files/collect-licenses.sh -w reads the proxy UI's license terms from node_modules.
working-directory: proxy/web
run: npm ci --ignore-scripts
- name: Set up QEMU
+3 -3
View File
@@ -385,7 +385,7 @@ dockers_v2:
RELEASE: "{{ .Timestamp }}"
hooks:
pre:
- cmd: 'sh client/collect-licenses.sh "{{ .ContextDir }}/licenses" amd64 arm64'
- cmd: 'sh release_files/collect-licenses.sh -t load_wgnt_from_rsrc "{{ .ContextDir }}/licenses" ./client amd64 arm64'
env:
- GOOS=linux
- CGO_ENABLED=0
@@ -530,7 +530,7 @@ dockers_v2:
RELEASE: "{{ .Timestamp }}"
hooks:
pre:
- cmd: 'sh combined/collect-licenses.sh "{{ .ContextDir }}/licenses" amd64 arm64'
- cmd: 'sh release_files/collect-licenses.sh -l combined/LICENSE "{{ .ContextDir }}/licenses" ./combined amd64 arm64'
env:
- GOOS=linux
- CGO_ENABLED=1
@@ -587,7 +587,7 @@ dockers_v2:
RELEASE: "{{ .Timestamp }}"
hooks:
pre:
- cmd: 'sh proxy/collect-licenses.sh "{{ .ContextDir }}/licenses" amd64 arm64'
- cmd: 'sh release_files/collect-licenses.sh -l proxy/LICENSE -w "{{ .ContextDir }}/licenses" ./proxy/cmd/proxy amd64 arm64'
env:
- GOOS=linux
- CGO_ENABLED=0
-77
View File
@@ -1,77 +0,0 @@
#!/bin/sh
set -eu
if [ "$#" -lt 2 ]; then
printf '%s\n' "usage: $0 OUTPUT_DIRECTORY GOARCH..." >&2
exit 2
fi
repo_root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
output_name=$(basename "$1")
if [ -z "$output_name" ] || [ "$output_name" = "." ] ||
[ "$output_name" = ".." ] || [ "$output_name" = "/" ]; then
printf '%s\n' "OUTPUT_DIRECTORY must name a directory" >&2
exit 2
fi
output_parent=$(CDPATH= cd -- "$(dirname "$1")" && pwd)
output="$output_parent/$output_name"
shift
modules=$(mktemp "${TMPDIR:-/tmp}/netbird-client-licenses.modules.XXXXXX")
sorted_modules=$(mktemp "${TMPDIR:-/tmp}/netbird-client-licenses.sorted.XXXXXX")
trap 'rm -f "$modules" "$sorted_modules"' EXIT HUP INT TERM
if [ -e "$output" ] || [ -L "$output" ]; then
printf 'output directory already exists: %s\n' "$output" >&2
exit 1
fi
mkdir "$output"
mkdir "$output/third_party"
cp "$repo_root/LICENSE" "$output/BSD-3-Clause.txt"
cd "$repo_root"
for arch in "$@"; do
GOOS=${GOOS:-linux} GOARCH="$arch" CGO_ENABLED=${CGO_ENABLED:-0} \
go list -deps -f '{{with .Module}}{{if .Replace}}{{.Replace.Path}}{{"\t"}}{{.Replace.Version}}{{"\t"}}{{.Replace.Dir}}{{else}}{{.Path}}{{"\t"}}{{.Version}}{{"\t"}}{{.Dir}}{{end}}{{end}}' -tags load_wgnt_from_rsrc ./client >>"$modules"
done
LC_ALL=C sort -u "$modules" >"$sorted_modules"
goroot=$(go env GOROOT)
for term in LICENSE PATENTS; do
if [ ! -f "$goroot/$term" ]; then
printf 'missing Go standard-library term: %s\n' "$goroot/$term" >&2
exit 1
fi
cp "$goroot/$term" "$output/Go-$term"
done
while IFS=' ' read -r module version module_dir; do
[ -n "$module" ] || continue
[ "$module" = "github.com/netbirdio/netbird" ] && continue
if [ -z "$version" ] || [ ! -d "$module_dir" ]; then
printf 'cannot collect terms for module %s at version %s\n' "$module" "$version" >&2
exit 1
fi
destination="$output/third_party/$module/$version"
mkdir -p "$destination"
printf 'module: %s\nversion: %s\n' "$module" "$version" >"$destination/MODULE"
found=false
for term in \
"$module_dir"/LICENSE* "$module_dir"/License* "$module_dir"/license* \
"$module_dir"/LICENCE* "$module_dir"/Licence* "$module_dir"/licence* \
"$module_dir"/COPYING* "$module_dir"/Copying* "$module_dir"/copying* \
"$module_dir"/NOTICE* "$module_dir"/Notice* "$module_dir"/notice* \
"$module_dir"/PATENTS* "$module_dir"/Patents* "$module_dir"/patents*; do
[ -f "$term" ] || continue
cp "$term" "$destination/"
found=true
done
if [ "$found" = false ]; then
printf 'no root license terms found for module %s at %s\n' "$module" "$module_dir" >&2
exit 1
fi
done <"$sorted_modules"
-82
View File
@@ -1,82 +0,0 @@
#!/bin/sh
set -eu
if [ "$#" -lt 2 ]; then
printf '%s\n' "usage: $0 OUTPUT_DIRECTORY GOARCH..." >&2
exit 2
fi
repo_root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
output_name=$(basename "$1")
if [ -z "$output_name" ] || [ "$output_name" = . ] || [ "$output_name" = .. ] || [ "$output_name" = / ]; then
printf '%s\n' "OUTPUT_DIRECTORY must name a directory" >&2
exit 2
fi
output_parent=$(CDPATH= cd -- "$(dirname "$1")" && pwd)
output="$output_parent/$output_name"
shift
modules=$(mktemp "${TMPDIR:-/tmp}/netbird-proxy-licenses.modules.XXXXXX")
sorted_modules=$(mktemp "${TMPDIR:-/tmp}/netbird-proxy-licenses.sorted.XXXXXX")
if [ -e "$output" ] || [ -L "$output" ]; then
printf 'output directory already exists: %s\n' "$output" >&2
exit 1
fi
# Assemble beside the target and rename on success, so a failed run leaves
# nothing behind that would block the next attempt.
staging=$(mktemp -d "$output_parent/.$output_name.XXXXXX")
trap 'rm -f "$modules" "$sorted_modules"; rm -rf "$staging"' EXIT HUP INT TERM
mkdir "$staging/third_party"
cp "$repo_root/proxy/LICENSE" "$staging/AGPL-3.0.txt"
cp "$repo_root/LICENSE" "$staging/BSD-3-Clause.txt"
node "$repo_root/proxy/web/scripts/third-party-licenses.mjs" >"$staging/Web-THIRD-PARTY-LICENSES"
cd "$repo_root"
for arch in "$@"; do
GOOS=${GOOS:-linux} GOARCH="$arch" CGO_ENABLED=${CGO_ENABLED:-0} \
go list -deps -f '{{with .Module}}{{if .Replace}}{{.Replace.Path}}{{"\t"}}{{.Replace.Version}}{{"\t"}}{{.Replace.Dir}}{{else}}{{.Path}}{{"\t"}}{{.Version}}{{"\t"}}{{.Dir}}{{end}}{{end}}' ./proxy/cmd/proxy >>"$modules"
done
LC_ALL=C sort -u "$modules" >"$sorted_modules"
goroot=$(go env GOROOT)
for term in LICENSE PATENTS; do
if [ ! -f "$goroot/$term" ]; then
printf 'missing Go standard-library term: %s\n' "$goroot/$term" >&2
exit 1
fi
cp "$goroot/$term" "$staging/Go-$term"
done
while IFS=' ' read -r module version module_dir; do
[ -n "$module" ] || continue
[ "$module" = "github.com/netbirdio/netbird" ] && continue
if [ -z "$version" ] || [ ! -d "$module_dir" ]; then
printf 'cannot collect terms for module %s at version %s\n' "$module" "$version" >&2
exit 1
fi
destination="$staging/third_party/$module/$version"
mkdir -p "$destination"
printf 'module: %s\nversion: %s\n' "$module" "$version" >"$destination/MODULE"
found=false
for term in \
"$module_dir"/LICENSE* "$module_dir"/License* "$module_dir"/license* \
"$module_dir"/LICENCE* "$module_dir"/Licence* "$module_dir"/licence* \
"$module_dir"/COPYING* "$module_dir"/Copying* "$module_dir"/copying* \
"$module_dir"/NOTICE* "$module_dir"/Notice* "$module_dir"/notice* \
"$module_dir"/PATENTS* "$module_dir"/Patents* "$module_dir"/patents*; do
[ -f "$term" ] || continue
cp "$term" "$destination/"
found=true
done
if [ "$found" = false ]; then
printf 'no root license terms found for module %s at %s\n' "$module" "$module_dir" >&2
exit 1
fi
done <"$sorted_modules"
mv "$staging" "$output"
@@ -1,10 +1,32 @@
#!/bin/sh
#
# Collect the license terms shipped in /licenses of the UBI images: NetBird's
# own licenses, the Go standard library terms, and the root license files of
# every module the Go package links on the given architectures.
#
# -l FILE component license, copied as AGPL-3.0.txt (path from repo root)
# -t TAGS build tags used for the dependency walk
# -w add the proxy web UI's third-party licenses (needs proxy/web/node_modules)
set -eu
if [ "$#" -lt 2 ]; then
printf '%s\n' "usage: $0 OUTPUT_DIRECTORY GOARCH..." >&2
usage() {
printf '%s\n' "usage: $0 [-l LICENSE_FILE] [-t TAGS] [-w] OUTPUT_DIRECTORY PACKAGE GOARCH..." >&2
exit 2
fi
}
component_license=""
tags=""
web=false
while getopts l:t:w opt; do
case "$opt" in
l) component_license=$OPTARG ;;
t) tags=$OPTARG ;;
w) web=true ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
[ "$#" -ge 3 ] || usage
repo_root=$(CDPATH='' cd -- "$(dirname "$0")/.." && pwd)
output_name=$(basename "$1")
@@ -14,27 +36,33 @@ if [ -z "$output_name" ] || [ "$output_name" = . ] || [ "$output_name" = .. ] ||
fi
output_parent=$(CDPATH='' cd -- "$(dirname "$1")" && pwd)
output="$output_parent/$output_name"
shift
package=$2
shift 2
if [ -e "$output" ] || [ -L "$output" ]; then
printf 'output directory already exists: %s\n' "$output" >&2
exit 1
fi
modules=$(mktemp "${TMPDIR:-/tmp}/netbird-server-licenses.modules.XXXXXX")
sorted_modules=$(mktemp "${TMPDIR:-/tmp}/netbird-server-licenses.sorted.XXXXXX")
modules=$(mktemp "${TMPDIR:-/tmp}/netbird-licenses.modules.XXXXXX")
sorted_modules=$(mktemp "${TMPDIR:-/tmp}/netbird-licenses.sorted.XXXXXX")
# Assemble beside the target and rename on success, so a failed run leaves
# nothing behind that would block the next attempt.
staging=$(mktemp -d "$output_parent/.$output_name.XXXXXX")
trap 'rm -f "$modules" "$sorted_modules"; rm -rf "$staging"' EXIT HUP INT TERM
mkdir "$staging/third_party"
cp "$repo_root/combined/LICENSE" "$staging/AGPL-3.0.txt"
if [ -n "$component_license" ]; then
cp "$repo_root/$component_license" "$staging/AGPL-3.0.txt"
fi
cp "$repo_root/LICENSE" "$staging/BSD-3-Clause.txt"
if [ "$web" = true ]; then
node "$repo_root/proxy/web/scripts/third-party-licenses.mjs" >"$staging/Web-THIRD-PARTY-LICENSES"
fi
cd "$repo_root"
for arch in "$@"; do
GOOS=${GOOS:-linux} GOARCH="$arch" CGO_ENABLED=${CGO_ENABLED:-1} \
go list -deps -f '{{with .Module}}{{if .Replace}}{{.Replace.Path}}{{"\t"}}{{.Replace.Version}}{{"\t"}}{{.Replace.Dir}}{{else}}{{.Path}}{{"\t"}}{{.Version}}{{"\t"}}{{.Dir}}{{end}}{{end}}' ./combined >>"$modules"
GOOS=${GOOS:-linux} GOARCH="$arch" CGO_ENABLED=${CGO_ENABLED:-0} \
go list -deps -tags "$tags" -f '{{with .Module}}{{if .Replace}}{{.Replace.Path}}{{"\t"}}{{.Replace.Version}}{{"\t"}}{{.Replace.Dir}}{{else}}{{.Path}}{{"\t"}}{{.Version}}{{"\t"}}{{.Dir}}{{end}}{{end}}' "$package" >>"$modules"
done
LC_ALL=C sort -u "$modules" >"$sorted_modules"