diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..0604722db --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,185 @@ +name: Build Configurator +# Don't enable CI on push, just on PR. If you +# are working on the main repo and want to trigger +# a CI build submit a draft PR. +on: pull_request + +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAME=inav-configurator_linux_x64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + - uses: actions/setup-node@v4 + with: + node-version: 20 + check-latest: true + cache: 'npm' + - name: Install dependencies + run: sudo apt-get update && sudo apt-get -y install dpkg fakeroot rpm build-essential libudev-dev + - name: Install deps + uses: nick-fields/retry@v2 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build Linux + run: npm run make + - name: Upload Linux deb + uses: actions/upload-artifact@v3 + with: + name: ${{ env.BUILD_NAME }}_DEB + path: ./out/make/deb/x64/*.deb + - name: Upload Linux rpm + uses: actions/upload-artifact@v3 + with: + name: ${{ env.BUILD_NAME }}_RPM + path: ./out/make/rpm/x64/*.rpm + - name: Upload Linux zip + uses: actions/upload-artifact@v3 + with: + name: ${{ env.BUILD_NAME }}_ZIP + path: ./out/make/zip/linux/x64/*.zip + build-mac: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEx64=inav-configurator_darwin_x64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEarm64=inav-configurator_darwin_arm64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + - uses: actions/setup-node@v4 + with: + node-version: 20 + check-latest: true + cache: 'npm' + # Workaround due to a bug in node-gyp: https://github.com/electron/rebuild/issues/1116 + - name: Install Setuptools + run: pip install setuptools + - name: Install deps + uses: nick-fields/retry@v2 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build MacOS x64 + run: npm run make -- --arch="x64" + - name: Build MacOS arm64 + run: npm run make -- --arch="arm64" + - name: Upload MacOS x64 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_ZIP + path: ./out/make/zip/darwin/x64/*.zip + - name: Upload MacOS x64 dmg + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_DMG + path: ./out/make/*x64*.dmg + - name: Upload MacOS arm64 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEarm64}}_ZIP + path: ./out/make/zip/darwin/arm64/*.zip + - name: Upload MacOS arm64 dmg + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEarm64}}_DMG + path: ./out/make/*arm64*.dmg + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Install linux utils + run: choco install --force -y awk grep sed + - name: Setup environment + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + run: | + # This is the hash of the commit for the PR + # when the action is triggered by PR, empty otherwise + COMMIT_ID=${{ github.event.pull_request.head.sha }} + # This is the hash of the commit when triggered by push + # but the hash of refs/pull//merge, which is different + # from the hash of the latest commit in the PR, that's + # why we try github.event.pull_request.head.sha first + COMMIT_ID=${COMMIT_ID:-${{ github.sha }}} + BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID}) + VERSION=$(grep version package.json | sed 's/.*"\([0-9][0-9]*.[0-9]*.[0-9]*\)".*/\1/g') + echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV + echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEx64=inav-configurator_win32_x64_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + echo "BUILD_NAMEia32=inav-configurator_win32_ia32_${VERSION}_${BUILD_SUFFIX}" >> $GITHUB_ENV + shell: bash + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + - uses: engineerd/configurator@v0.0.10 + with: + name: "Wix Toolset 3.1.4" + url: "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" + pathInArchive: "/" + - name: Install deps + uses: nick-fields/retry@v2 + with: + max_attempts: 3 + retry_on: error + command: npm install + timeout_minutes: 10 + - name: Build Win32 x64 + run: npm run make -- --arch="x64" + - name: Build win32 ia32 + run: npm run make -- --arch="ia32" + - name: Upload Windows x64 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_ZIP + path: ./out/make/zip/win32/x64/*.zip + - name: Upload Windows x64 msi + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEx64}}_MSI + path: ./out/make/wix/x64/*.msi + - name: Upload Windows ia32 zip + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEia32}}_ZIP + path: ./out/make/zip/win32/ia32/*.zip + - name: Upload Windows ia32 msi + uses: actions/upload-artifact@v4 + with: + name: ${{env.BUILD_NAMEia32}}_MSI + path: ./out/make/wix/ia32/*.msi + \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec45237d6..fc5c7174c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,14 +5,6 @@ npm-debug.log .idea/ npm-debug.log inav-configurator.iml -# Generated scripts and styles -/build -# Used by nw-builder to download runtimes -/cache -# Where we put the final app directory structure -/dist -# Path where the NW.js apps get built -/apps -/.vscode/ -.eslintrc.json -/.project +/out +.eslintrc.json +/.project diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..f8efa898f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug Configurator", + "runtimeExecutable": "${workspaceFolder}/node_modules/@electron-forge/cli/script/vscode.sh", + "windows": { + "runtimeExecutable": "${workspaceFolder}/node_modules/@electron-forge/cli/script/vscode.cmd", + + }, + "args": ["--inspect"], + "env": { + "NODE_ENV": "development", + "NODE_PATH": "${workspaceFolder}/js/" + }, + "cwd": "${workspaceFolder}", + "console": "integratedTerminal" + } + ] + } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..889586c40 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "problemMatcher": [], + "label": "npm: start", + "detail": "node node_modules/gulp/bin/gulp.js build && node node_modules/nw/bin/nw .", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index d84f356d8..7f07253b9 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,6 @@ everything, the hardware is not working, or you have any other _support_ problem * [RC Groups Support](https://www.rcgroups.com/forums/showthread.php?2495732-Cleanflight-iNav-(navigation-rewrite)-project) * [INAV Official on Telegram](https://t.me/INAVFlight) -## INAV Configurator starts minimized, what should I do? - -You have to remove the `C:\Users%Your_UserName%\AppData\Local\inav-configurator` folder and all its content. - -[https://www.youtube.com/watch?v=XMoULyiFDp4](https://www.youtube.com/watch?v=XMoULyiFDp4) - -Alternatively, on Windows with PowerShell, you can use the `post_install_cleanup.ps1` script that will do the cleaning. (thank you, James Cherrill) - ## Installation Depending on the target operating system, _INAV Configurator_ is distributed as a _standalone_ application or Chrome App. @@ -33,10 +25,12 @@ Depending on the target operating system, _INAV Configurator_ is distributed as ### Windows 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) -1. Download Configurator for Windows platform (win32 or win64 is present) -1. Extract ZIP archive -1. Run the INAV Configurator app from the unpacked folder -1. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run +2. Download Configurator for Windows platform (win32 or win64 is present) +3. Install + * Extract ZIP archive and run the INAV Configurator app from the unpacked folder + * OR just use the setup program `INAV Configurator.msi` + +4. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run ### Linux @@ -44,12 +38,12 @@ Depending on the target operating system, _INAV Configurator_ is distributed as 2. Download Configurator for Linux platform (linux32 and linux64 are present) * **.rpm** is the Fedora installation file. Just download and install using `sudo dnf localinstall /path/to/INAV-Configurator_linux64-x.y.z-x86_64.rpm` or open it with a package manager (e.g. via Files) * **.deb** is the Debian/Ubuntu installation file. Just download and install using `sudo apt install /path/to/INAV-Configurator_linux64_x.y.z.deb` or open it with a package manager (e.g. via the File Manager) - * **.tar.gz** is a universal archive. Download and continue with these instructions to install -3. Change to the directory containing the downloaded **tar.gz** file + * **.zip** is a universal archive. Download and continue with these instructions to install +3. Change to the directory containing the downloaded **zip** file 4. download [this](https://raw.githubusercontent.com/iNavFlight/inav-configurator/master/assets/linux/inav-configurator.desktop) file to the same directory. Its filename should be `inav-configurator.desktop`. -5. Extract **tar.gz** archive +5. Extract **zip** archive ``` -tar -C /tmp/ -xf INAV-Configurator_linuxNN_x.y.z.tar.gz +unzip INAV-Configurator_linuxNN_x.y.z.tar.gz -d /tmp/ ``` **NN** is the bits of your OS. **x.y.z** is the INAV Configurator version number. @@ -73,26 +67,15 @@ sudo mv inav-configurator.desktop /usr/share/applications/ ``` 10. Make the following files executable: * inav-configurator `chmod +x /opt/inav/inav-configurator/inav-configurator` - * (5.0.0+) chrome_crashpad_handler `chmod +x /opt/inav/inav-configurator/chrome_crashpad_handler` 11. Run the INAV Configurator app from the unpacked folder `/opt/inav/inav-configurator/inav-configurator` -#### Notes - -On some Linux distros, you may be missing `libatomic` and/or `NW.JS` (especially `libnode.so`) dependencies. If so, please install `libatomic` using your distro's package manager, e.g: - -* Arch Linux: `sudo pacman -S --needed libatomic_ops` -* Debian / Ubuntu: `sudo apt install libatomic1` -* Fedora: `sudo dnf install libatomic` - -1. Don't forget to add your user to the dialout group "sudo usermod -aG dialout YOUR_USERNAME" for serial access -2. If you have 3D model animation problems, enable "Override software rendering list" in Chrome flags chrome://flags/#ignore-gpu-blacklist - ### Mac 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) -1. Download Configurator for the Mac platform -1. Extract ZIP archive -1. Run INAV Configurator +2. Download Configurator for the Mac platform +3. Install + * Extract ZIP archive and run INAV Configurator + * OR use the DMG package for installation ## Building and running INAV Configurator locally (for development) @@ -100,30 +83,33 @@ For local development, the **node.js** build system is used. 1. Install node.js 1. From the project folder run `npm install` -1. To build the JS and CSS files and start the configurator: - - With NW.js: Run `npm start`. - - With Chrome: Run `npm run gulp`. Then open `chrome://extensions`, enable - the `Developer mode`, click on the `Load unpacked extension...` button, and select the `inav-configurator` directory. +1. To build the and start the configurator: + - Run `npm start`. + +To build the App run `npm run make` to build for your platform. -Other tasks are also defined in `gulpfile.js`. To run a task, use `node ./node_modules/gulp/bin/gulp.js task-name`. Available ones are: +Options: +* Architecture: --arch - Allowed values are: "ia32", "x64", "armv7l", "arm64", "universal", or "mips64el". -- **build**: Generate JS and CSS output files used by the configurator from their sources. It must be run whenever changes are made to any `.js` or `.css` files in order to have those changes appear -in the configurator. If new files are added, they must be included in `gulpfile.js`. See the comments at the top of `gulpfile.js` to learn how to do so. See also the `watch` task. -- **watch**: Watch JS and CSS sources for changes and run the `build` task whenever they're edited. -- **dist**: Create a distribution of the app (valid for packaging both as a Chrome app or NW.js app) -in the `./dist/` directory. -- **release**: Create NW.js apps for each supported platform (win32, osx64 and linux64) in the `./apps` -directory. Running this task on macOS or Linux requires Wine since it's needed to set the icon -for the Windows app. If you don't have Wine installed, you can create a release by running the **release-only-Linux** task. -
`--installer` argument can be added to build installers for a particular OS. NOTE: MacOS Installer can be built with MacOS only. +See [Electron Forge CLI Documentation](https://www.electronforge.io/cli#options-2) for details -To build a specific release, use the command `release --platform="win64"` for example. +To build the setup program for windows, you have to install [WiX Toolset V3](https://github.com/wixtoolset/wix3/releases) and add the `bin` folder to you `PATH`, e.g. +```C:\Program Files (x86)\WiX Toolset v3.14\bin``` + +To build deb and rpm packages for Linux, you have to install the following packages: +- Ubuntu/Debian: `dpkg, fakeroot, rpmbuild, build-essential, libudev-dev` +- OpenSuse/Fedora: `dpkg, fakeroot, rpmbuild, systemd-devel, devel-basis (zypper install -t pattern devel_basis), zip` + +Example (note the double -- ): +``` npm run make -- --arch="x64" ``` ### Running with debug | Inspector -To be able to open Inspector, you will need SDK flavours of NW.js +To be able to open Inspector, set envorinment variable `NODE_ENV` to `develpoment` or set the flag directly when run `npm start`: + +```NODE_ENV=development npm start``` -`npm install nw@0.61.0 --nwjs_build_type=sdk` +Or use vscode and start a debug session `Debug Configurator` (Just hit F5!) ## Different map providers diff --git a/_config.yml b/_config.yml deleted file mode 100644 index c74188174..000000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-slate \ No newline at end of file diff --git a/assets/linux/inav-configurator.desktop b/assets/linux/inav-configurator.desktop index 40b0c778d..3b8124bcd 100644 --- a/assets/linux/inav-configurator.desktop +++ b/assets/linux/inav-configurator.desktop @@ -2,7 +2,7 @@ Name=INAV Configurator Comment=Crossplatform configuration tool for the INAV flight control system Exec=/opt/inav/inav-configurator/inav-configurator -Icon=/opt/inav/inav-configurator/icon/inav_icon_128.png +Icon=/opt/inav/inav-configurator/resources/app/assets/linux/icon/inav_icon_128.png Terminal=false Type=Application Categories=Utility \ No newline at end of file diff --git a/assets/windows/background.jpg b/assets/windows/background.jpg new file mode 100644 index 000000000..6e044d5b6 Binary files /dev/null and b/assets/windows/background.jpg differ diff --git a/assets/windows/banner.jpg b/assets/windows/banner.jpg new file mode 100644 index 000000000..66c3f04f8 Binary files /dev/null and b/assets/windows/banner.jpg differ diff --git a/assets/windows/inav_installer.bmp b/assets/windows/inav_installer.bmp deleted file mode 100644 index d65f39407..000000000 Binary files a/assets/windows/inav_installer.bmp and /dev/null differ diff --git a/assets/windows/inav_installer_small.bmp b/assets/windows/inav_installer_small.bmp deleted file mode 100644 index 535303fb4..000000000 Binary files a/assets/windows/inav_installer_small.bmp and /dev/null differ diff --git a/assets/windows/installer.iss b/assets/windows/installer.iss deleted file mode 100644 index 89f56efa7..000000000 --- a/assets/windows/installer.iss +++ /dev/null @@ -1,169 +0,0 @@ -; ------------------------------------------ -; Installer for INAV -; ------------------------------------------ -; It receives from the command line with /D the parameters: -; version -; archName -; archAllowed -; archInstallIn64bit -; sourceFolder -; targetFolder - -#define ApplicationName "INAV Configurator" -#define CompanyName "The INAV open source project" -#define CompanyUrl "https://github.com/iNavFlight/inav" -#define ExecutableFileName "inav-configurator.exe" -#define GroupName "INAV" -#define InstallerFileName "INAV-Configurator_" + archName + "_" + version -#define SourcePath "..\..\" + sourceFolder + "\inav-configurator\" + archName -#define TargetFolderName "INAV-Configurator" -#define UpdatesUrl "https://github.com/iNavFlight/inav-configurator/releases" - -[CustomMessages] -AppName=inav-configurator -LaunchProgram=Start {#ApplicationName} - -[Files] -Source: "{#SourcePath}\*"; DestDir: "{app}"; Flags: recursesubdirs - -[Icons] -; Programs group -Name: "{group}\{#ApplicationName}"; Filename: "{app}\{#ExecutableFileName}"; -; Desktop icon -Name: "{autodesktop}\{#ApplicationName}"; Filename: "{app}\{#ExecutableFileName}"; -; Non admin users, uninstall icon -Name: "{group}\Uninstall {#ApplicationName}"; Filename: "{uninstallexe}"; Check: not IsAdminInstallMode - -[Languages] -; English default, it must be first -Name: "en"; MessagesFile: "compiler:Default.isl" -; Official languages -;Name: "ca"; MessagesFile: "compiler:Languages\Catalan.isl" -;Name: "da"; MessagesFile: "compiler:Languages\Danish.isl" -;Name: "de"; MessagesFile: "compiler:Languages\German.isl" -;Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl" -;Name: "fr"; MessagesFile: "compiler:Languages\French.isl" -;Name: "it"; MessagesFile: "compiler:Languages\Italian.isl" -;Name: "ja"; MessagesFile: "compiler:Languages\Japanese.isl" -;Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl" -;Name: "pt"; MessagesFile: "compiler:Languages\Portuguese.isl" -;Name: "pl"; MessagesFile: "compiler:Languages\Polish.isl" -;Name: "ru"; MessagesFile: "compiler:Languages\Russian.isl" -; Not official. Sometimes not updated to latest version (strings missing) -;Name: "ga"; MessagesFile: "unofficial_inno_languages\Galician.isl" -;Name: "eu"; MessagesFile: "unofficial_inno_languages\Basque.isl" -;Name: "hr"; MessagesFile: "unofficial_inno_languages\Croatian.isl" -;Name: "hu"; MessagesFile: "unofficial_inno_languages\Hungarian.isl" -;Name: "id"; MessagesFile: "unofficial_inno_languages\Indonesian.isl" -;Name: "ko"; MessagesFile: "unofficial_inno_languages\Korean.isl" -;Name: "lv"; MessagesFile: "unofficial_inno_languages\Latvian.isl" -;Name: "sv"; MessagesFile: "unofficial_inno_languages\Swedish.isl" -;Name: "zh_CN"; MessagesFile: "unofficial_inno_languages\ChineseSimplified.isl" -;Name: "zh_TW"; MessagesFile: "unofficial_inno_languages\ChineseTraditional.isl" -; Not available -; pt_BR (Portuguese Brasileiro) - -[Run] -; Add a checkbox to start the app after installed -Filename: {app}\{cm:AppName}.exe; Description: {cm:LaunchProgram,{cm:AppName}}; Flags: nowait postinstall skipifsilent - -[Setup] -AppId=2e5662ca-1fb3-8f1e-a7e1-e390add7a19d -AppName={#ApplicationName} -AppPublisher={#CompanyName} -AppPublisherURL={#CompanyUrl} -AppUpdatesURL={#UpdatesUrl} -AppVersion={#version} -ArchitecturesAllowed={#archAllowed} -ArchitecturesInstallIn64BitMode={#archInstallIn64bit} -Compression=lzma2 -DefaultDirName={autopf}\{#GroupName}\{#TargetFolderName} -DefaultGroupName={#GroupName}\{#ApplicationName} -LicenseFile=..\..\LICENSE -MinVersion=6.2 -OutputBaseFilename={#InstallerFileName} -OutputDir=..\..\{#targetFolder}\ -PrivilegesRequiredOverridesAllowed=commandline dialog -SetupIconFile=inav_installer_icon.ico -ShowLanguageDialog=yes -SolidCompression=yes -UninstallDisplayIcon={app}\{#ExecutableFileName} -UninstallDisplayName={#ApplicationName} -WizardImageFile=inav_installer.bmp -WizardSmallImageFile=inav_installer_small.bmp -WizardStyle=modern - -[Code] -function GetOldNsisUninstallerPath(): String; -var - RegKey: String; -begin - Result := ''; - // Look into the different registry entries: win32, win64 and without user rights - if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\INAV Configurator', 'UninstallString', Result) then - begin - if not RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\INAV Configurator', 'UninstallString', Result) then - begin - RegQueryStringValue(HKCU, 'SOFTWARE\INAV\INAV Configurator', 'UninstallString', Result) - end; - end; -end; - -function GetQuietUninstallerPath(): String; -var - RegKey: String; -begin - Result := ''; - RegKey := Format('%s\%s_is1', ['Software\Microsoft\Windows\CurrentVersion\Uninstall', '{#emit SetupSetting("AppId")}']); - if not RegQueryStringValue(HKEY_LOCAL_MACHINE, RegKey, 'QuietUninstallString', Result) then - begin - RegQueryStringValue(HKEY_CURRENT_USER, RegKey, 'QuietUninstallString', Result); - end; -end; - -function InitializeSetup(): Boolean; -var - ResultCode: Integer; - ParameterStr : String; - UninstPath : String; -begin - - Result := True; - - // Check if the application is already installed by the old NSIS installer, and uninstall it - UninstPath := GetOldNsisUninstallerPath(); - - // Found, start uninstall - if UninstPath <> '' then - begin - - UninstPath := RemoveQuotes(UninstPath); - - // Add this parameter to not return until uninstall finished. The drawback is that the uninstaller file is not deleted - ParameterStr := '_?=' + ExtractFilePath(UninstPath); - - if Exec(UninstPath, ParameterStr, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then - begin - // Delete the unistaller file and empty folders. Not deleting the files. - DeleteFile(UninstPath); - DelTree(ExtractFilePath(UninstPath), True, False, True); - end - else begin - Result := False; - MsgBox('Error uninstalling old Configurator ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK); - end; - end - else begin - - // Search for new Inno Setup installations - UninstPath := GetQuietUninstallerPath(); - if UninstPath <> '' then - begin - if not Exec('>', UninstPath, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then - begin - Result := False; - MsgBox('Error uninstalling Configurator ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK); - end; - end; - end; -end; \ No newline at end of file diff --git a/assets/windows/wix.xml b/assets/windows/wix.xml new file mode 100644 index 000000000..acace5f6e --- /dev/null +++ b/assets/windows/wix.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eventPage.js b/eventPage.js deleted file mode 100755 index 2843cdbf0..000000000 --- a/eventPage.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - If an id is also specified and a window with a matching id has been shown before, the remembered bounds of the window will be used instead. -*/ -'use strict'; - -function startApplication() { - var applicationStartTime = new Date().getTime(); - - chrome.app.window.create('main.html', { - id: 'main-window', - frame: 'chrome', - innerBounds: { - minWidth: 1024, - minHeight: 550 - } - }, function (createdWindow) { - createdWindow.contentWindow.addEventListener('load', function () { - createdWindow.contentWindow.catch_startup_time(applicationStartTime); - }); - - createdWindow.onClosed.addListener(function () { - // automatically close the port when application closes - // save connectionId in separate variable before createdWindow.contentWindow is destroyed - var connectionId = createdWindow.contentWindow.CONFIGURATOR.connection.connectionId, - valid_connection = createdWindow.contentWindow.CONFIGURATOR.connectionValid, - mincommand = createdWindow.contentWindow.MISC.mincommand; - - console.log("EP:" + connectionId); - if (connectionId && valid_connection) { - // code below is handmade MSP message (without pretty JS wrapper), it behaves exactly like MSP.send_message - // sending exit command just in case the cli tab was open. - // reset motors to default (mincommand) - - var bufferOut = new ArrayBuffer(5), - bufView = new Uint8Array(bufferOut); - - bufView[0] = 0x65; // e - bufView[1] = 0x78; // x - bufView[2] = 0x69; // i - bufView[3] = 0x74; // t - bufView[4] = 0x0D; // enter - - chrome.serial.send(connectionId, bufferOut, function () { console.log('Send exit') }); - - setTimeout(function() { - bufferOut = new ArrayBuffer(22); - bufView = new Uint8Array(bufferOut); - var checksum = 0; - - bufView[0] = 36; // $ - bufView[1] = 77; // M - bufView[2] = 60; // < - bufView[3] = 16; // data length - bufView[4] = 214; // MSP_SET_MOTOR - - checksum = bufView[3] ^ bufView[4]; - - for (var i = 0; i < 16; i += 2) { - bufView[i + 5] = mincommand & 0x00FF; - bufView[i + 6] = mincommand >> 8; - - checksum ^= bufView[i + 5]; - checksum ^= bufView[i + 6]; - } - - bufView[5 + 16] = checksum; - - chrome.serial.send(connectionId, bufferOut, function (sendInfo) { - chrome.serial.disconnect(connectionId, function (result) { - console.log('SERIAL: Connection closed - ' + result); - }); - }); - }, 100); - } else if (connectionId) { - chrome.serial.disconnect(connectionId, function (result) { - console.log('SERIAL: Connection closed - ' + result); - }); - } - }); - }); -} - -chrome.app.runtime.onLaunched.addListener(startApplication); - -chrome.runtime.onInstalled.addListener(function (details) { - if (details.reason == 'update') { - var previousVersionArr = details.previousVersion.split('.'), - currentVersionArr = chrome.runtime.getManifest().version.split('.'); - - // only fire up notification sequence when one of the major version numbers changed - if (currentVersionArr[0] > previousVersionArr[0] || currentVersionArr[1] > previousVersionArr[1]) { - chrome.storage.local.get('update_notify', function (result) { - if (result.update_notify === 'undefined' || result.update_notify) { - var manifest = chrome.runtime.getManifest(); - var options = { - priority: 0, - type: 'basic', - title: manifest.name, - message: chrome.i18n.getMessage('notifications_app_just_updated_to_version', [manifest.version]), - iconUrl: '/images/icon_128.png', - buttons: [{'title': chrome.i18n.getMessage('notifications_click_here_to_start_app')}] - }; - - chrome.notifications.create('baseflight_update', options, function (notificationId) { - // empty - }); - } - }); - } - } -}); - -chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) { - if (notificationId == 'baseflight_update') { - startApplication(); - } -}); \ No newline at end of file diff --git a/forge.config.js b/forge.config.js new file mode 100644 index 000000000..092043bee --- /dev/null +++ b/forge.config.js @@ -0,0 +1,113 @@ +const path = require('path'); +const fs = require('fs'); + +module.exports = { + packagerConfig: { + executableName: "inav-configurator", + asar: false, + icon: 'images/inav', + ignore: [ + "^(\/\.vscode$)", + "^(\/support$)", + ".gitattributes", + ".gitignore", + "3D_model_creation.md", + "LICENSE", + "MAPPROXY.md", + "package-lock.json", + "README.md", + "inav_icon_128.psd", + ] + }, + hooks: { + // Uniform artifact file names + postMake: async (config, makeResults) => { + makeResults.forEach(result => { + var baseName = `${result.packageJSON.productName.replace(' ', '-')}_${result.platform}_${result.arch}_${result.packageJSON.version}`; + result.artifacts.forEach(artifact => { + var artifactStr = artifact.toString(); + var newPath = path.join(path.dirname(artifactStr), baseName + path.extname(artifactStr)); + fs.renameSync(artifactStr, newPath); + console.log('Artifact: ' + newPath); + }); + }); + } + }, + rebuildConfig: {}, + makers: [ + { + name: '@electron-forge/maker-wix', + config: { + name: "INAV Configurator", + shortName: "INAV", + exe: "inav-configurator", + description: "Configurator for the open source flight controller software INAV.", + programFilesFolderName: "inav-configurator", + shortcutFolderName: "INAV", + manufacturer: "The INAV open source project", + appUserModelId: "com.inav.configurator", + icon: path.join(__dirname, "./assets/windows/inav_installer_icon.ico"), + upgradeCode: "13606ff3-b0bc-4dde-8fac-805bc8aed2f8", + ui : { + enabled: false, + chooseDirectory: true, + images: { + background: path.join(__dirname, "./assets/windows/background.jpg"), + banner: path.join(__dirname, "./assets/windows/banner.jpg") + } + }, + // Standard WiX template appends the unsightly "(Machine - WSI)" to the name, so use our own template + beforeCreate: (msiCreator) => { + return new Promise((resolve, reject) => { + fs.readFile(path.join(__dirname,"./assets/windows/wix.xml"), "utf8" , (err, content) => { + if (err) { + reject (err); + } + msiCreator.wixTemplate = content; + resolve(); + }); + }); + } + } + }, + { + name: '@electron-forge/maker-dmg', + config: { + name: "INAV Configurator", + background: "./assets/osx/dmg-background.png" + } + }, + { + name: '@electron-forge/maker-zip', + platforms: ['win32', 'linux', 'darwin'], + }, + { + name: '@electron-forge/maker-deb', + config: { + options: { + name: "inav-configurator", + productName: "INAV Configurator", + categories: ["Utility"], + icon: "./assets/linux/icon/inav_icon_128.png", + description: "Configurator for the open source flight controller software INAV.", + homepage: "https://github.com/inavflight/", + + } + }, + }, + { + name: '@electron-forge/maker-rpm', + config: { + options: { + name: "inav-configurator", + productName: "INAV Configurator", + license: "GPL-3.0", + categories: ["Utility"], + icon: "./assets/linux/icon/inav_icon_128.png", + description: "Configurator for the open source flight controller software INAV.", + homepage: "https://github.com/inavflight/", + } + }, + }, + ], +}; diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index fca5fa686..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,754 +0,0 @@ -'use strict'; - -var child_process = require('child_process'); -var fs = require('fs'); -var path = require('path'); -var minimist = require('minimist'); - -var archiver = require('archiver'); -var del = require('del'); -var NwBuilder = require('nw-builder'); -var semver = require('semver'); - -var gulp = require('gulp'); -var concat = require('gulp-concat'); - -const commandExistsSync = require('command-exists').sync; - -// Each key in the *sources* variable must be an array of -// the source files that will be combined into a single -// file and stored in *outputDir*. Each key in *sources* -// must be also present in *output*, whose value indicates -// the filename for the output file which combines the -// contents of the source files. -// -// Keys must be camel cased and end with either 'Css' or -// 'Js' (e.g. someSourcesCss or someSourcesJs). For each -// key, a build task will be generated named by prepending -// 'build-' and converting the key to dash-separated words -// (e.g. someSourcesCss will generate build-some-sources-css). -// -// Tasks with names ending with '-js' will be executed by the -// build-all-js task, while the ones ending with '-css' will -// be done by build-all-css. There's also a build task which -// runs both build-all-css and build-all-js. -// -// The watch task will monitor any files mentioned in the *sources* -// variable and regenerate the corresponding output file when -// they change. -// -// See README.md for details on the other tasks. - -var sources = {}; - -sources.css = [ - './main.css', - './js/libraries/jquery.nouislider.min.css', - './js/libraries/jquery.nouislider.pips.min.css', - './js/libraries/flightindicators.css', - './src/css/tabs/*.css', - './src/css/opensans_webfontkit/fonts.css', - './src/css/font-awesome/css/font-awesome.css', - './src/css/dropdown-lists/css/style_lists.css', - './js/libraries/switchery/switchery.css', - './js/libraries/jbox/jBox.css', - './node_modules/openlayers/dist/ol.css', - './src/css/logic.css', - './src/css/defaults_dialog.css', - './src/css/groundstation.css', -]; - -sources.js = [ - './js/libraries/google-analytics-bundle.js', - './node_modules/jquery/dist/jquery.min.js', - './node_modules/jquery-ui-npm/jquery-ui.min.js', - './node_modules/marked/lib/marked.js', - './js/libraries/d3.min.js', - './js/libraries/jquery.nouislider.all.min.js', - './node_modules/three/build/three.min.js', - './node_modules/three/examples/js/loaders/GLTFLoader.js', - './node_modules/three/examples/js/controls/OrbitControls.js', - './js/libraries/nw-dialog.js', - './js/libraries/bundle_xml2js.js', - './js/libraries/Projector.js', - './js/libraries/CanvasRenderer.js', - './js/libraries/jquery.flightindicators.js', - './js/libraries/semver.js', - './js/libraries/jbox/jBox.min.js', - './js/libraries/switchery/switchery.js', - './js/libraries/jquery.ba-throttle-debounce.js', - './js/helpers.js', - './node_modules/inflection/inflection.min.js', - './node_modules/bluebird/js/browser/bluebird.min.js', - './js/injected_methods.js', - './js/intervals.js', - './js/timeouts.js', - './js/pid_controller.js', - './js/simple_smooth_filter.js', - './js/walking_average_filter.js', - './js/gui.js', - './js/serialPortHelper.js', - './js/msp/MSPCodes.js', - './js/msp/MSPHelper.js', - './js/msp/MSPchainer.js', - './js/port_handler.js', - './js/connection/connection.js', - './js/connection/connectionBle.js', - './js/connection/connectionSerial.js', - './js/connection/connectionTcp.js', - './js/connection/connectionUdp.js', - './js/servoMixRule.js', - './js/motorMixRule.js', - './js/logicCondition.js', - './js/settings.js', - './js/outputMapping.js', - './js/model.js', - './js/serial_backend.js', - './js/data_storage.js', - './js/fc.js', - './js/msp.js', - './js/protocols/stm32.js', - './js/protocols/stm32usbdfu.js', - './js/localization.js', - './js/boards.js', - './js/servoMixerRuleCollection.js', - './js/motorMixerRuleCollection.js', - './js/logicConditionsCollection.js', - './js/logicConditionsStatus.js', - './js/globalVariablesStatus.js', - './js/programmingPid.js', - './js/programmingPidCollection.js', - './js/programmingPidStatus.js', - './js/vtx.js', - './main.js', - './js/tabs.js', - './tabs/*.js', - './js/eventFrequencyAnalyzer.js', - './js/periodicStatusUpdater.js', - './js/serial_queue.js', - './js/msp_balanced_interval.js', - './tabs/advanced_tuning.js', - './js/peripherals.js', - './js/appUpdater.js', - './js/feature_framework.js', - './js/defaults_dialog.js', - './js/safehomeCollection.js', - './js/safehome.js', - './js/waypointCollection.js', - './js/waypoint.js', - './node_modules/openlayers/dist/ol.js', - './js/libraries/plotly-latest.min.js', - './js/sitl.js', - './js/CliAutoComplete.js', - './node_modules/jquery-textcomplete/dist/jquery.textcomplete.js', - './js/fwApproach.js', - './js/fwApproachCollection.js', - './js/ltmDecoder.js', - './js/groundstation.js' -]; - -sources.receiverCss = [ - './src/css/tabs/receiver_msp.css', - './src/css/opensans_webfontkit/fonts.css', - './js/libraries/jquery.nouislider.min.css', - './js/libraries/jquery.nouislider.pips.min.css', -]; - -sources.receiverJs = [ - './node_modules/jquery/dist/jquery.min.js', - './node_modules/jquery-ui-npm/jquery-ui.min.js', - './js/libraries/jquery.nouislider.all.min.js', - './tabs/receiver_msp.js' -]; - -sources.debugTraceJs = [ - './js/debug_trace.js' -]; - -sources.hexParserJs = [ - './js/workers/hex_parser.js', -]; - -var output = { - css: 'styles.css', - js: 'script.js', - receiverCss: 'receiver-msp.css', - receiverJs: 'receiver-msp.js', - debugTraceJs: 'debug-trace.js', - hexParserJs: 'hex_parser.js', -}; - - -var outputDir = './build/'; -var distDir = './dist/'; -var appsDir = './apps/'; - -function get_task_name(key) { - return 'build-' + key.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();}); -} - -function getArguments() { - return minimist(process.argv.slice(2)); -} - -function getPlatforms() { - const defaultPlatforms = ['win32', 'win64', 'osx64', 'linux32', 'linux64']; - const platform = getArguments().platform; - if (platform) { - if (defaultPlatforms.indexOf(platform) < 0) { - throw new Error(`Invalid platform "${platform}". Available ones are: ${defaultPlatforms}`) - } - return [platform]; - } - return defaultPlatforms; -} - -function execSync() { - const cmd = arguments[0]; - const args = Array.prototype.slice.call(arguments, 1); - const result = child_process.spawnSync(cmd, args, {stdio: 'inherit'}); - if (result.error) { - throw result.error; - } -} - -// Define build tasks dynamically based on the sources -// and output variables. -var buildCssTasks = []; -var buildJsTasks = []; -(function() { - // Convers fooBarBaz to foo-bar-baz - for (var k in output) { - (function (key) { - var name = get_task_name(key); - if (name.endsWith('-css')) { - buildCssTasks.push(name); - } else if (name.endsWith('-js')) { - buildJsTasks.push(name); - } else { - throw 'Invalid task name: "' + name + '": must end with -css or -js'; - } - gulp.task(name, function() { - return gulp.src(sources[key]) - .pipe(concat(output[key])) - .pipe(gulp.dest(outputDir)); - }); - })(k); - } -})(); - -gulp.task('build-all-js', gulp.parallel(buildJsTasks)) -gulp.task('build-all-css', gulp.parallel(buildCssTasks)); -gulp.task('build', gulp.parallel('build-all-css', 'build-all-js')); - -gulp.task('clean', function() { return del(['./build/**', './dist/**'], {force: true}); }); - -// Real work for dist task. Done in another task to call it via -// run-sequence. -gulp.task('dist-build', gulp.series('build', function() { - var distSources = [ - './package.json', // For NW.js - './manifest.json', // For Chrome app - './eventPage.js', - './*.html', - './tabs/*.html', - './images/**/*', - './_locales/**/*', - './build/*', - './src/css/font-awesome/webfonts/*', - './src/css/opensans_webfontkit/*.{eot,svg,ttf,woff,woff2}', - './resources/*.json', - './resources/models/*', - './resources/osd/analogue/*.mcm', - './resources/motor_order/*.svg', - './resources/sitl/windows/*', - './resources/sitl/linux/*' - ]; - return gulp.src(distSources, { base: '.' }) - .pipe(gulp.dest(distDir)); -})); - -gulp.task('dist', gulp.series('clean', 'dist-build')); - -// Create app directories in ./apps -gulp.task('apps', gulp.series('dist', function(done) { - var builder = new NwBuilder({ - files: './dist/**/*', - buildDir: appsDir, - platforms: getPlatforms(), - flavor: 'normal', - macIcns: './images/inav.icns', - winIco: './images/inav.ico', - version: get_nw_version(), - zip: false - }); - builder.on('log', console.log); - builder.build(function (err) { - if (err) { - console.log("Error building NW apps:" + err); - done(); - return; - } - // Package apps as .zip files - done(); - }); -})); - -function get_nw_version() { - return semver.valid(semver.coerce(require('./package.json').dependencies.nw)); -} - -function get_release_filename_base(platform) { - return 'INAV-Configurator_' + platform; -} - -function get_release_filename(platform, ext, addition = '') { - var pkg = require('./package.json'); - return get_release_filename_base(platform) + addition + '_' + pkg.version + '.' + ext; -} - -function build_win_zip(arch) { - return function build_win_zip_proc(done) { - var pkg = require('./package.json'); - - // Create ZIP - console.log(`Creating ${arch} ZIP file...`); - var src = path.join(appsDir, pkg.name, arch); - var output = fs.createWriteStream(path.join(appsDir, get_release_filename(arch, 'zip'))); - var archive = archiver('zip', { - zlib: { level: 9 } - }); - archive.on('warning', function(err) { throw err; }); - archive.on('error', function(err) { throw err; }); - archive.pipe(output); - archive.directory(src, 'INAV Configurator'); - return archive.finalize(); - } -} - -function build_win_iss(arch) { - return function build_win_iss_proc(done) { - if (!getArguments().installer) { - done(); - return null; - } - - // Create Installer - console.log(`Creating ${arch} Installer...`); - const innoSetup = require('@quanle94/innosetup'); - - const APPS_DIR = './apps/'; - const pkg = require('./package.json'); - - // Parameters passed to the installer script - const parameters = []; - - // Extra parameters to replace inside the iss file - parameters.push(`/Dversion=${pkg.version}`); - parameters.push(`/DarchName=${arch}`); - parameters.push(`/DarchAllowed=${(arch === 'win32') ? 'x86 x64' : 'x64'}`); - parameters.push(`/DarchInstallIn64bit=${(arch === 'win32') ? '' : 'x64'}`); - parameters.push(`/DsourceFolder=${APPS_DIR}`); - parameters.push(`/DtargetFolder=${APPS_DIR}`); - - // Show only errors in console - parameters.push(`/Q`); - - // Script file to execute - parameters.push("assets/windows/installer.iss"); - - innoSetup(parameters, {}, - function(error) { - if (error != null) { - console.error(`Installer for platform ${arch} finished with error ${error}`); - } else { - console.log(`Installer for platform ${arch} finished`); - } - done(); - }); - } -} - -gulp.task('release-win32', gulp.series(build_win_zip('win32'), build_win_iss('win32'))); -gulp.task('release-win64', gulp.series(build_win_zip('win64'), build_win_iss('win64'))); - -gulp.task('release-osx64', function(done) { - var pkg = require('./package.json'); - var src = path.join(appsDir, pkg.name, 'osx64', pkg.name + '.app'); - // Check if we want to sign the .app bundle - if (getArguments().codesign) { - // macapptool can be downloaded from - // https://github.com/fiam/macapptool - // - // Make sure the bundle is well formed - execSync('macapptool', '-v', '1', 'fix', src); - // Sign - const codesignArgs = ['macapptool', '-v', '1', 'sign']; - const codesignIdentity = getArguments()['codesign-identity']; - if (codesignIdentity) { - codesignArgs.push('-i', codesignIdentity); - } - codesignArgs.push('-e', 'entitlements.plist'); - codesignArgs.push(src) - execSync.apply(this, codesignArgs); - - // Check if the bundle is signed - const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', src ]; - execSync.apply(this, codesignCheckArgs); - } - - // 'old' .zip mode - if (!getArguments().installer) { - const zipFilename = path.join(appsDir, get_release_filename('macOS', 'zip')); - console.log('Creating ZIP file: ' + zipFilename); - var output = fs.createWriteStream(zipFilename); - var archive = archiver('zip', { - zlib: { level: 9 } - }); - archive.on('warning', function(err) { throw err; }); - archive.on('error', function(err) { throw err; }); - archive.pipe(output); - archive.directory(src, 'INAV Configurator.app'); - output.on('close', function() { - if (getArguments().notarize) { - console.log('Notarizing ZIP file: ' + zipFilename); - const notarizeArgs = ['xcrun', 'notarytool', 'submit']; - notarizeArgs.push(zipFilename); - const notarizationUsername = getArguments()['notarization-username']; - if (notarizationUsername) { - notarizeArgs.push('--apple-id', notarizationUsername) - } else { - throw new Error('Missing notarization username'); - } - const notarizationPassword = getArguments()['notarization-password']; - if (notarizationPassword) { - notarizeArgs.push('--password', notarizationPassword) - } else { - throw new Error('Missing notarization password'); - } - const notarizationTeamId = getArguments()['notarization-team-id']; - if (notarizationTeamId) { - notarizeArgs.push('--team-id', notarizationTeamId) - } else { - throw new Error('Missing notarization Team ID'); - } - notarizeArgs.push('--wait'); - - const notarizationWebhook = getArguments()['notarization-webhook']; - if (notarizationWebhook) { - notarizeArgs.push('--webhook', notarizationWebhook); - } - execSync.apply(this, notarizeArgs); - - console.log('Stapling ZIP file: ' + zipFilename); - const stapleArgs = ['macapptool', '-v', '1', 'staple']; - stapleArgs.push(zipFilename) - execSync.apply(this, stapleArgs); - } - done(); - }); - archive.finalize(); - } - // 'new' .dmg mode - else { - const appdmg = require('appdmg'); - - var target = path.join(appsDir, get_release_filename('macOS', 'dmg')); - console.log('Creating DMG file: ' + target); - var basepath = path.join(appsDir, pkg.name, 'osx64'); - console.log('Base path: ' + basepath); - - if (fs.existsSync(target)) { - fs.unlinkSync(target); - } - - var specs = {}; - - specs["title"] = "INAV Configurator"; - specs["contents"] = [ - { "x": 448, "y": 342, "type": "link", "path": "/Applications" }, - { "x": 192, "y": 344, "type": "file", "path": pkg.name + ".app", "name": "INAV Configurator.app" }, - ]; - specs["background"] = path.join(__dirname, 'assets/osx/dmg-background.png'); - specs["format"] = "UDZO"; - specs["window"] = { - "size": { - "width": 638, - "height": 479, - } - }; - - const codesignIdentity = getArguments()['codesign-identity']; - if (getArguments().codesign) { - specs['code-sign'] = { - 'signing-identity': codesignIdentity, - } - } - - const ee = appdmg({ - target: target, - basepath: basepath, - specification: specs, - }); - - ee.on('progress', function(info) { - //console.log(info); - }); - - ee.on('error', function(err) { - console.log(err); - }); - - ee.on('finish', function() { - if (getArguments().codesign) { - // Check if the bundle is signed - const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', target ]; - execSync.apply(this, codesignCheckArgs); - } - if (getArguments().notarize) { - console.log('Notarizing DMG file: ' + target); - const notarizeArgs = ['xcrun', 'notarytool', 'submit']; - notarizeArgs.push(target); - const notarizationUsername = getArguments()['notarization-username']; - if (notarizationUsername) { - notarizeArgs.push('--apple-id', notarizationUsername) - } else { - throw new Error('Missing notarization username'); - } - const notarizationPassword = getArguments()['notarization-password']; - if (notarizationPassword) { - notarizeArgs.push('--password', notarizationPassword) - } else { - throw new Error('Missing notarization password'); - } - const notarizationTeamId = getArguments()['notarization-team-id']; - if (notarizationTeamId) { - notarizeArgs.push('--team-id', notarizationTeamId) - } else { - throw new Error('Missing notarization Team ID'); - } - notarizeArgs.push('--wait'); - - const notarizationWebhook = getArguments()['notarization-webhook']; - if (notarizationWebhook) { - notarizeArgs.push('--webhook', notarizationWebhook); - } - execSync.apply(this, notarizeArgs); - - console.log('Stapling DMG file: ' + target); - const stapleArgs = ['xcrun', 'stapler', 'staple']; - stapleArgs.push(target); - execSync.apply(this, stapleArgs); - - console.log('Checking DMG file: ' + target); - const checkArgs = ['spctl', '-vvv', '--assess', '--type', 'install', target]; - execSync.apply(this, checkArgs); - } - done(); - }); - } -}); - -function post_build(arch, folder) { - return function post_build_linux(done) { - if ((arch === 'linux32') || (arch === 'linux64')) { - const metadata = require('./package.json'); - // Copy Ubuntu launcher scripts to destination dir - const launcherDir = path.join(folder, metadata.name, arch); - console.log(`Copy Ubuntu launcher scripts to ${launcherDir}`); - return gulp.src('assets/linux/**') - .pipe(gulp.dest(launcherDir)); - } - - return done(); - } -} - -// Create the dir directory, with write permissions -function createDirIfNotExists(dir) { - fs.mkdir(dir, '0775', function(err) { - if (err && err.code !== 'EEXIST') { - throw err; - } - }); -} - -function release_deb(arch) { - return function release_deb_proc(done) { - if (!getArguments().installer) { - done(); - return null; - } - - // Check if dpkg-deb exists - if (!commandExistsSync('dpkg-deb')) { - console.warn(`dpkg-deb command not found, not generating deb package for ${arch}`); - done(); - return null; - } - - const deb = require('gulp-debian'); - const LINUX_INSTALL_DIR = '/opt/inav'; - const metadata = require('./package.json'); - - console.log(`Generating deb package for ${arch}`); - - return gulp.src([path.join(appsDir, metadata.name, arch, '*')]) - .pipe(deb({ - package: metadata.name, - version: metadata.version, - section: 'base', - priority: 'optional', - architecture: getLinuxPackageArch('deb', arch), - maintainer: metadata.author, - description: metadata.description, - preinst: [`rm -rf ${LINUX_INSTALL_DIR}/${metadata.name}`], - postinst: [ - `chown root:root ${LINUX_INSTALL_DIR}`, - `chown -R root:root ${LINUX_INSTALL_DIR}/${metadata.name}`, - `xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`, - ], - prerm: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`], - depends: ['libatomic1'], - changelog: [], - _target: `${LINUX_INSTALL_DIR}/${metadata.name}`, - _out: appsDir, - _copyright: 'assets/linux/copyright', - _clean: true, - })); - } -} - -function post_release_deb(arch) { - return function post_release_linux_deb(done) { - if (!getArguments().installer) { - done(); - return null; - } - if ((arch === 'linux32') || (arch === 'linux64')) { - var rename = require("gulp-rename"); - const metadata = require('./package.json'); - const renameFrom = path.join(appsDir, metadata.name + '_' + metadata.version + '_' + getLinuxPackageArch('.deb', arch) + '.deb'); - const renameTo = path.join(appsDir, get_release_filename_base(arch) + '_' + metadata.version + '.deb'); - // Rename .deb build to common naming - console.log(`Renaming .deb installer ${renameFrom} to ${renameTo}`); - return gulp.src(renameFrom) - .pipe(rename(renameTo)) - .pipe(gulp.dest(".")); - } - - return done(); - } -} - -function release_rpm(arch) { - return function release_rpm_proc(done) { - if (!getArguments().installer) { - done(); - return null; - } - - // Check if rpmbuild exists - if (!commandExistsSync('rpmbuild')) { - console.warn(`rpmbuild command not found, not generating rpm package for ${arch}`); - done(); - return; - } - - const buildRpm = require('rpm-builder'); - const NAME_REGEX = /-/g; - const LINUX_INSTALL_DIR = '/opt/inav'; - const metadata = require('./package.json'); - - console.log(`Generating rpm package for ${arch}`); - - // The buildRpm does not generate the folder correctly, manually - createDirIfNotExists(appsDir); - - const options = { - name: get_release_filename_base(arch), // metadata.name, - version: metadata.version.replace(NAME_REGEX, '_'), // RPM does not like release candidate versions - buildArch: getLinuxPackageArch('rpm', arch), - vendor: metadata.author, - summary: metadata.description, - license: 'GNU General Public License v3.0', - requires: ['libatomic1'], - prefix: '/opt', - files: [{ - cwd: path.join(appsDir, metadata.name, arch), - src: '*', - dest: `${LINUX_INSTALL_DIR}/${metadata.name}`, - }], - postInstallScript: [`xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`], - preUninstallScript: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`], - tempDir: path.join(appsDir, `tmp-rpm-build-${arch}`), - keepTemp: false, - verbose: false, - rpmDest: appsDir, - execOpts: { maxBuffer: 1024 * 1024 * 16 }, - }; - - buildRpm(options, function(err) { - if (err) { - console.error(`Error generating rpm package: ${err}`); - } - done(); - }); - } -} - -function getLinuxPackageArch(type, arch) { - let packArch; - - switch (arch) { - case 'linux32': - packArch = 'i386'; - break; - case 'linux64': - if (type === 'rpm') { - packArch = 'x86_64'; - } else { - packArch = 'amd64'; - } - break; - default: - console.error(`Package error, arch: ${arch}`); - process.exit(1); - break; - } - - return packArch; -} - -function releaseLinux(bits) { - return function() { - console.log(`Generating zip package for linux${bits}`); - var dirname = 'linux' + bits; - var pkg = require('./package.json'); - var src = path.join(appsDir, pkg.name, dirname); - var output = fs.createWriteStream(path.join(appsDir, get_release_filename(dirname, 'tar.gz'))); - var archive = archiver('tar', { - zlib: { level: 9 }, - gzip: true - }); - archive.on('warning', function(err) { throw err; }); - archive.on('error', function(err) { throw err; }); - archive.pipe(output); - archive.directory(src, 'INAV Configurator'); - return archive.finalize(); - } -} - -gulp.task('release-linux32', gulp.series(releaseLinux(32), post_build('linux32', appsDir), release_deb('linux32'), post_release_deb('linux32'))); -gulp.task('release-linux64', gulp.series(releaseLinux(64), post_build('linux64', appsDir), release_deb('linux64'), post_release_deb('linux64'), release_rpm('linux64'))); - -// Create distributable .zip files in ./apps -gulp.task('release', gulp.series('apps', getPlatforms().map(function(v) { return 'release-' + v; }))); - -gulp.task('watch', function () { - for(var k in output) { - gulp.watch(sources[k], gulp.series(get_task_name(k))); - } -}); - -gulp.task('default', gulp.series('build')); diff --git a/images/inav-installing.gif b/images/inav-installing.gif new file mode 100644 index 000000000..7a8974410 Binary files /dev/null and b/images/inav-installing.gif differ diff --git a/main.html b/index.html old mode 100755 new mode 100644 similarity index 96% rename from main.html rename to index.html index 0d5578bb8..0ac68c831 --- a/main.html +++ b/index.html @@ -3,9 +3,13 @@ - - - + + + + + + + INAV Configurator @@ -352,7 +356,7 @@