add build scripts

This commit is contained in:
Félix Dorn 2025-06-19 13:00:16 +02:00
parent c3ee96429c
commit 1d2190d68e
26 changed files with 1327 additions and 6 deletions

@ -1 +0,0 @@
Subproject commit ececd00d5fcbc78b83947db8fbab4a4b628ffd13

View file

@ -9,7 +9,7 @@
"nativeAllowList": ["app.*"],
"modes": {
"window": {
"title": "myapp",
"title": "Pucoti",
"width": 800,
"height": 500,
"minWidth": 400,
@ -18,7 +18,7 @@
}
},
"cli": {
"binaryName": "myapp",
"binaryName": "pucoti",
"resourcesPath": "/www/dist/",
"extensionsPath": "/extensions/",
"clientLibrary": "/www/public/neutralino.js",
@ -29,9 +29,9 @@
"mac": {
"architecture": ["x64", "arm64", "universal"],
"minimumOS": "10.13.0",
"appName": "myapp",
"appBundleName": "myapp",
"appIdentifier": "com.marketmix.ext.bun.demo",
"appName": "pucoti",
"appBundleName": "pucoti",
"appIdentifier": "com.pucoti.app",
"appIcon": "/packaging/pucoti.icns"
}
}

2
scripts/.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

21
scripts/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 hschneider
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

178
scripts/README.md Normal file
View file

