Skip to content

Commit 9e52176

Browse files
committed
feat: UI redesign, GitHub Actions CI, security hardening, beta branch support
- Redesign settings page with card-based layout and full Unraid theme support - Add GitHub Actions CI workflow with JSON-driven build matrix (build-matrix.json), replacing the need for Jenkins — supports branches and extra pin versions - Add beta branch support to download.sh, update-check.sh, and exec.sh - Add open-source kernel module builds for all branches (not just production) - Add Blackwell GPU detection with warning when using proprietary modules - Add GPU architecture and CUDA version display in system info - Security: input validation in update_version() and change_update_check() to prevent command injection via sed - Security: function whitelist replacing unrestricted $@ dispatcher in exec.sh - Security: safe file cleanup in download.sh and update-check.sh to prevent accidental mass deletion when variables are empty - Fix: extract FETCH_VERSIONS() to eliminate redundant API call - Fix: $DRIVERS variable scope bug in legacy 580 driver check
1 parent 6316ed0 commit 9e52176

6 files changed

Lines changed: 664 additions & 179 deletions

File tree

.github/workflows/build-nvidia.yml

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
name: Build NVIDIA Driver Packages
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
paths:
7+
- 'build-matrix.json'
8+
9+
jobs:
10+
prepare:
11+
runs-on: ubuntu-latest
12+
outputs:
13+
matrix: ${{ steps.set-matrix.outputs.matrix }}
14+
gcc_tag: ${{ steps.set-matrix.outputs.gcc_tag }}
15+
steps:
16+
- uses: actions/checkout@v4
17+
- id: set-matrix
18+
run: |
19+
MATRIX=$(jq -c '
20+
# Builds from branch definitions
21+
[
22+
.kernel_versions[] as $k |
23+
.branches | to_entries[] |
24+
.value.module_types[] as $m |
25+
{
26+
driver_version: .value.driver_version,
27+
module_type: $m,
28+
kernel_version: $k,
29+
branch: .key
30+
}
31+
]
32+
+
33+
# Extra builds for pin-to-specific-version (outside branch structure)
34+
[
35+
.kernel_versions[] as $k |
36+
(.extra_builds // [])[] |
37+
.module_types[] as $m |
38+
{
39+
driver_version: .driver_version,
40+
module_type: $m,
41+
kernel_version: $k,
42+
branch: "extra"
43+
}
44+
]
45+
' build-matrix.json)
46+
echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT"
47+
echo "gcc_tag=$(jq -r '.gcc_tag' build-matrix.json)" >> "$GITHUB_OUTPUT"
48+
49+
build:
50+
needs: prepare
51+
runs-on: ubuntu-latest
52+
timeout-minutes: 120
53+
strategy:
54+
fail-fast: false
55+
matrix:
56+
include: ${{ fromJson(needs.prepare.outputs.matrix) }}
57+
58+
steps:
59+
- name: Checkout repository
60+
uses: actions/checkout@v4
61+
62+
- name: Free up disk space
63+
run: |
64+
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
65+
df -h /
66+
67+
- name: Create output directory
68+
run: mkdir -p output
69+
70+
- name: Build package in container
71+
run: |
72+
docker pull ghcr.io/ich777/unraid_kernel:${{ needs.prepare.outputs.gcc_tag }}
73+
docker run --rm \
74+
--entrypoint bash \
75+
-v "$GITHUB_WORKSPACE":/workspace \
76+
-v "$GITHUB_WORKSPACE/output":/output \
77+
ghcr.io/ich777/unraid_kernel:${{ needs.prepare.outputs.gcc_tag }} \
78+
-c '
79+
set -euo pipefail
80+
81+
DRIVER_VERSION="${{ matrix.driver_version }}"
82+
KERNEL_VERSION="${{ matrix.kernel_version }}"
83+
MODULE_TYPE="${{ matrix.module_type }}"
84+
85+
if [ "${MODULE_TYPE}" == "opensource" ]; then
86+
PKG_PREFIX="nvos"
87+
BUILD_ARG="opensource"
88+
else
89+
PKG_PREFIX="nvidia"
90+
BUILD_ARG=""
91+
fi
92+
93+
echo "=== Building ${PKG_PREFIX}-${DRIVER_VERSION} (${MODULE_TYPE}) for kernel ${KERNEL_VERSION} ==="
94+
95+
export UNAME="${KERNEL_VERSION}"
96+
export CPU_COUNT="$(nproc)"
97+
export DATA_DIR="/tmp/nvidia-build"
98+
mkdir -p "${DATA_DIR}"
99+
100+
# ---------------------------------------------------------------
101+
# Step 1: Prepare kernel sources
102+
# ---------------------------------------------------------------
103+
echo "=== Downloading kernel sources ==="
104+
KERNEL_TAR="linux-${KERNEL_VERSION}.tar.xz"
105+
KERNEL_URL="https://github.com/ich777/unraid_kernel/releases/download/${KERNEL_VERSION}/${KERNEL_TAR}"
106+
wget -q -O "/tmp/${KERNEL_TAR}" "${KERNEL_URL}"
107+
mkdir -p "/usr/src/linux-${KERNEL_VERSION}"
108+
tar xf "/tmp/${KERNEL_TAR}" -C "/usr/src/linux-${KERNEL_VERSION}"
109+
cd "/usr/src/linux-${KERNEL_VERSION}"
110+
111+
echo "=== Preparing kernel headers ==="
112+
make oldconfig </dev/null 2>&1 | tail -5
113+
make modules_prepare -j${CPU_COUNT} 2>&1 | tail -5
114+
115+
mkdir -p "/lib/modules/${UNAME}"
116+
ln -sf "/usr/src/linux-${KERNEL_VERSION}" "/lib/modules/${UNAME}/build"
117+
ln -sf "/usr/src/linux-${KERNEL_VERSION}" "/lib/modules/${UNAME}/source"
118+
cd /
119+
120+
# ---------------------------------------------------------------
121+
# Step 2: Create makepkg shim
122+
# ---------------------------------------------------------------
123+
mkdir -p "${DATA_DIR}/bzroot-extracted-${UNAME}/sbin"
124+
printf "#!/bin/bash\n# Grab last argument as output file\nfor PKG; do true; done\ntar cJf \"\${PKG}\" . 2>/dev/null; exit 0\n" > "${DATA_DIR}/bzroot-extracted-${UNAME}/sbin/makepkg"
125+
chmod +x "${DATA_DIR}/bzroot-extracted-${UNAME}/sbin/makepkg"
126+
127+
# ---------------------------------------------------------------
128+
# Step 3: Build using compile.sh
129+
# ---------------------------------------------------------------
130+
echo "=== Loading compile.sh and building ==="
131+
cd /workspace
132+
133+
real_wget="$(which wget)"
134+
mkdir -p /usr/local/bin
135+
printf "#!/bin/bash\nexec ${real_wget} \"\${@/--show-progress/}\"" > /usr/local/bin/wget
136+
chmod +x /usr/local/bin/wget
137+
138+
source source/compile.sh "${DRIVER_VERSION}" ${BUILD_ARG:-"dummy"}
139+
nvidia_driver "${DRIVER_VERSION}" ${BUILD_ARG}
140+
141+
# ---------------------------------------------------------------
142+
# Step 4: Copy output artifacts
143+
# ---------------------------------------------------------------
144+
echo "=== Copying build artifacts ==="
145+
find /tmp -name "${PKG_PREFIX}-*.txz" -exec cp {} /output/ \;
146+
find /tmp -name "${PKG_PREFIX}-*.txz.md5" -exec cp {} /output/ \;
147+
148+
echo "=== Build artifacts ==="
149+
ls -la /output/
150+
'
151+
152+
- name: Verify build output
153+
run: |
154+
ls -la output/
155+
if [ -z "$(ls output/*.txz 2>/dev/null)" ]; then
156+
echo "ERROR: No .txz package found in output!"
157+
exit 1
158+
fi
159+
cd output
160+
for f in *.txz; do
161+
if [ -f "${f}.md5" ]; then
162+
EXPECTED="$(cat "${f}.md5")"
163+
ACTUAL="$(md5sum "${f}" | awk '{print $1}')"
164+
echo "${f}: expected=${EXPECTED} actual=${ACTUAL}"
165+
if [ "${EXPECTED}" != "${ACTUAL}" ]; then
166+
echo "CHECKSUM MISMATCH!"
167+
exit 1
168+
fi
169+
echo "Checksum OK"
170+
fi
171+
done
172+
173+
- name: Upload artifact
174+
uses: actions/upload-artifact@v4
175+
with:
176+
name: ${{ matrix.module_type == 'opensource' && 'nvos' || 'nvidia' }}-${{ matrix.driver_version }}-${{ matrix.kernel_version }}
177+
path: output/*
178+
retention-days: 90
179+
180+
- name: Create or update GitHub Release
181+
env:
182+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
183+
run: |
184+
TAG="${{ matrix.kernel_version }}"
185+
gh release view "${TAG}" --repo ${{ github.repository }} >/dev/null 2>&1 || \
186+
gh release create "${TAG}" --repo ${{ github.repository }} \
187+
--title "Drivers for kernel ${TAG}" \
188+
--notes "NVIDIA driver packages for Unraid kernel ${TAG}"
189+
for f in output/*; do
190+
gh release upload "${TAG}" "${f}" --repo ${{ github.repository }} --clobber
191+
done
192+
echo "=== Release ${TAG} updated ==="
193+
gh release view "${TAG}" --repo ${{ github.repository }}

build-matrix.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"gcc_tag": "gcc_14.2.0",
3+
"kernel_versions": ["6.12.54-Unraid"],
4+
"branches": {
5+
"production": {
6+
"driver_version": "580.142",
7+
"module_types": ["proprietary", "opensource"]
8+
},
9+
"newfeature": {
10+
"driver_version": "590.48.01",
11+
"module_types": ["proprietary", "opensource"]
12+
},
13+
"beta": {
14+
"driver_version": "595.45.04",
15+
"module_types": ["proprietary", "opensource"]
16+
}
17+
},
18+
"extra_builds": [
19+
{ "driver_version": "575.64.05", "module_types": ["proprietary", "opensource"] },
20+
{ "driver_version": "580.126.18", "module_types": ["proprietary", "opensource"] },
21+
{ "driver_version": "590.44.01", "module_types": ["proprietary", "opensource"] }
22+
]
23+
}

source/usr/local/emhttp/plugins/nvidia-driver/include/download.sh

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,49 @@ elif [ "${SET_DRV_V}" == "latest_nfb" ]; then
193193
LAT_PACKAGE="$(echo "$DRIVER_AVAIL" | grep "$LAT_NFB_V")"
194194
fi
195195
fi
196+
elif [ "${SET_DRV_V}" == "latest_beta" ]; then
197+
LAT_BETA_AVAIL="$(echo "$BRANCHES" | jq -r '.beta.current')"
198+
if [ -z "$LAT_BETA_AVAIL" ]; then
199+
if [ -z "${CUR_V}" ]; then
200+
echo
201+
echo "-----ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR------"
202+
echo "------Can't get Beta Branch version and found no installed local driver---------"
203+
echo "-----Please wait for an hour and try it again, if it then also fails please-----"
204+
echo "------go to the Support Thread on the Unraid forums and make a post there!------"
205+
exit 1
206+
else
207+
LAT_PACKAGE=$PACKAGE-$CUR_V-$KERNEL_V-1.txz
208+
fi
209+
elif [ -z "$DRIVER_AVAIL" ]; then
210+
if [ -z "${CUR_V}" ]; then
211+
echo
212+
echo "-----ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR------"
213+
echo "------Can't get Nvidia driver versions and found no installed local driver------"
214+
echo "-----Please wait for an hour and try it again, if it then also fails please-----"
215+
echo "------go to the Support Thread on the Unraid forums and make a post there!------"
216+
exit 1
217+
else
218+
LAT_PACKAGE=$PACKAGE-$CUR_V-$KERNEL_V-1.txz
219+
fi
220+
else
221+
LAT_BETA_V="$(comm -12 <(echo "$DRIVER_AVAIL" | cut -d '-' -f2 | awk -F '.' '{printf "%d.%03d.%d\n", $1,$2,$3}' | awk -F '.' '{printf "%d.%03d.%02d\n", $1,$2,$3}') <(echo "$LAT_BETA_AVAIL" | awk -F '.' '{printf "%d.%03d.%d\n", $1,$2,$3}' | awk -F '.' '{printf "%d.%03d.%02d\n", $1,$2,$3}') | sort -V | tail -1 | awk -F '.' '{printf "%d.%02d.%02d\n", $1,$2,$3}' | awk '{sub(/\.0+$/,"")}1')"
222+
if [ -z "${LAT_BETA_V}" ]; then
223+
if [ -z "${CUR_V}" ]; then
224+
echo
225+
echo "-----ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR------"
226+
echo "------Can't get Nvidia driver versions and found no installed local driver------"
227+
echo "-----Please wait for an hour and try it again, if it then also fails please-----"
228+
echo "------go to the Support Thread on the Unraid forums and make a post there!------"
229+
exit 1
230+
else
231+
LAT_PACKAGE="$(echo "$DRIVER_AVAIL" | tail -1)"
232+
echo "---Can't find latest Beta Branch Nvidia Driver for your Kernel v${KERNEL_V%%-*} falling back to latest Nvidia Driver v$(echo $LAT_PACKAGE | cut -d '-' -f2)---"
233+
sed -i '/driver_version=/c\driver_version=latest' "/boot/config/plugins/nvidia-driver/settings.cfg"
234+
fi
235+
else
236+
LAT_PACKAGE="$(echo "$DRIVER_AVAIL" | grep "$LAT_BETA_V")"
237+
fi
238+
fi
196239
elif [ "${SET_DRV_V}" == "latest_nos" ]; then
197240
if [ -z "$LAT_NOS_AVAIL" ]; then
198241
if [ -z "${CUR_V}" ]; then
@@ -235,9 +278,22 @@ fi
235278
#Begin Check
236279
check
237280

238-
#Check for old packages that are not suitable for this Kernel and not suitable for the current Nvidia driver version
239-
rm -rf $(ls -d /boot/config/plugins/nvidia-driver/packages/* 2>/dev/null | grep -v "${KERNEL_V%%-*}")
240-
rm -f $(ls /boot/config/plugins/nvidia-driver/packages/${KERNEL_V%%-*}/* 2>/dev/null | grep -v "$LAT_PACKAGE")
281+
# SEC: Safe cleanup of old kernel directories.
282+
# Original used unquoted command substitution with rm -rf, which could
283+
# delete everything if variables were empty (grep -v "" matches all lines).
284+
# Using a loop with explicit checks prevents accidental mass deletion.
285+
while IFS= read -r dir; do
286+
[[ "$(basename "$dir")" != "${KERNEL_V%%-*}" ]] && rm -rf "$dir"
287+
done < <(find /boot/config/plugins/nvidia-driver/packages -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
288+
289+
# SEC: Only clean up old packages if we know the current package name.
290+
# Without this guard, an empty $LAT_PACKAGE would cause grep -v "" to
291+
# match nothing, and all files would be deleted from the packages dir.
292+
if [ -n "$LAT_PACKAGE" ]; then
293+
while IFS= read -r file; do
294+
[[ "$(basename "$file")" != "$LAT_PACKAGE" && "$(basename "$file")" != "${LAT_PACKAGE}.md5" ]] && rm -f "$file"
295+
done < <(find "/boot/config/plugins/nvidia-driver/packages/${KERNEL_V%%-*}" -mindepth 1 -maxdepth 1 -type f 2>/dev/null)
296+
fi
241297

242298
#Display message to reboot server both in Plugin and WebUI
243299
echo

0 commit comments

Comments
 (0)