Skip to content

Build Electron and NWJS packages #1318

Build Electron and NWJS packages

Build Electron and NWJS packages #1318

# Workflow to build Electron and (if not a packaged app) NWJS packages
name: Build Electron and NWJS packages
on:
schedule:
# Nightly run at 03:39 UTC
- cron: '39 03 * * *'
workflow_dispatch:
inputs:
version:
description: Specific version to build like v9.9.9 without suffix (if empty, builds version in package.json)
required: false
default: ''
target:
type: choice
description: Do you wish to build for "release", "nightly", or "artefacts" for testing? Nightly will only publish on the main branch. Artefacts will appear under the workflow run. For release, a draft release with corresponding tag must exist.
required: false
options:
- release
- nightly
- artefacts
default: 'artefacts'
win11only:
description: Do you wish to build only for Windows 11 (primarily for testing)?
type: choice
required: false
options:
- true
- false
default: 'false'
sign:
description: Do you wish to sign the Windows packages?
type: choice
required: false
options:
- true
- false
default: 'false'
env:
INPUT_VERSION: ${{ github.event.inputs.version }}
INPUT_TARGET: ${{ github.event.inputs.target }}
CRON_LAUNCHED: ${{ github.event.schedule }}
INPUT_SIGN: ${{ github.event.inputs.sign || 'false' }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ESIGNER_USERNAME: ${{ secrets.ESIGNER_USER_USERNAME }}
ESIGNER_PASSWORD: ${{ secrets.ESIGNER_USER_PASSWORD }}
ESIGNER_TOTP_SECRET: ${{ secrets.ESIGNER_USER_TOTP_SECRET }}
MASTER_KEY_FILE: "C:\\Users\\runneradmin\\eSignerCKA\\master.key"
INSTALL_DIR: C:\Users\runneradmin\eSignerCKA
SSH_KEY: ${{ secrets.SSH_KEY }}
REF_NAME: ${{ github.ref_name }}
jobs:
Release_Linux:
if: github.event.inputs.win11only != 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Install dependencies
run: npm install
- name: Rewrite app version number and file name
run: |
chmod +x ./scripts/rewrite_app_version_number.sh
./scripts/rewrite_app_version_number.sh
# Replace -app in archive name for Electron apps
sed -i -E 's/(mdwiki[^-]+)-app_/\1_/g' ./www/js/init.js
- name: Build production code
run: npm run build-min
- name: Download archive if needed
run: |
echo "Changing to the dist directory"
cd dist && pwd
# Get archive name
packagedFile=$(grep -m1 'params\[.packagedFile' www/js/init.js | sed -E "s/^.+'([^']+\.zim)'.+/\1/")
# If packagedFile doesn't match a zim file, we don't need to download anything, so exit
if [[ ! $packagedFile =~ \.zim$ ]]; then
echo -e "\nNo zim file to download.\n"
exit 0
fi
# If file doesn't exist in FS, download it
if [ ! -f "archives/$packagedFile" ]; then
# Generalize the name if cron_launched and download it
echo -e "\nDownloading https://download.kiwix.org/zim/$packagedFile"
if [[ $CRON_LAUNCHED = true ]]; then
packagedFileGeneric=$(sed -E 's/_[0-9-]+(\.zim)/\1/' <<<"$packagedFile")
wget -nv "https://download.kiwix.org/zim/$packagedFileGeneric" -O "archives/$packagedFile"
else
flavour=$(sed -E 's/^([^_]+)_.+$/\1/' <<<"$packagedFile")
if [[ $flavour = "mdwiki" ]]; then
flavour='other'
fi
wget -nv "https://mirror.download.kiwix.org/zim/$flavour/$packagedFile" -O "archives/$packagedFile"
fi
fi
ls archives
if [ -f "archives/$packagedFile" ]; then
echo -e "\nFile $packagedFile now available in 'archives'.\n"
else
echo -e "\nError! We could not obtain the requested archive $packagedFile!\n"
exit 1
fi
- name: Build and publish 64bit
env:
USE_HARD_LINKS: false
run: |
# echo "Setting the module type to one supported by Electron in ./package.json"
# sed -i -E 's/("type":\s+)"module"/\1"commonjs"/' ./package.json
echo "Installing dependencies in dist"
cd dist && npm install && cd ..
echo "Building 64bit packages for ref_name=$REF_NAME..."
if [[ $REF_NAME = "main" ]]; then
npx electron-builder --linux AppImage:x64 AppImage:arm64 deb:x64 rpm:x64 --projectDir dist
else
npx electron-builder --linux AppImage:x64 AppImage:arm64 deb:x64 --projectDir dist
fi
- name: Build and pulblish 32bit
env:
USE_HARD_LINKS: false
run: |
echo "Changing Electron version to latest that supports 32bit Linux (18.3.15) in ./dist/package.json"
sed -i -E 's/("electron":\s")[^"]+/\118.3.15/' ./dist/package.json
echo "Installing dependencies in dist"
cd dist && npm install && cd ..
echo "Building 32bit packages for ref_name=$REF_NAME..."
if [[ $REF_NAME = "main" ]]; then
npx electron-builder --linux AppImage:ia32 deb:ia32 rpm:ia32 --projectDir dist
else
npx electron-builder --linux AppImage:ia32 --projectDir dist
fi
- name: Upload packages to Kiwix
if: github.ref_name == 'main' && github.event.inputs.target != 'artefacts'
run: |
echo "$SSH_KEY" > ./scripts/ssh_key
chmod 600 ./scripts/ssh_key
chmod +x ./scripts/publish_linux_packages_to_kiwix.sh
./scripts/publish_linux_packages_to_kiwix.sh
- name: Archive build artefacts
if: github.event.inputs.target == 'artefacts'
uses: actions/upload-artifact@v4
with:
name: kiwix-js-electron_linux
path: |
dist/bld/Electron/*.AppImage
dist/bld/Electron/*.deb
dist/bld/Electron/*.rpm
Release_Windows:
runs-on: windows-latest
env:
SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x86/signtool.exe"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Install dependencies
run: npm install
- name: Rewrite app version number and file name
run: |
$INPUT_VERSION = $Env:INPUT_VERSION
$INPUT_TARGET = $Env:INPUT_TARGET
$CRON_LAUNCHED = $Env:CRON_LAUNCHED
./scripts/Rewrite-AppVersion.ps1
# Replace -app in archive name for Electron apps
(Get-Content ./www/js/init.js) -replace '(mdwiki[^-]+)-app_', '$1_' | Set-Content -encoding 'utf8BOM' ./www/js/init.js
- name: Build production code
run: npm run build-min
- name: Download archive if needed
run: |
echo "Changing to the dist directory"
cd dist && pwd
$packagedFile = (Select-String 'packagedFile' "www\js\init.js" -List) -ireplace "^.+'([^']+\.zim)'.+", '$1'
# If packagedFile doesn't match a zim file, we don't need to download anything, so exit
if ($packagedFile -and ! ($packagedFile -match '\.zim$')) {
Write-Host "`nNo zim file to download.`n"
exit 0
}
if ($packagedFile -and ! (Test-Path "archives\$packagedFile" -PathType Leaf)) {
# File not in archives, so generalize the name (if nightly) and download it
Write-Host "`nDownloading https://download.kiwix.org/zim/$packagedFile"
if ($CRON_LAUNCHED) {
$packagedFileGeneric = $packagedFile -replace '_[0-9-]+(\.zim)', '$1'
Invoke-WebRequest "https://download.kiwix.org/zim/$packagedFileGeneric" -OutFile "archives\$packagedFile"
} else {
$flavour = $packagedFile -replace '^([^_]+)_.+$', '$1'
if ($flavour -eq 'mdwiki') {
$flavour = 'other'
}
Invoke-WebRequest "https://mirror.download.kiwix.org/zim/$flavour/$packagedFile" -OutFile "archives\$packagedFile"
}
}
ls archives
if ($packagedFile -and (Test-Path "archives\$packagedFile" -PathType Leaf)) {
Write-Host "`nFile $packagedFile now available in 'archives'.`n" -ForegroundColor Green
} else {
Write-Host "`nError! We could not obtain the requested archive $packagedFile!`n" -ForegroundColor Red
exit 1
}
- name: Install and configure eSigner CKA and Windows SDK
if: github.event.inputs.sign == 'true'
env:
ESIGNER_URL: https://github.com/SSLcom/eSignerCKA/releases/download/v1.0.7/SSL.COM-eSigner-CKA_1.0.7.zip
run: |
Set-StrictMode -Version 'Latest'
# Download and Unzip eSignerCKA Setup
Invoke-WebRequest -OutFile eSigner_CKA_Setup.zip "$env:ESIGNER_URL"
Expand-Archive -Force eSigner_CKA_Setup.zip
Remove-Item eSigner_CKA_Setup.zip
Move-Item -Destination “eSigner_CKA_Installer.exe” -Path “eSigner_CKA_*\*.exe”
# Install eSignerCKA
New-Item -ItemType Directory -Force -Path ${{ env.INSTALL_DIR }}
./eSigner_CKA_Installer.exe /CURRENTUSER /VERYSILENT /SUPPRESSMSGBOXES /DIR=”${{ env.INSTALL_DIR }}” | Out-Null
# Disable logger
$LogConfig = Get-Content -Path ${{ env.INSTALL_DIR }}/log4net.config
$LogConfig[0] = '<log4net threshold="OFF">'
$LogConfig | Set-Content -Path ${{ env.INSTALL_DIR }}/log4net.config
# Configure
${{ env.INSTALL_DIR }}/eSignerCKATool.exe config -mode product -user "${{ env.ESIGNER_USERNAME }}" -pass "${{ env.ESIGNER_PASSWORD }}" -totp "${{ env.ESIGNER_TOTP_SECRET }}" -key "${{ env.MASTER_KEY_FILE }}" -r
${{ env.INSTALL_DIR }}/eSignerCKATool.exe unload
${{ env.INSTALL_DIR }}/eSignerCKATool.exe load
# Find certificate
$CodeSigningCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
echo Certificate: $CodeSigningCert
# Extract thumbprint and subject name
$Thumbprint = $CodeSigningCert.Thumbprint
$SubjectName = ($CodeSigningCert.Subject -replace ", ?", "`n" | ConvertFrom-StringData).CN
ls -l ${{ env.MASTER_KEY_FILE }}
echo "ED_SIGNTOOL_THUMBPRINT=$Thumbprint" >> $env:GITHUB_ENV
echo "ED_SIGNTOOL_SUBJECT_NAME=$SubjectName" >> $env:GITHUB_ENV
- name: Run electron builder for Win 7/8/8.1
if: github.event.inputs.win11only != 'true'
shell: powershell
run: |
$GITHUB_TOKEN = $Env:GITHUB_TOKEN
$INPUT_VERSION = $Env:INPUT_VERSION
$INPUT_TARGET = $Env:INPUT_TARGET
$CRON_LAUNCHED = $Env:CRON_LAUNCHED
$INPUT_SIGN = !$CRON_LAUNCHED -and [System.Convert]::ToBoolean($Env:INPUT_SIGN)
if (-not ($Env:CRON_LAUNCHED -or ($Env:INPUT_TARGET -eq 'nightly'))) {
$INPUT_VERSION_E = $INPUT_VERSION -replace '^v([0-9.]+).*', '$1-E'
} else {
$INPUT_VERSION_E = $INPUT_VERSION -replace '^v', ''
}
./scripts/Rewrite-DraftReleaseTag.ps1
$ORIGINAL_ELECTRON_VERSION = (Get-Content ./package.json | sls '"electron":') -replace '.*"electron"\s*:\s*"(.*)".*', '$1'
echo "ORIGINAL_ELECTRON_VERSION=$ORIGINAL_ELECTRON_VERSION" | Out-File $Env:GITHUB_ENV -Encoding utf8 -Append
echo "Setting the Electron version to the latest supporting Windows 7/8/8.1 (=22.3.25)"
(Get-Content ./package.json) -replace '("electron":\s+)"[\d.]+[\w\d-.]*?"', '$1"22.3.25"' | Set-Content ./package.json
echo "Copying the package.json to dist"
cp ./package.json ./dist/package.json
echo "Installing dependencies in root and dist"
npm install; cd dist; npm install; cd ..
echo "Installed Electron version:$(npx electron --version)"
echo "Building Windows 7+ 32bit NSIS package..."
if ($INPUT_SIGN) {
npm run dist-win-nsis
} else {
npm run dist-win-nsis-skipsigning
}
echo "Renaming Windows 7+ executable"
$files = @("Kiwix JS Electron", "WikiMed by Kiwix", "Wikivoyage by Kiwix")
foreach ($file in $files) {
mv "dist/bld/Electron/$file Setup*.exe" "dist/bld/Electron/$file Win7 Setup $INPUT_VERSION_E.exe"
mv "dist/bld/Electron/$file Setup*.exe.blockmap" "dist/bld/Electron/$file Win7 Setup $INPUT_VERSION_E.exe.blockmap"
}
mv "dist/bld/Electron/latest.yml" "dist/bld/Electron/latest-win7.yml"
ls ./dist/bld/Electron/
# DEV: We did not use electron-builder to publish because we needed to change the filenames, so we need to publish to GitHub here
if ($INPUT_TARGET -ne 'artefacts') {
& ./scripts/Publish-ElectronPackages.ps1 -githubonly
}
./scripts/Rewrite-DraftReleaseTag.ps1
- name: Run electron builder for Win 10/11
shell: powershell
run: |
$GITHUB_TOKEN = $Env:GITHUB_TOKEN
$INPUT_VERSION = $Env:INPUT_VERSION
$INPUT_TARGET = $Env:INPUT_TARGET
$CRON_LAUNCHED = $Env:CRON_LAUNCHED
$INPUT_SIGN = !$CRON_LAUNCHED -and [System.Convert]::ToBoolean($Env:INPUT_SIGN)
$ORIGINAL_ELECTRON_VERSION = $Env:ORIGINAL_ELECTRON_VERSION
echo "Restoring original Electron version: $ORIGINAL_ELECTRON_VERSION"
(Get-Content ./package.json) -replace '("electron":\s+)"[\d.]+[\w\d-.]*?"', ('$1"' + $ORIGINAL_ELECTRON_VERSION + '"') | Set-Content ./package.json
./scripts/Rewrite-DraftReleaseTag.ps1
# Set the module type to one supported by Electron
# (Get-Content ./package.json) -replace '("type":\s+)"module"', '$1"commonjs"' | Set-Content ./package.json
echo "Copying the rewritten package.json to dist"
cp ./package.json ./dist/package.json
echo "Installing dependencies in root and dist"
npm install; cd dist; npm install; cd ..
echo "Installed Electron version:$(npx electron --version)"
# echo "Configuring and loading esigner..."
# ${{ env.INSTALL_DIR }}/eSignerCKATool.exe config -mode product -user "${{ env.ESIGNER_USERNAME }}" -pass "${{ env.ESIGNER_PASSWORD }}" -totp "${{ env.ESIGNER_TOTP_SECRET }}" -key "${{ env.MASTER_KEY_FILE }}" -r
# ${{ env.INSTALL_DIR }}/eSignerCKATool.exe unload
# ${{ env.INSTALL_DIR }}/eSignerCKATool.exe load
echo "Building Windows packages..."
if ($INPUT_SIGN) {
npm run publish
} else {
npm run build-skipsigning
}
./scripts/Rewrite-DraftReleaseTag.ps1
- name: Build portable Electron app
run: |
if (-not ($Env:CRON_LAUNCHED -or ($Env:INPUT_TARGET -eq 'nightly'))) {
$GITHUB_TOKEN = $Env:GITHUB_TOKEN
$INPUT_VERSION_E = $Env:INPUT_VERSION -replace '^(v[0-9.]+).*', '$1E'
if ($Env:INPUT_VERSION -match '-Wiki[\w]+') {
$INPUT_VERSION_E += $matches[0]
}
# To ensure there is enough disk space, we can delete the archive that is no longer needed
rm -r dist/archives
./scripts/Create-DraftRelease -buildonly -tag_name $INPUT_VERSION_E -portableonly -nobundle -wingetprompt N -nobranchcheck
}
- name: Publish packages
if: github.event.inputs.target != 'artefacts'
run: |
$SSH_KEY = $Env:SSH_KEY
echo "$SSH_KEY" > .\scripts\ssh_key
$GITHUB_TOKEN = $Env:GITHUB_TOKEN
$INPUT_VERSION = $Env:INPUT_VERSION
$INPUT_TARGET = $Env:INPUT_TARGET
$CRON_LAUNCHED = $Env:CRON_LAUNCHED
if ($Env:REF_NAME -eq "main") {
./scripts/Publish-ElectronPackages.ps1 -portableonly
} else {
./scripts/Publish-ElectronPackages.ps1 -portableonly -githubonly
}
- name: Archive build artefacts
if: github.event.inputs.target == 'artefacts'
uses: actions/upload-artifact@v4
with:
name: kiwix-js-electron_windows
path: |
dist/bld/Electron/*.exe
dist/bld/Electron/*.appx
dist/bld/Electron/*.zip
dist/bld/Electron/nsis-web/*.exe
dist/bld/Electron/nsis-web/*.nsis.7z
Release_NWJS:
if: github.ref_name == 'main' && github.event.inputs.win11only != 'true'
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Install dependencies
run: npm install
- name: Build production code
run: npm run build-min
- name: Select NWJS app
run: |
ren package.json package.json.electron
ren package.json.nwjs package.json
- name: Rewrite app version number
run: |
$INPUT_VERSION = $Env:INPUT_VERSION
$INPUT_TARGET = $Env:INPUT_TARGET
$CRON_LAUNCHED = $Env:CRON_LAUNCHED
./scripts/Rewrite-AppVersion.ps1
cp package.json dist/package.json
- name: Download archive if needed
run: |
echo "Changing to the dist directory"
cd dist && pwd
$packagedFile = (Select-String 'packagedFile' "www\js\init.js" -List) -ireplace "^.+'([^']+\.zim)'.+", '$1'
# If packagedFile doesn't match a zim file, we don't need to download anything, so exit
if ($packagedFile -and ! ($packagedFile -match '\.zim$')) {
Write-Host "`nNo zim file to download.`n"
exit 0
}
if ($packagedFile -and ! (Test-Path "archives\$packagedFile" -PathType Leaf)) {
# File not in archives, so generalize the name (if nightly) and download it
Write-Host "`nDownloading https://download.kiwix.org/zim/$packagedFile"
if ($CRON_LAUNCHED) {
$packagedFileGeneric = $packagedFile -replace '_[0-9-]+(\.zim)', '$1'
Invoke-WebRequest "https://download.kiwix.org/zim/$packagedFileGeneric" -OutFile "archives\$packagedFile"
} else {
$flavour = $packagedFile -replace '^([^_]+)_.+$', '$1'
if ($flavour -eq 'mdwiki') {
$flavour = 'other'
}
Invoke-WebRequest "https://mirror.download.kiwix.org/zim/$flavour/$packagedFile" -OutFile "archives\$packagedFile"
}
}
ls archives
if ($packagedFile -and (Test-Path "archives\$packagedFile" -PathType Leaf)) {
Write-Host "`nFile $packagedFile now available in 'archives'.`n" -ForegroundColor Green
} else {
Write-Host "`nError! We could not obtain the requested archive $packagedFile!`n" -ForegroundColor Red
exit 1
}
- name: Build NWJS app
run: ./scripts/Build-NWJS.ps1 -only32bit
- name: Publish
if: github.event.inputs.target != 'artefacts'
run: |
$SSH_KEY = $Env:SSH_KEY
echo "$SSH_KEY" > .\scripts\ssh_key
$GITHUB_TOKEN = $Env:GITHUB_TOKEN
$INPUT_VERSION = $Env:INPUT_VERSION
$INPUT_TARGET = $Env:INPUT_TARGET
$CRON_LAUNCHED = $Env:CRON_LAUNCHED
./scripts/Publish-ElectronPackages.ps1
- name: Archive build artefacts
if: github.event.inputs.target == 'artefacts'
uses: actions/upload-artifact@v4
with:
name: kiwix-js-nwjs_windows
path: dist/bld/nwjs/*.zip