@ -0,0 +1,178 @@
![](https://marketmix.com/git-assets/neutralino-build-scripts/neutralino-macos-appbundles.jpg)
# neutralino-build-scripts
**Neutralino Build-Automation for macOS, Linux and Windows App-Bundles**.
This set of scripts replace the `neu build` command for macOS-, Linux and Windows-builds. Instead of plain binaries, it outputs ready-to-use app-bundles.
> The macOS build-script solves the problem, that Neutralino only produces plain macOS binaries and not macOS AppBundles. These files cannot be signed and notarized.
> **build-mac.sh** generates valid AppBundles which pass Apple's notarization process successfully :-
## Setup
Copy all, **except neutralino.config.json** (which is just an example) to your project's root folder. The scripts are tested under macOS and should also run on Linux or Windows/WSL.
Install **jq**, which is required for parsing JSON files:
```bash
# On macOS:
brew install jq
# On Linux or Windows/WSL:
sudo apt-get install jq
```
If your are a Mac-user and never heard of Homebrew, visit https://brew.sh
Add this to your neutralino.config.json:
```json
"buildScript": {
"mac": {
"architecture": ["x64", "arm64", "universal"],
"minimumOS": "10.13.0",
"appName": "ExtBunDemo",
"appBundleName": "ExtBunDemo",
"appIdentifier": "com.marketmix.ext.bun.demo",
"appIcon": "icon.icns"
},
"win": {
"architecture": ["x64"],
"appName": "ExtBunDemo",
"appIcon": "icon.ico"
},
"linux": {
"architecture": ["x64", "arm64", "armhf"],
"appName": "ExtBunDemo",
"appIcon": "icon.png",
"appPath": "/usr/share/ExtBunDemo",
"appIconLocation": "/usr/share/ExtBunDemo/icon.png"
}
}
```
If you are unsure where to add, examine **the example neutralino.config.json**, included in this repo.
## Build for macOS
```bash
./build-mac.sh
```
This starts the following procedure:
- Erase the target folder ./dist/APPNAME
- Run `neu build`
- Execute `preproc-mac.sh`
- Clone the app-bundle scaffold from `_app_scaffolds/mac` and adapt it to your app.
- Copy all resources and extensions to the app-bundle.
- Execute `postproc-mac.sh`
All build targets are created in the ./dist folder.
Because the macOS-platform consists of 3 binary architectures, you might want to add different resources after the app has been built. That's what `postproc-mac.sh` is for. Just add your custom code there and you are good to go.
If you need to prepare platform-specific resource before bundling starts, you can add your custom code to `preproc-mac.sh`.
Keep in mind that alle additional resources have to be copied to `${APP_RESOURCES}/`, which resolves to `MyApp.app/Contents/Resources`. If you place them elsewhere, your signature or notarization might break.
The `buildScript/mac` JSON segment in the config-file contains the following fields:
| Key | Description |
| ------------- | ------------------------------------------------------------ |
| architecture | This is an array of the architectures, you want to build. In our example we build all 3 architectures. |
| minimumOS | The minimum macOS version. |
| appName | The app-name as displayed in the Finder. |
| appBundleName | The macOS app-bundle name. |
| appIdentifier | The macOS app-identifier. |
| appIcon | Path to the app-icon in **.icns-format**. If only the filename is submitted, the file is expected in the project's root. |
If you want to streamline your deployment process under macOS, you might also be interested in **[Sign and Notarize Automation](https://github.com/hschneider/macos-sign-notarize)** from commandline.
## Build for Windows
```bash
./build-win.sh
```
This starts the following procedure:
- Erase the target folder ./dist/APPNAME
- Run `neu build`
- Execute `preproc-win.sh`
- Copy all resources and extensions to the app-bundle.
- Execute `postproc-win.sh`
- Create the `install-icon.cmd` helper script from its template in `_app_scaffolds/win/`, if an app icon file exists.
The build is created in the ./dist folder.
In contrast to macOS, the whole process is straight-forward. The app-bundle is just a plain folder with the binary, resources.neu, the extensions-folder and WebView2Loader.dll. The DLL can be deleted, if you deploy on WIndows 11 or newer.
If you need to prepare platform-specific resource before bundling starts, you can add your custom code to `preproc-win.sh`.
You can also put custom code into `postproc-win.sh` to perform any action after the bundle has been built.
The `buildScript/win` JSON segment in the config-file contains the following fields:
| Key | Description |
| ------------ | ------------------------------------------------------------ |
| architecture | This is an array of the architectures, you want to build. Because Neutralino currently only support 'x64', you should leave this untouched. |
| appName | The app-name as displayed in the File Explorer, with or without .exe-suffix. |
| appIcon | Path to the app-icon in **.ico-format**. If only the filename is submitted, the file is expected in the project's root. The icon is copied from this path into the app-bundle. To apply the icon to the executable file, you'll have to run **[Resource Hacker](https://www.angusj.com/resourcehacker/)** from a Windows machine. To do so, just double-click **install-icon.cmd** in the app-bundle. |
The icon installer in action:
![](https://marketmix.com//git-assets/neutralino-build-scripts/neutralino-icon-installer.gif)
## Build for Linux
```bash
./build-linux.sh
```
This starts the following procedure:
- Erase the target folder ./dist/APPNAME
- Run `neu build`
- Execute `preproc-linux.sh`
- Copy all resources and extensions to the app-bundle.
- Clones the .desktop-file from `_app_scaffolds/linux` to the app-bundle and adapts its content.
- Execute `postproc-linux.sh`
All build targets are created in the ./dist folder.
Because the Linux-platform consists of 3 binary architectures, you might want to add different resources after the app has been built. That's what `postproc-linux.sh` is for. Just add your custom code there and you are good to go.
If you need to prepare platform-specific resource before bundling starts, you can add your custom code to `preproc-linux.sh`.
> The **APP_NAME.desktop**-file and the **app icon** have to be copied to their proper places, when you deploy your app.
The following paths are proposed:
| Resource | Path |
| ------------------ | ---------------------------------------- |
| Application Folder | /usr/share/APP_NAME |
| Application Icon | /usr/share/APP_NAME/icon.png |
| Desktop File | /usr/share/applications/APP_NAME.desktop |
The `buildScript/win` JSON segment in the config-file contains the following fields:
| Key | Description |
| ------------ | ------------------------------------------------------------ |
| architecture | This is an array of the architectures, you want to build. In our example we build all 3 architectures. |
| appName | The app-name as displayed in the File Explorer. |
| appPath | The application path without the executable name and without ending slash. |
| appIcon | Path to the app-icon in .**png- or svg-format**. If only the filename is submitted, the file is expected in the project's root. The icon is copied from this path into the app-bundle. |
| appIconPath | This is the icon's path **after** the has been installed on a Linux system. That path is written to the .desktop-file. |
Calling `sudo ./install.sh` from your build folder automatically installs the app to the locations you defined.
## More about Neutralino
- [NeutralinoJS Home](https://neutralino.js.org)
- [Neutralino related blog posts at marketmix.com](https://marketmix.com/de/tag/neutralinojs/)
<img src="https://marketmix.com/git-assets/star-me-2.svg">

1
scripts/_VERSION Normal file
View file

@ -0,0 +1 @@
1.1.4

View file

@ -0,0 +1,20 @@
#!/bin/bash
echo "Installing ..."
cd ..
mkdir {APP_PATH}
cp -r {APP_NAME} {APP_BASEPATH}
cd {APP_NAME}
chmod +x {APP_EXEC}
cp {APP_ICON} {APP_ICON_PATH}
cp {APP_NAME}.desktop /usr/share/applications/{APP_NAME}.desktop
read -p "Delete original files? (y/n): " answer
if [[ $answer == "y" ]]; then
echo "Deleting ..."
cd ..
rm -rf {APP_NAME}
fi
echo "DONE."

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Encoding=UTF-8
Name={APP_NAME}
Icon={APP_ICON_PATH}
Exec={APP_EXEC}
Path={APP_PATH}
Terminal=false
Type=Application

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSHighResolutionCapable</key>
<true/>
<key>CFBundleExecutable</key>
<string>bootstrap</string>
<key>CFBundleGetInfoString</key>
<string>{APP_NAME}</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>{APP_ID}</string>
<key>CFBundleName</key>
<string>{APP_BUNDLE}</string>
<key>CFBundleShortVersionString</key>
<string>{APP_VERSION}</string>
<key>CFBundleGetInfoString</key>
<string>{APP_NAME} {APP_VERSION}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSMinimumSystemVersion</key>
<string>{APP_MIN_OS}</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Based on code by https://github.com/malipetek - hey you did a brilliant job!
MACOS="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
CONTENTS="$(dirname "$MACOS")"
exec "${MACOS}/main" --path="${CONTENTS}/Resources" --enable-extensions=true

Binary file not shown.

View file

@ -0,0 +1,13 @@
@echo off
set TOOL="c:\Program Files (x86)\Resource Hacker\ResourceHacker.exe"
if exist %TOOL% (
%TOOL% -open {APP_NAME} -save {APP_NAME} -action addskip -res {APP_ICON} -mask ICONGROUP,MAINICON,
del {APP_ICON}
del install-icon.cmd
)
else (
echo "Please install 'Resource Hacker' first."
echo "Press any key ..."
pause
start "" "https://www.angusj.com/resourcehacker/"
)

195
scripts/build-linux.sh Executable file
View file

@ -0,0 +1,195 @@
#!/bin/bash
#
# build-linux.sh
#
# Linux build script for NeutralinoJS
#
# Call:
# ./build-linux.sh
#
# Requirements:
# brew install jq
#
# (c)2023-2024 Harald Schneider - marketmix.com
VERSION='1.0.8'
OS=$(uname -s)
echo
echo -e "\033[1mNeutralino BuildScript for Linux platform, version ${VERSION}\033[0m"
CONF=./neutralino.config.json
if [ ! -e "./${CONF}" ]; then
echo
echo -e "\033[31m\033[1mERROR: ${CONF} not found.\033[0m"
exit 1
fi
if ! jq -e '.buildScript | has("linux")' "${CONF}" > /dev/null; then
echo
echo -e "\033[31m\033[1mERROR: Missing buildScript JSON structure in ${CONF}\033[0m"
exit 1
fi
APP_ARCH_LIST=($(jq -r '.buildScript.linux.architecture[]' ${CONF}))
APP_VERSION=$(jq -r '.version' ${CONF})
APP_BINARY=$(jq -r '.cli.binaryName' ${CONF})
APP_NAME=$(jq -r '.buildScript.linux.appName' ${CONF})
APP_ICON=$(jq -r '.buildScript.linux.appIcon' ${CONF})
APP_ICON_PATH=$(jq -r '.buildScript.linux.appIconLocation' ${CONF})
if jq -e '.buildScript.linux.appPath' "${CONF}" &> /dev/null; then
APP_PATH=$(jq -r '.buildScript.linux.appPath' ${CONF})
else
echo
echo -e "\033[31m\033[1mWARNING: Please set appPath in neutralino.config.json!\033[0m"
APP_PATH="/usr/share/${APP_NAME}"
fi
APP_BASEPATH=$(echo ${APP_PATH} | awk -v replacement="" -v search="/${APP_NAME}" '{gsub(search, replacement)}1')
if jq -e '.buildScript.linux.appIconPath' "${CONF}" &> /dev/null; then
APP_ICON_PATH=$(jq -r '.buildScript.linux.appIconPath' ${CONF})
else
echo
echo -e "\033[31m\033[1mWARNING: Please set appIconPath in neutralino.config.json!\033[0m"
APP_ICON_PATH="/usr/share/${APP_NAME}"
fi
APP_SRC=./_app_scaffolds/linux/myapp.desktop
INSTALL_SCRIPT=./_app_scaffolds/linux/install.sh
if [ ! -e "./${APP_SRC}" ]; then
echo
echo -e "\033[31m\033[1mERROR: App scaffold not found: ${APP_SRC}\033[0m"
exit 1
fi
if [ ! -e "./${INSTALL_SCRIPT}" ]; then
echo
echo -e "\033[31m\033[1mERROR: App install script not found: ${INSTALL_SCRIPT}\033[0m"
exit 1
fi
if [ "$1" != "--test" ]; then
echo
echo -e "\033[1mBuilding Neutralino Apps ...\033[0m"
echo
rm -rf "./dist/${APP_BINARY}"
neu build
echo -e "\033[1mDone.\033[0m"
else
echo
echo "Skipped 'neu build' in test-mode ..."
fi
for APP_ARCH in "${APP_ARCH_LIST[@]}"; do
APP_DST=./dist/linux_${APP_ARCH}/${APP_NAME}
if [ -e "./preproc-linux.sh" ]; then
echo " Running pre-processor ..."
. preproc-linux.sh
fi
EXE=./dist/${APP_BINARY}/${APP_BINARY}-linux_${APP_ARCH}
RES=./dist/${APP_BINARY}/resources.neu
EXT=./dist/${APP_BINARY}/extensions
APP_EXEC=${APP_PATH}/${APP_BINARY}-linux_${APP_ARCH}
chmod +x "${APP_DST}"
echo
echo -e "\033[1mBuilding App Bundle (${APP_ARCH}):\033[0m"
echo
echo " App Name: ${APP_NAME}"
echo " App Executable: ${APP_EXEC}"
echo " Icon: ${APP_ICON}"
echo " Icon Install Path: ${APP_ICON_PATH}"
echo " App Path: ${APP_PATH}"
echo " Target Folder: ${APP_DST}"
echo
if [ ! -e "./${EXE}" ]; then
echo -e "\033[31m\033[1m ERROR: File not found: ${EXE}\033[0m"
exit 1
fi
if [ ! -e "./${RES}" ]; then
echo -e "\033[31m\033[1m ERROR: Resource file not found: ${RES}\033[0m"
exit 1
fi
echo " Cloning scaffold ..."
mkdir -p "${APP_DST}"
cp "${APP_SRC}" "${APP_DST}/${APP_NAME}.desktop"
cp "${INSTALL_SCRIPT}" "${APP_DST}"
echo " Copying content:"
echo " - Binary File"
cp "${EXE}" "${APP_DST}/"
echo " - Resources"
cp "${RES}" "${APP_DST}/"
if [ -e "./${EXT}" ]; then
echo " - Extensions"
cp -r "${EXT}" "${APP_DST}/"
fi
if [ -e "./${APP_ICON}" ]; then
echo " - Icon"
cp -r "${APP_ICON}" "${APP_DST}/"
fi
echo " Processing Desktop File ..."
if [ "$OS" == "Darwin" ]; then
sed -i '' "s/{APP_NAME}/${APP_NAME}/g" "${APP_DST}/${APP_NAME}.desktop"
sed -i '' "s|{APP_ICON_LOCATION}|${APP_ICON_PATH}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i '' "s|{APP_ICON_PATH}|${APP_ICON_PATH}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i '' "s|{APP_PATH}|${APP_PATH}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i '' "s|{APP_EXEC}|${APP_EXEC}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i '' "s|{APP_NAME}|${APP_NAME}|g" "${APP_DST}/install.sh"
sed -i '' "s|{APP_PATH}|${APP_PATH}|g" "${APP_DST}/install.sh"
sed -i '' "s|{APP_BASEPATH}|${APP_BASEPATH}|g" "${APP_DST}/install.sh"
sed -i '' "s|{APP_EXEC}|${APP_EXEC}|g" "${APP_DST}/install.sh"
sed -i '' "s|{APP_ICON}|${APP_ICON}|g" "${APP_DST}/install.sh"
sed -i '' "s|{APP_ICON_PATH}|${APP_ICON_PATH}|g" "${APP_DST}/install.sh"
else
sed -i "s/{APP_NAME}/${APP_NAME}/g" "${APP_DST}/${APP_NAME}.desktop"
sed -i "s|{APP_ICON_LOCATION}|${APP_ICON_PATH}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i "s|{APP_ICON_PATH}|${APP_ICON_PATH}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i "s|{APP_PATH}|${APP_PATH}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i "s|{APP_EXEC}|${APP_EXEC}|g" "${APP_DST}/${APP_NAME}.desktop"
sed -i "s|{APP_NAME}|${APP_NAME}|g" "${APP_DST}/install.sh"
sed -i "s|{APP_PATH}|${APP_PATH}|g" "${APP_DST}/install.sh"
sed -i "s|{APP_BASEPATH}|${APP_BASEPATH}|g" "${APP_DST}/install.sh"
sed -i "s|{APP_EXEC}|${APP_EXEC}|g" "${APP_DST}/install.sh"
sed -i "s|{APP_ICON}|${APP_ICON}|g" "${APP_DST}/install.sh"
sed -i "s|{APP_ICON_PATH}|${APP_ICON_PATH}|g" "${APP_DST}/install.sh"
fi
if [ -e "./postproc-linux.sh" ]; then
echo " Running post-processor ..."
. postproc-linux.sh
fi
echo
echo -e "\033[1mBuild finished.\033[0m"
done
echo
echo -e "\033[1mI propose the following paths for your installer:\033[0m"
echo
echo " Application Folder: ${APP_PATH}"
echo " Application Icon: ${APP_ICON_PATH}"
echo " Desktop File: /usr/share/applications/${APP_NAME}.desktop"
echo
echo "You can call ./install.sh to start a quick installation on the target machine."
echo
echo -e "\033[1mAll done.\033[0m"

154
scripts/build-mac.sh Executable file
View file

@ -0,0 +1,154 @@
#!/bin/bash
#
# build-mac.sh
#
# macOS build script for NeutralinoJS
#
# Call:
# ./build-mac.sh
#
# Requirements:
# brew install jq
#
# (c)2023-2024 Harald Schneider - marketmix.com
VERSION='1.0.7'
OS=$(uname -s)
echo
echo -e "\033[1mNeutralino BuildScript for macOS platform, version ${VERSION}\033[0m"
CONF=./../neutralino.config.json
if [ ! -e "./${CONF}" ]; then
echo
echo -e "\033[31m\033[1mERROR: ${CONF} not found.\033[0m"
exit 1
fi
if ! jq -e '.buildScript | has("mac")' "${CONF}" > /dev/null; then
echo
echo -e "\033[31m\033[1mERROR: Missing buildScript JSON structure in ${CONF}\033[0m"
exit 1
fi
APP_ARCH_LIST=($(jq -r '.buildScript.mac.architecture[]' ${CONF}))
APP_VERSION=$(jq -r '.version' ${CONF})
APP_MIN_OS=$(jq -r '.buildScript.mac.minimumOS' ${CONF})
APP_BINARY=$(jq -r '.cli.binaryName' ${CONF})
APP_NAME=$(jq -r '.buildScript.mac.appName' ${CONF})
APP_ID=$(jq -r '.buildScript.mac.appIdentifier' ${CONF})
APP_BUNDLE=$(jq -r '.buildScript.mac.appBundleName' ${CONF})
APP_ICON=$(jq -r '.buildScript.mac.appIcon' ${CONF})
APP_SRC=./_app_scaffolds/mac/myapp.app
if [ ! -e "./${APP_SRC}" ]; then
echo
echo -e "\033[31m\033[1mERROR: App scaffold not found: ${APP_SRC}\033[0m"
exit 1
fi
if [ "$1" != "--test" ]; then
echo
echo -e "\033[1mBuilding Neutralino Apps ...\033[0m"
echo
rm -rf "./dist/${APP_BINARY}"
neu build
echo -e "\033[1mDone.\033[0m"
else
echo
echo "Skipped 'neu build' in test-mode ..."
fi
for APP_ARCH in "${APP_ARCH_LIST[@]}"; do
APP_DST=./dist/mac_${APP_ARCH}/${APP_NAME}.app
APP_MACOS=${APP_DST}/Contents/MacOS
APP_RESOURCES=${APP_DST}/Contents/Resources
if [ -e "./preproc-mac.sh" ]; then
echo " Running pre-processor ..."
. preproc-mac.sh
fi
EXE=./dist/${APP_BINARY}/${APP_BINARY}-mac_${APP_ARCH}
RES=./dist/${APP_BINARY}/resources.neu
EXT=./dist/${APP_BINARY}/extensions
echo
echo -e "\033[1mBuilding App Bundle (${APP_ARCH}):\033[0m"
echo
echo " Minimum macOS: ${APP_MIN_OS}"
echo " App Name: ${APP_NAME}"
echo " Bundle Name: ${APP_BUNDLE}"
echo " Identifier: ${APP_ID}"
echo " Icon: ${APP_ICON}"
echo " Source Folder: ${APP_SRC}"
echo " Target Folder: ${APP_DST}"
echo
if [ ! -e "./${EXE}" ]; then
echo -e "\033[31m\033[1m ERROR: File not found: ${EXE}\033[0m"
exit 1
fi
if [ ! -e "./${RES}" ]; then
echo -e "\033[31m\033[1m ERROR: Resource file not found: ${RES}\033[0m"
exit 1
fi
echo " Cloning scaffold ..."
mkdir -p "${APP_DST}"
cp -r ${APP_SRC}/* ${APP_DST}/
echo " Copying content:"
echo " - Binary File"
cp "${EXE}" "${APP_MACOS}/main"
chmod 755 "${APP_MACOS}/main"
echo " - Resources"
cp "${RES}" "${APP_RESOURCES}/"
if [ -e "./${EXT}" ]; then
echo " - Extensions"
cp -r "${EXT}" "${APP_RESOURCES}/"
fi
if [ -e "./${APP_ICON}" ]; then
echo " - Icon"
cp -r "${APP_ICON}" "${APP_RESOURCES}/"
fi
echo " Processing Info.plist ..."
if [ "$OS" == "Darwin" ]; then
sed -i '' "s/{APP_NAME}/${APP_NAME}/g" "${APP_DST}/Contents/Info.plist"
sed -i '' "s/{APP_BUNDLE}/${APP_BUNDLE}/g" "${APP_DST}/Contents/Info.plist"
sed -i '' "s/{APP_ID}/${APP_ID}/g" "${APP_DST}/Contents/Info.plist"
sed -i '' "s/{APP_VERSION}/${APP_VERSION}/g" "${APP_DST}/Contents/Info.plist"
sed -i '' "s/{APP_MIN_OS}/${APP_MIN_OS}/g" "${APP_DST}/Contents/Info.plist"
else
sed -i "s/{APP_NAME}/${APP_NAME}/g" "${APP_DST}/Contents/Info.plist"
sed -i "s/{APP_BUNDLE}/${APP_BUNDLE}/g" "${APP_DST}/Contents/Info.plist"
sed -i "s/{APP_ID}/${APP_ID}/g" "${APP_DST}/Contents/Info.plist"
sed -i "s/{APP_VERSION}/${APP_VERSION}/g" "${APP_DST}/Contents/Info.plist"
sed -i "s/{APP_MIN_OS}/${APP_MIN_OS}/g" "${APP_DST}/Contents/Info.plist"
fi
if [ -e "./postproc-mac.sh" ]; then
echo " Running post-processor ..."
. postproc-mac.sh
fi
if [ "$OS" == "Darwin" ]; then
echo " Clearing Extended Attributes ..."
find "${APP_DST}" -type f -exec xattr -c {} \;
fi
echo
echo -e "\033[1mBuild finished, ready to sign and notarize.\033[0m"
done
echo
echo -e "\033[1mAll done.\033[0m"

140
scripts/build-win.sh Executable file
View file

@ -0,0 +1,140 @@
#!/bin/bash
#
# build-win.sh
#
# Windows build script for NeutralinoJS
#
# Call:
# ./build-win.sh
#
# Requirements:
# brew install jq
#
# (c)2023-2024 Harald Schneider - marketmix.com
VERSION='1.1.0'
OS=$(uname -s)
echo
echo -e "\033[1mNeutralino BuildScript for Windows platform, version ${VERSION}\033[0m"
CONF=./neutralino.config.json
if [ ! -e "./${CONF}" ]; then
echo
echo -e "\033[31m\033[1mERROR: ${CONF} not found.\033[0m"
exit 1
fi
if ! jq -e '.buildScript | has("win")' "${CONF}" > /dev/null; then
echo
echo -e "\033[31m\033[1mERROR: Missing buildScript JSON structure in ${CONF}\033[0m"
exit 1
fi
APP_ARCH_LIST=($(jq -r '.buildScript.win.architecture[]' ${CONF}))
APP_BINARY=$(jq -r '.cli.binaryName' ${CONF})
APP_NAME=$(jq -r '.buildScript.win.appName' ${CONF})
APP_ICON=$(jq -r '.buildScript.win.appIcon' ${CONF})
if [[ $APP_NAME != *".exe"* ]]; then
APP_NAME=${APP_NAME}.exe
fi
APP_SRC=./_app_scaffolds/win
if [ "$1" != "--test" ]; then
echo
echo -e "\033[1mBuilding Neutralino Apps ...\033[0m"
echo
rm -rf "./dist/${APP_BINARY}"
neu build
echo -e "\033[1mDone.\033[0m"
else
echo
echo "Skipped 'neu build' in test-mode ..."
fi
for APP_ARCH in "${APP_ARCH_LIST[@]}"; do
APP_DST=./dist/win_${APP_ARCH}
if [ -e "./preproc-win.sh" ]; then
echo " Running pre-processor ..."
. preproc-win.sh
fi
EXE=./dist/${APP_BINARY}/${APP_BINARY}-win_${APP_ARCH}.exe
RES=./dist/${APP_BINARY}/resources.neu
EXT=./dist/${APP_BINARY}/extensions
echo
echo -e "\033[1mBuilding App Bundle (${APP_ARCH}):\033[0m"
echo
echo " App Name: ${APP_NAME}"
echo " Target Folder: ${APP_DST}"
echo
if [ ! -e "./${EXE}" ]; then
echo -e "\033[31m\033[1m ERROR: Binary file not found: ${EXE}\033[0m"
exit 1
fi
if [ ! -e "./${RES}" ]; then
echo -e "\033[31m\033[1m ERROR: Resource file not found: ${RES}\033[0m"
exit 1
fi
echo " Creating target folder ..."
mkdir -p "${APP_DST}"
if [ -e "./${APP_ICON}" ]; then
echo " Cloning scaffold ..."
set +f
cp ${APP_SRC}/* "${APP_DST}/"
set -f
if [ "$OS" == "Darwin" ]; then
sed -i '' "s/{APP_NAME}/${APP_NAME}/g" "${APP_DST}/install-icon.cmd"
sed -i '' "s/{APP_ICON}/${APP_ICON}/g" "${APP_DST}/install-icon.cmd"
else
sed -i "s/{APP_NAME}/${APP_NAME}/g" "${APP_DST}/install-icon.cmd"
sed -i "s/{APP_ICON}/${APP_ICON}/g" "${APP_DST}/install-icon.cmd"
fi
fi
echo " Copying content:"
echo " - Binary File"
cp "${EXE}" "${APP_DST}/${APP_NAME}"
echo " - Resources"
cp "${RES}" "${APP_DST}/"
if [ -e "./${EXT}" ]; then
echo " - Extensions"
cp -r "${EXT}" "${APP_DST}/"
fi
if [ -e "./${APP_ICON}" ]; then
echo " - Icon"
cp -r "${APP_ICON}" "${APP_DST}/"
fi
if [ -e "./postproc-win.sh" ]; then
echo " Running post-processor ..."
. postproc-win.sh
fi
echo
echo -e "\033[1mBuild finished.\033[0m"
if [ -e "./${APP_ICON}" ]; then
echo
echo -e "\033[32m\033[1mDouble-click install-icon.cmd on a Windows machine to apply the app icon.\033[0m"
fi
done
echo
echo -e "\033[1mAll done.\033[0m"

BIN
scripts/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
scripts/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -0,0 +1,70 @@
{
"$schema": "https://raw.githubusercontent.com/neutralinojs/neutralinojs/main/schemas/neutralino.config.schema.json",
"applicationId": "js.neutralino.zero",
"version": "1.0.0",
"defaultMode": "window",
"port": 0,
"documentRoot": "/www/dist/",
"url": "/",
"enableServer": true,
"enableNativeAPI": true,
"enableExtensions": false,
"exportAuthInfo": true,
"tokenSecurity": "one-time",
"nativeAllowList": ["app.*"],
"logging": {
"enabled": false,
"writeToLogFile": false
},
"globalVariables": {},
"modes": {
"window": {
"title": "myapp",
"width": 800,
"height": 500,
"minWidth": 400,
"minHeight": 200,
"fullScreen": false,
"alwaysOnTop": false,
"icon": "/www/dist/favicon.ico",
"enableInspector": true,
"borderless": false,
"maximize": false,
"hidden": false,
"center": true,
"useSavedState": false,
"resizable": true,
"exitProcessOnClose": false
}
},
"cli": {
"binaryName": "myapp",
"resourcesPath": "/www/dist/",
"extensionsPath": "/extensions/",
"clientLibrary": "/www/public/neutralino.js",
"binaryVersion": "6.1.0",
"clientVersion": "6.1.0"
},
"buildScript": {
"mac": {
"architecture": ["x64", "arm64", "universal"],
"minimumOS": "10.13.0",
"appName": "myapp",
"appBundleName": "myapp",
"appIdentifier": "com.marketmix.ext.bun.demo",
"appIcon": "../packaging/pucoti.icns"
},
"win": {
"architecture": ["x64"],
"appName": "myapp",
"appIcon": "icon.ico"
},
"linux": {
"architecture": ["x64", "arm64", "armhf"],
"appName": "myapp",
"appIcon": "icon.png",
"appPath": "/usr/share/myapp",
"appIconPath": "/usr/share/myapp/icon.png"
}
}
}

29
scripts/postproc-linux.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/bash
#
# postproc-linux.sh 1.0.1
#
# Linux build script post-processor.
#
# This is called from build-win.sh after the app-bundle has been built.
# Use this e.g. to copy additional resources to the app-bundle.
# You can use all variables from the main script here.
#
# (c)2023 Harald Schneider - marketmix.com
if [ $APP_ARCH = "x64" ]; then
:
# Handle Intel releases here
# cp SOME_FILE "${APP_DST}/"
fi
if [ $APP_ARCH = "arm64" ]; then
:
# Handle ARM releases here
# cp SOME_FILE "${APP_DST}/"
fi
if [ $APP_ARCH = "armhf" ]; then
:
# Handle ARM hard_float releases here.
# cp SOME_FILE "${APP_DST}/"
fi

29
scripts/postproc-mac.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/bash
#
# postproc-mac.sh 1.0.0
#
# macOS build script post-processor.
#
# This is called from build-mac.sh after each app-bundle has been built.
# Use this e.g. to copy additional resources to the app-bundle.
# You can use all variables from the main script here.
#
# (c)2023 Harald Schneider - marketmix.com
if [ $APP_ARCH = "x64" ]; then
:
# Handle Intel releases here
# cp SOME_FILE "${APP_RESOURCES}/"
fi
if [ $APP_ARCH = "arm64" ]; then
:
# Handle Apple Silicon releases here
# cp SOME_FILE "${APP_RESOURCES}/"
fi
if [ $APP_ARCH = "universal" ]; then
:
# Handle Universal releases here.
# cp SOME_FILE "${APP_RESOURCES}/"
fi

17
scripts/postproc-win.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# postproc-win.sh 1.0.1
#
# Windows build script post-processor.
#
# This is called from build-win.sh after the app-bundle has been built.
# Use this e.g. to copy additional resources to the app-bundle.
# You can use all variables from the main script here.
#
# (c)2023 Harald Schneider - marketmix.com
if [ $APP_ARCH = "x64" ]; then
:
# Handle Intel releases here
# cp SOME_FILE "${APP_DST}/"
fi

25
scripts/preproc-linux.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
#
# preproc-linux.sh 1.0.1
#
# Linux build script pre-processor.
#
# This is called from build-win.sh before the app-bundle has been built.
# Use this e.g. to preoare platform specific resources.
#
# (c)2024 Harald Schneider - marketmix.com
if [ $APP_ARCH = "x64" ]; then
:
# Handle Intel releases here
fi
if [ $APP_ARCH = "arm64" ]; then
:
# Handle ARM releases here
fi
if [ $APP_ARCH = "armhf" ]; then
:
# Handle ARM hard_float releases here.
fi

25
scripts/preproc-mac.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
#
# preproc-mac.sh 1.0.0
#
# macOS build script pre-processor.
#
# This is called from build-win.sh before the app-bundle has been built.
# Use this e.g. to preoare platform specific resources.
#
# (c)2024 Harald Schneider - marketmix.com
if [ $APP_ARCH = "x64" ]; then
:
# Handle Intel releases here
fi
if [ $APP_ARCH = "arm64" ]; then
:
# Handle Apple Silicon releases here
fi
if [ $APP_ARCH = "universal" ]; then
:
# Handle Universal releases here.
fi

15
scripts/preproc-win.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
#
# preproc-win.sh 1.0.0
#
# Windows build script pre-processor.
#
# This is called from build-win.sh before the app-bundle has been built.
# Use this e.g. to preoare platform specific resources.
#
# (c)2024 Harald Schneider - marketmix.com
if [ $APP_ARCH = "x64" ]; then
:
# Handle Intel releases here
fi

343
scripts/resources/js/neutralino.d.ts vendored Normal file
View file

@ -0,0 +1,343 @@
// Type definitions for Neutralino 5.0.1
// Project: https://github.com/neutralinojs
// Definitions project: https://github.com/neutralinojs/neutralino.js
declare namespace Neutralino {
namespace filesystem {
interface DirectoryEntry {
entry: string;
path: string;
type: string;
}
interface FileReaderOptions {
pos: number;
size: number;
}
interface DirectoryReaderOptions {
recursive: boolean;
}
interface OpenedFile {
id: number;
eof: boolean;
pos: number;
lastRead: number;
}
interface Stats {
size: number;
isFile: boolean;
isDirectory: boolean;
createdAt: number;
modifiedAt: number;
}
interface Watcher {
id: number;
path: string;
}
function createDirectory(path: string): Promise<void>;
function remove(path: string): Promise<void>;
function writeFile(path: string, data: string): Promise<void>;
function appendFile(path: string, data: string): Promise<void>;
function writeBinaryFile(path: string, data: ArrayBuffer): Promise<void>;
function appendBinaryFile(path: string, data: ArrayBuffer): Promise<void>;
function readFile(path: string, options?: FileReaderOptions): Promise<string>;
function readBinaryFile(path: string, options?: FileReaderOptions): Promise<ArrayBuffer>;
function openFile(path: string): Promise<number>;
function createWatcher(path: string): Promise<number>;
function removeWatcher(id: number): Promise<number>;
function getWatchers(): Promise<Watcher[]>;
function updateOpenedFile(id: number, event: string, data?: any): Promise<void>;
function getOpenedFileInfo(id: number): Promise<OpenedFile>;
function readDirectory(path: string, options?: DirectoryReaderOptions): Promise<DirectoryEntry[]>;
function copy(source: string, destination: string): Promise<void>;
function move(source: string, destination: string): Promise<void>;
function getStats(path: string): Promise<Stats>;
}
namespace os {
interface ExecCommandOptions {
stdIn?: string;
background?: boolean;
cwd?: string;
}
interface ExecCommandResult {
pid: number;
stdOut: string;
stdErr: string;
exitCode: number;
}
interface SpawnedProcess {
id: number;
pid: number;
}
interface Envs {
[key: string]: string;
}
interface OpenDialogOptions {
multiSelections?: boolean;
filters?: Filter[];
defaultPath?: string;
}
interface FolderDialogOptions {
defaultPath?: string;
}
interface SaveDialogOptions {
forceOverwrite?: boolean;
filters?: Filter[];
defaultPath?: string;
}
interface Filter {
name: string;
extensions: string[];
}
interface TrayOptions {
icon: string;
menuItems: TrayMenuItem[];
}
interface TrayMenuItem {
id?: string;
text: string;
isDisabled?: boolean;
isChecked?: boolean;
}
enum Icon {
WARNING = "WARNING",
ERROR = "ERROR",
INFO = "INFO",
QUESTION = "QUESTION"
}
enum MessageBoxChoice {
OK = "OK",
OK_CANCEL = "OK_CANCEL",
YES_NO = "YES_NO",
YES_NO_CANCEL = "YES_NO_CANCEL",
RETRY_CANCEL = "RETRY_CANCEL",
ABORT_RETRY_IGNORE = "ABORT_RETRY_IGNORE"
}
type KnownPath = "config" | "data" | "cache" | "documents" | "pictures" | "music" | "video" | "downloads" | "savedGames1" | "savedGames2";
function execCommand(command: string, options?: ExecCommandOptions): Promise<ExecCommandResult>;
function spawnProcess(command: string, cwd?: string): Promise<SpawnedProcess>;
function updateSpawnedProcess(id: number, event: string, data?: any): Promise<void>;
function getSpawnedProcesses(): Promise<SpawnedProcess[]>;
function getEnv(key: string): Promise<string>;
function getEnvs(): Promise<Envs>;
function showOpenDialog(title?: string, options?: OpenDialogOptions): Promise<string[]>;
function showFolderDialog(title?: string, options?: FolderDialogOptions): Promise<string>;
function showSaveDialog(title?: string, options?: SaveDialogOptions): Promise<string>;
function showNotification(title: string, content: string, icon?: Icon): Promise<void>;
function showMessageBox(title: string, content: string, choice?: MessageBoxChoice, icon?: Icon): Promise<string>;
function setTray(options: TrayOptions): Promise<void>;
function open(url: string): Promise<void>;
function getPath(name: KnownPath): Promise<string>;
}
namespace computer {
interface MemoryInfo {
total: number;
available: number;
}
interface KernelInfo {
variant: string;
version: string;
}
interface OSInfo {
name: string;
description: string;
version: string;
}
interface CPUInfo {
vendor: string;
model: string;
frequency: number;
architecture: string;
logicalThreads: number;
physicalCores: number;
physicalUnits: number;
}
interface Display {
id: number;
resolution: Resolution;
dpi: number;
bpp: number;
refreshRate: number;
}
interface Resolution {
width: number;
height: number;
}
interface MousePosition {
x: number;
y: number;
}
function getMemoryInfo(): Promise<MemoryInfo>;
function getArch(): Promise<string>;
function getKernelInfo(): Promise<KernelInfo>;
function getOSInfo(): Promise<OSInfo>;
function getCPUInfo(): Promise<CPUInfo>;
function getDisplays(): Promise<Display[]>;
function getMousePosition(): Promise<MousePosition>;
}
namespace storage {
function setData(key: string, data: string): Promise<void>;
function getData(key: string): Promise<string>;
function getKeys(): Promise<string[]>;
}
namespace debug {
enum LoggerType {
WARNING = "WARNING",
ERROR = "ERROR",
INFO = "INFO"
}
function log(message: string, type?: LoggerType): Promise<void>;
}
namespace app {
interface OpenActionOptions {
url: string;
}
interface RestartOptions {
args: string;
}
function exit(code?: number): Promise<void>;
function killProcess(): Promise<void>;
function restartProcess(options?: RestartOptions): Promise<void>;
function getConfig(): Promise<any>;
function broadcast(event: string, data?: any): Promise<void>;
function readProcessInput(readAll?: boolean): Promise<string>;
function writeProcessOutput(data: string): Promise<void>;
function writeProcessError(data: string): Promise<void>;
}
namespace window {
interface WindowOptions extends WindowSizeOptions, WindowPosOptions {
title?: string;
icon?: string;
fullScreen?: boolean;
alwaysOnTop?: boolean;
enableInspector?: boolean;
borderless?: boolean;
maximize?: boolean;
hidden?: boolean;
maximizable?: boolean;
useSavedState?: boolean;
exitProcessOnClose?: boolean;
extendUserAgentWith?: string;
processArgs?: string;
}
interface WindowSizeOptions {
width?: number;
height?: number;
minWidth?: number;
minHeight?: number;
maxWidth?: number;
maxHeight?: number;
resizable?: boolean;
}
interface WindowPosOptions {
x: number;
y: number;
}
function setTitle(title: string): Promise<void>;
function getTitle(): Promise<string>;
function maximize(): Promise<void>;
function unmaximize(): Promise<void>;
function isMaximized(): Promise<boolean>;
function minimize(): Promise<void>;
function setFullScreen(): Promise<void>;
function exitFullScreen(): Promise<void>;
function isFullScreen(): Promise<boolean>;
function show(): Promise<void>;
function hide(): Promise<void>;
function isVisible(): Promise<boolean>;
function focus(): Promise<void>;
function setIcon(icon: string): Promise<void>;
function move(x: number, y: number): Promise<void>;
function center(): Promise<void>;
function setDraggableRegion(domElementOrId: string | HTMLElement): Promise<void>;
function unsetDraggableRegion(domElementOrId: string | HTMLElement): Promise<void>;
function setSize(options: WindowSizeOptions): Promise<void>;
function getSize(): Promise<WindowSizeOptions>;
function getPosition(): Promise<WindowPosOptions>;
function setAlwaysOnTop(onTop: boolean): Promise<void>;
function create(url: string, options?: WindowOptions): Promise<void>;
}
namespace events {
interface Response {
success: boolean;
message: string;
}
type Builtin = "ready" | "trayMenuItemClicked" | "windowClose" | "serverOffline" | "clientConnect" | "clientDisconnect" | "appClientConnect" | "appClientDisconnect" | "extClientConnect" | "extClientDisconnect" | "extensionReady" | "neuDev_reloadApp";
function on(event: string, handler: (ev: CustomEvent) => void): Promise<Response>;
function off(event: string, handler: (ev: CustomEvent) => void): Promise<Response>;
function dispatch(event: string, data?: any): Promise<Response>;
function broadcast(event: string, data?: any): Promise<void>;
}
namespace extensions {
interface ExtensionStats {
loaded: string[];
connected: string[];
}
function dispatch(extensionId: string, event: string, data?: any): Promise<void>;
function broadcast(event: string, data?: any): Promise<void>;
function getStats(): Promise<ExtensionStats>;
}
namespace updater {
interface Manifest {
applicationId: string;
version: string;
resourcesURL: string;
}
function checkForUpdates(url: string): Promise<Manifest>;
function install(): Promise<void>;
}
namespace clipboard {
function readText(key: string, data: string): Promise<void>;
function writeText(data: string): Promise<string>;
}
namespace custom {
function getMethods(): Promise<string[]>;
}
interface InitOptions {
exportCustomMethods?: boolean;
}
function init(options?: InitOptions): void;
type ErrorCode = "NE_FS_DIRCRER" | "NE_FS_RMDIRER" | "NE_FS_FILRDER" | "NE_FS_FILWRER" | "NE_FS_FILRMER" | "NE_FS_NOPATHE" | "NE_FS_COPYFER" | "NE_FS_MOVEFER" | "NE_OS_INVMSGA" | "NE_OS_INVKNPT" | "NE_ST_INVSTKY" | "NE_ST_STKEYWE" | "NE_RT_INVTOKN" | "NE_RT_NATPRME" | "NE_RT_APIPRME" | "NE_RT_NATRTER" | "NE_RT_NATNTIM" | "NE_CL_NSEROFF" | "NE_EX_EXTNOTC" | "NE_UP_CUPDMER" | "NE_UP_CUPDERR" | "NE_UP_UPDNOUF" | "NE_UP_UPDINER";
interface Error {
code: ErrorCode;
message: string;
}
}
// --- globals ---
/** Mode of the application: window, browser, cloud, or chrome */
declare const NL_MODE: string;
/** Application port */
declare const NL_PORT: number;
/** Command-line arguments */
declare const NL_ARGS: string[];
/** Basic authentication token */
declare const NL_TOKEN: string;
/** Neutralinojs client version */
declare const NL_CVERSION: string;
/** Application identifier */
declare const NL_APPID: string;
/** Application version */
declare const NL_APPVERSION: string;
/** Application path */
declare const NL_PATH: string;
/** Returns true if extensions are enabled */
declare const NL_EXTENABLED: boolean;
/** Operating system name: Linux, Windows, Darwin, FreeBSD, or Uknown */
declare const NL_OS: string;
/** CPU architecture: x64, arm, itanium, ia32, or unknown */
declare const NL_ARCH: string;
/** Neutralinojs server version */
declare const NL_VERSION: string;
/** Current working directory */
declare const NL_CWD: string;
/** Identifier of the current process */
declare const NL_PID: string;
/** Source of application resources: bundle or directory */
declare const NL_RESMODE: string;
/** Release commit of the client library */
declare const NL_CCOMMIT: string;
/** An array of custom methods */
declare const NL_CMETHODS: string[];