1+ #! /usr/bin/env bash
2+ set -euo pipefail
3+
4+ # config
5+ NW_VERSION=" ${NW_VERSION:- v0.108.0} "
6+ ARCH=" ${ARCH:- x64} "
7+ SRCDIR=" ${SRCDIR:- src} "
8+ CACHE_DIR=" ${CACHE_DIR:- ./ nwjs-cache} "
9+ BUILD_DIR=" ${BUILD_DIR:- ./ build} "
10+ DIST_DIR=" ${DIST_DIR:- ./ dist} "
11+ APPIMAGETOOL=" ${CACHE_DIR} /appimagetool-x86_64.AppImage"
12+
13+ mkdir -p " $CACHE_DIR " " $BUILD_DIR " " $DIST_DIR "
14+
15+ # read metadata from package.json
16+ read -r NAME DISPLAY VERSION << EOF
17+ $( python3 - << PY
18+ import json,sys,re
19+ d=json.load(open('package.json'))
20+ name = d.get('name','app')
21+ display = d.get('displayName', d.get('name','App'))
22+ version = d.get('version','0.0.0')
23+ # sanitize display for filenames: keep letters, numbers, dash, underscore
24+ display = re.sub(r'[^A-Za-z0-9._-]','',display)
25+ print(name, display, version)
26+ PY
27+ )
28+ EOF
29+
30+ echo " Building ${DISPLAY} (package name: ${NAME} ) version ${VERSION} "
31+ echo " NW.js: ${NW_VERSION} arch: ${ARCH} "
32+
33+ # cached download
34+ cached_download () {
35+ local url=" $1 " ; local outname=" $2 "
36+ local outpath=" $CACHE_DIR /$outname "
37+ if [ -f " $outpath " ]; then
38+ echo " Using cached $outname "
39+ else
40+ echo " Downloading $outname "
41+ curl -L -sS -o " $outpath " " $url "
42+ fi
43+ echo " $outpath "
44+ }
45+
46+ # clean previous build
47+ rm -rf " $BUILD_DIR "
48+ mkdir -p " $BUILD_DIR "
49+
50+ # macos
51+ build_macos () {
52+ echo " === macOS build ==="
53+ local file=" nwjs-${NW_VERSION} -osx-${ARCH} .zip"
54+ local url=" https://dl.nwjs.io/${NW_VERSION} /${file} "
55+ local zip=$( cached_download " $url " " $file " )
56+
57+ tmp=$( mktemp -d)
58+ unzip -q " $zip " -d " $tmp "
59+ NW_APP=$( find " $tmp " -type d -name " nwjs.app" -print -quit || true)
60+ if [ -z " $NW_APP " ]; then
61+ NW_APP=$( find " $tmp " -maxdepth 2 -type d -name " *.app" -print -quit || true)
62+ fi
63+ if [ -z " $NW_APP " ]; then
64+ echo " nwjs.app not found in archive"
65+ rm -rf " $tmp "
66+ return 1
67+ fi
68+
69+ OUT_APP=" ${BUILD_DIR} /${DISPLAY} .app"
70+ rm -rf " $OUT_APP "
71+ cp -R " $NW_APP " " $OUT_APP "
72+
73+ APP_NW_DIR=" ${OUT_APP} /Contents/Resources/app.nw"
74+ rm -rf " $APP_NW_DIR "
75+ mkdir -p " $APP_NW_DIR "
76+ cp -R " ${SRCDIR} /." " $APP_NW_DIR /"
77+
78+ PLIST=" ${OUT_APP} /Contents/Info.plist"
79+ if [ -f " icon.icns" ]; then
80+ cp " icon.icns" " ${OUT_APP} /Contents/Resources/app.icns" || true
81+ if command -v /usr/libexec/PlistBuddy > /dev/null 2>&1 ; then
82+ /usr/libexec/PlistBuddy -c " Set :CFBundleName ${DISPLAY} " " $PLIST " || true
83+ /usr/libexec/PlistBuddy -c " Set :CFBundleDisplayName ${DISPLAY} " " $PLIST " || true
84+ /usr/libexec/PlistBuddy -c " Set :CFBundleIconFile app.icns" " $PLIST " || true
85+ fi
86+ fi
87+
88+ mkdir -p " $DIST_DIR "
89+ cp -R " $OUT_APP " " $DIST_DIR /${DISPLAY} .app"
90+ (cd " $BUILD_DIR " && zip -r -q " ../${DIST_DIR} /${DISPLAY} .app.zip" " $( basename " $OUT_APP " ) " )
91+
92+ rm -rf " $tmp "
93+ echo " mac: produced ${DIST_DIR} /${DISPLAY} .app (bundle) and ${DIST_DIR} /${DISPLAY} .app.zip"
94+ }
95+
96+ # windows
97+ build_windows () {
98+ echo " === Windows build ==="
99+ local file=" nwjs-${NW_VERSION} -win-${ARCH} .zip"
100+ local url=" https://dl.nwjs.io/${NW_VERSION} /${file} "
101+ local zip=$( cached_download " $url " " $file " )
102+
103+ tmp=$( mktemp -d)
104+ unzip -q " $zip " -d " $tmp "
105+
106+ TOPDIR=$( find " $tmp " -maxdepth 1 -type d -name " nwjs*" -print -quit || true)
107+ if [ -z " $TOPDIR " ]; then TOPDIR=" $tmp " ; fi
108+
109+ OUT_DIR=" ${BUILD_DIR} /${DISPLAY} -win"
110+ rm -rf " $OUT_DIR "
111+ mkdir -p " $OUT_DIR "
112+ cp -R " $TOPDIR /" * " $OUT_DIR /"
113+
114+ mkdir -p " ${OUT_DIR} /${NAME} -files"
115+ cp -R " ${SRCDIR} /." " ${OUT_DIR} /${NAME} -files/"
116+
117+ PKGZIP=" ${BUILD_DIR} /package.nw"
118+ (cd " ${SRCDIR} " && zip -r -q " ../${PKGZIP} " .)
119+
120+ NW_EXE=" $( find " $OUT_DIR " -maxdepth 1 -type f -iname ' nw.exe' -print -quit || true) "
121+ if [ -z " $NW_EXE " ]; then
122+ echo " nw.exe not found in extracted runtime"
123+ else
124+ DIST_EXE=" ${DIST_DIR} /${DISPLAY} .exe"
125+ if command -v cat > /dev/null 2>&1 ; then
126+ cat " $NW_EXE " " $PKGZIP " > " ${DIST_EXE} "
127+ chmod +x " ${DIST_EXE} " || true
128+ echo " windows: produced single-file ${DIST_EXE} (note: some runtime files may still be required alongside this exe)"
129+ else
130+ echo " cat not available; skipping single-file exe creation"
131+ fi
132+ fi
133+
134+ mkdir -p " $DIST_DIR "
135+ (cd " $BUILD_DIR " && zip -r -q " ../${DIST_DIR} /${DISPLAY} -win-${VERSION} .zip" " $( basename " $OUT_DIR " ) " )
136+
137+ rm -rf " $tmp " " $PKGZIP "
138+ }
139+
140+ # linux
141+ build_linux () {
142+ echo " === Linux build ==="
143+ local file=" nwjs-${NW_VERSION} -linux-${ARCH} .zip"
144+ local url=" https://dl.nwjs.io/${NW_VERSION} /${file} "
145+ local zip=$( cached_download " $url " " $file " )
146+
147+ tmp=$( mktemp -d)
148+ unzip -q " $zip " -d " $tmp "
149+ TOPDIR=$( find " $tmp " -maxdepth 1 -type d -name " nwjs*" -print -quit || true)
150+ if [ -z " $TOPDIR " ]; then TOPDIR=" $tmp " ; fi
151+
152+ APPDIR=" ${BUILD_DIR} /${DISPLAY} .AppDir"
153+ rm -rf " $APPDIR "
154+ mkdir -p " $APPDIR /usr/bin" " $APPDIR /usr/share/icons/hicolor/256x256/apps"
155+
156+ cp -R " $TOPDIR /" * " $APPDIR /"
157+
158+ mkdir -p " ${APPDIR} /usr/share/${NAME} "
159+ cp -R " ${SRCDIR} /." " ${APPDIR} /usr/share/${NAME} /"
160+
161+ cat > " ${APPDIR} /AppRun" << AR
162+ #!/usr/bin/env bash
163+ HERE="\$ (dirname "\$ (readlink -f "\$ {0}")")"
164+ # run the nw binary from inside the AppDir; prefer ./nw or usr/bin/nw
165+ if [ -x "\$ HERE/nw" ]; then
166+ exec "\$ HERE/nw" "\$ HERE/usr/share/${NAME} " "\$ @"
167+ elif [ -x "\$ HERE/usr/bin/nw" ]; then
168+ exec "\$ HERE/usr/bin/nw" "\$ HERE/usr/share/${NAME} " "\$ @"
169+ else
170+ echo "nw binary not found in AppImage"
171+ exit 1
172+ fi
173+ AR
174+ chmod +x " ${APPDIR} /AppRun"
175+
176+ if [ -f " icon.png" ]; then
177+ cp " icon.png" " ${APPDIR} /usr/share/icons/hicolor/256x256/apps/${DISPLAY} .png" || true
178+ fi
179+
180+ if [ ! -f " $APPIMAGETOOL " ]; then
181+ echo " Downloading appimagetool"
182+ curl -L -sS -o " $APPIMAGETOOL " " https://github.com/AppImage/appimagetool/releases/latest/download/appimagetool-x86_64.AppImage"
183+ chmod +x " $APPIMAGETOOL "
184+ else
185+ echo " Using cached appimagetool"
186+ fi
187+
188+ (cd " $BUILD_DIR " && " $APPIMAGETOOL " " ${APPDIR} " " ${DIST_DIR} /${DISPLAY} .AppImage" )
189+ chmod +x " ${DIST_DIR} /${DISPLAY} .AppImage"
190+ echo " linux: produced ${DIST_DIR} /${DISPLAY} .AppImage"
191+ rm -rf " $tmp "
192+ }
193+
194+ # run builds
195+ build_macos || echo " mac build failed (continuing)"
196+ build_windows || echo " windows build failed (continuing)"
197+ build_linux || echo " linux build failed (continuing)"
198+
199+ echo " Artifacts in ${DIST_DIR} :"
200+ ls -la " $DIST_DIR " || true
0 commit comments