Build Electron and NWJS packages #1327
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |