From ebe4a606d0c3fb28cc55728fa7076646d0bdf275 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 15:55:26 +0200 Subject: [PATCH 01/26] =?UTF-8?q?=F0=9F=AA=9F=F0=9F=94=90=F0=9F=93=9C=20Wi?= =?UTF-8?q?ndows=20Certificate=20Authority?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .circleci/config.yml | 14 +++++++++++--- forge.config.js | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ec8e2b4..dccff5ac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -136,7 +136,16 @@ jobs: command: yarn test - run: name: Package Application - command: CERTIFICATE_PASSWORD=$KEYCHAIN_PASSWORD yarn package + command: DEBUG=* CERTIFICATE_PASSWORD=$KEYCHAIN_PASSWORD yarn package + - run: + name: Print project directory contents + command: dir /a + - run: + name: Print /out directory contents + command: dir /a out + - run: + name: Print /out/make directory contents + command: dir /a out\make - run: name: Create distribution files command: yarn only-make @@ -312,6 +321,7 @@ workflows: branches: ignore: - /agent-version-.*/ + - windows-certificate pre-steps: - run: name: Use Node 20 @@ -331,5 +341,3 @@ workflows: branches: ignore: - /agent-version-.*/ - requires: - - build_macos diff --git a/forge.config.js b/forge.config.js index 7534b8d3..d5888402 100644 --- a/forge.config.js +++ b/forge.config.js @@ -13,6 +13,12 @@ module.exports = { iconUrl: 'https://loadmill.com/favicon.ico', setupExe: `${productName}-${name}-${version}-Setup.exe`, setupIcon: './images/loadmill-icon-256-256.ico', + // windowsSign: { + // // tell signtool where to find the certificate and password + // // certificateFile: './cert.pfx', + // // certificatePassword: process.env.CERTIFICATE_PASSWORD, + // signWithParams: '/ds "Loadmill LTD" /fd SHA256 /td SHA256', + // }, }, name: '@electron-forge/maker-squirrel', }, From 985c7bfcb32ee63e7b6093283e64c5c84e0ac069 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 16:38:55 +0200 Subject: [PATCH 02/26] see what windowsSign and signWithParams do --- .circleci/config.yml | 6 +++--- forge.config.js | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dccff5ac..175cf028 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -139,13 +139,13 @@ jobs: command: DEBUG=* CERTIFICATE_PASSWORD=$KEYCHAIN_PASSWORD yarn package - run: name: Print project directory contents - command: dir /a + command: ls -la - run: name: Print /out directory contents - command: dir /a out + command: ls -la out - run: name: Print /out/make directory contents - command: dir /a out\make + command: ls -la out/make - run: name: Create distribution files command: yarn only-make diff --git a/forge.config.js b/forge.config.js index d5888402..b24e4135 100644 --- a/forge.config.js +++ b/forge.config.js @@ -13,12 +13,10 @@ module.exports = { iconUrl: 'https://loadmill.com/favicon.ico', setupExe: `${productName}-${name}-${version}-Setup.exe`, setupIcon: './images/loadmill-icon-256-256.ico', - // windowsSign: { - // // tell signtool where to find the certificate and password - // // certificateFile: './cert.pfx', - // // certificatePassword: process.env.CERTIFICATE_PASSWORD, - // signWithParams: '/ds "Loadmill LTD" /fd SHA256 /td SHA256', - // }, + windowsSign: { + // tell signtool where to find the certificate and password + signWithParams: `/f ./cert.pfx /p ${process.env.CERTIFICATE_PASSWORD} /fd SHA256 /v /debug`, + }, }, name: '@electron-forge/maker-squirrel', }, From da1159cde98c458af425491d973948b92fd5d140 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 17:52:05 +0200 Subject: [PATCH 03/26] hunting for the codesign step in windows build --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 175cf028..783ec4ee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -136,19 +136,19 @@ jobs: command: yarn test - run: name: Package Application - command: DEBUG=* CERTIFICATE_PASSWORD=$KEYCHAIN_PASSWORD yarn package + command: CERTIFICATE_PASSWORD=$KEYCHAIN_PASSWORD yarn package - run: name: Print project directory contents command: ls -la - run: name: Print /out directory contents command: ls -la out + - run: + name: Create distribution files + command: DEBUG=* yarn only-make - run: name: Print /out/make directory contents command: ls -la out/make - - run: - name: Create distribution files - command: yarn only-make - store_artifacts: name: Store Artifacts path: out/make/ From d6502f22ca4facc79a2666db64f54cd627baa027 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 18:03:23 +0200 Subject: [PATCH 04/26] node 20 on win build --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 783ec4ee..85ab167c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -114,8 +114,8 @@ jobs: # print out the certificate openssl pkcs12 -info -in cert.pfx -passin pass:${KEYCHAIN_PASSWORD} -passout pass:${KEYCHAIN_PASSWORD} -nokeys - run: - name: Install Node 18.20.5 - command: nvm install 18.20.5 && nvm use 18.20.5 + name: Install Node 20.19.5 + command: nvm install 20.19.5 && nvm use 20.19.5 - run: name: Install Yarn command: npm install -g yarn From 348ca8a8a1a19c297ba1f07cfaa8962cc686c10f Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 18:21:05 +0200 Subject: [PATCH 05/26] debug prod signing method in windows --- forge.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/forge.config.js b/forge.config.js index b24e4135..22c714f6 100644 --- a/forge.config.js +++ b/forge.config.js @@ -13,10 +13,10 @@ module.exports = { iconUrl: 'https://loadmill.com/favicon.ico', setupExe: `${productName}-${name}-${version}-Setup.exe`, setupIcon: './images/loadmill-icon-256-256.ico', - windowsSign: { - // tell signtool where to find the certificate and password - signWithParams: `/f ./cert.pfx /p ${process.env.CERTIFICATE_PASSWORD} /fd SHA256 /v /debug`, - }, + // windowsSign: { + // // tell signtool where to find the certificate and password + // signWithParams: `/f ./cert.pfx /p ${process.env.CERTIFICATE_PASSWORD} /fd SHA256 /v /debug`, + // }, }, name: '@electron-forge/maker-squirrel', }, From a43b3ccbced5f498bdac3f9f413a3df94f251cf8 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 18:45:53 +0200 Subject: [PATCH 06/26] experiment --- forge.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forge.config.js b/forge.config.js index 22c714f6..28b9908e 100644 --- a/forge.config.js +++ b/forge.config.js @@ -8,14 +8,14 @@ module.exports = { makers: [ { config: { - certificateFile: './cert.pfx', - certificatePassword: process.env.CERTIFICATE_PASSWORD, + // certificateFile: './cert.pfx', + // certificatePassword: process.env.CERTIFICATE_PASSWORD, iconUrl: 'https://loadmill.com/favicon.ico', setupExe: `${productName}-${name}-${version}-Setup.exe`, setupIcon: './images/loadmill-icon-256-256.ico', // windowsSign: { // // tell signtool where to find the certificate and password - // signWithParams: `/f ./cert.pfx /p ${process.env.CERTIFICATE_PASSWORD} /fd SHA256 /v /debug`, + signWithParams: `/f ./cert.pfx /p ${process.env.CERTIFICATE_PASSWORD} /fd SHA256 /v /debug`, // }, }, name: '@electron-forge/maker-squirrel', From 5fc9cece325d572e52cb39d1bb55403976462331 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 19:22:29 +0200 Subject: [PATCH 07/26] reduce the problem - only self signed cert --- .circleci/config.yml | 190 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 85ab167c..e40c8782 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -311,6 +311,194 @@ jobs: ) echo "UPLOAD_ASSET_RESPONSE" echo "$UPLOAD_ASSET_RESPONSE" | jq . + windows-test-certificate-and-signing: + executor: + name: win/default + shell: powershell.exe + steps: + - checkout + + - run: + name: Create Self-Signed Certificate + command: | + Write-Host "Creating self-signed certificate..." + + # Create a self-signed code signing certificate + $cert = New-SelfSignedCertificate ` + -Type CodeSigningCert ` + -Subject "CN=Loadmill Test Certificate, O=Loadmill LTD, C=US" ` + -KeyAlgorithm RSA ` + -KeyLength 2048 ` + -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" ` + -CertStoreLocation "Cert:\CurrentUser\My" ` + -NotAfter (Get-Date).AddYears(2) + + Write-Host "✓ Certificate created in store" + Write-Host " Thumbprint: $($cert.Thumbprint)" + Write-Host " Subject: $($cert.Subject)" + Write-Host " NotAfter: $($cert.NotAfter)" + + # Export to PFX file + $password = ConvertTo-SecureString -String "TestPassword123" -Force -AsPlainText + Export-PfxCertificate -Cert $cert -FilePath "cert.pfx" -Password $password + + Write-Host "✓ Certificate exported to cert.pfx" + + # Verify the PFX file was created + if (Test-Path cert.pfx) { + $pfxFile = Get-Item cert.pfx + Write-Host "✓ cert.pfx file size: $($pfxFile.Length) bytes" + } else { + Write-Host "✗ ERROR: cert.pfx was not created!" + exit 1 + } + + - run: + name: Verify Certificate with certutil + command: | + Write-Host "Dumping certificate info..." + certutil -dump -v cert.pfx + + Write-Host "`n=== Certificate Details ===" + certutil -dump cert.pfx | Select-String -Pattern "Subject:|Issuer:|NotBefore:|NotAfter:" + + - run: + name: Create Dummy EXE to Sign + command: | + Write-Host "Creating a dummy executable..." + + # Create a simple C# program + $code = @" + using System; + class Program { + static void Main() { + Console.WriteLine("Hello from signed executable!"); + } + } + "@ + + # Save the code + $code | Out-File -FilePath "test.cs" -Encoding ASCII + + # Compile it (csc.exe is in .NET Framework) + $cscPath = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" + + if (Test-Path $cscPath) { + Write-Host "Compiling test.cs..." + & $cscPath /out:test.exe test.cs + + if (Test-Path test.exe) { + Write-Host "✓ test.exe created successfully" + $exeFile = Get-Item test.exe + Write-Host " Size: $($exeFile.Length) bytes" + } else { + Write-Host "✗ ERROR: Failed to create test.exe" + exit 1 + } + } else { + Write-Host "✗ ERROR: C# compiler not found at $cscPath" + Write-Host "Creating a dummy file instead..." + "MZ" | Out-File -FilePath test.exe -Encoding ASCII -NoNewline + } + + - run: + name: Sign the EXE with signtool + command: | + Write-Host "Attempting to sign test.exe..." + + # Find signtool.exe (usually in Windows SDK) + $signtoolPaths = @( + "C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" + ) + + $signtool = $null + foreach ($path in $signtoolPaths) { + if (Test-Path $path) { + $signtool = $path + Write-Host "✓ Found signtool at: $path" + break + } + } + + if (-not $signtool) { + Write-Host "✗ ERROR: signtool.exe not found!" + Write-Host "Searching for signtool..." + $found = Get-ChildItem "C:\Program Files (x86)\Windows Kits\" -Recurse -Filter "signtool.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { + $signtool = $found.FullName + Write-Host "✓ Found signtool at: $signtool" + } else { + Write-Host "Installing Windows SDK..." + choco install windows-sdk-10.1 -y + exit 1 + } + } + + # Sign the file + Write-Host "`nSigning test.exe with certificate..." + $certPath = Resolve-Path "cert.pfx" + + & $signtool sign /f $certPath /p "TestPassword123" /fd SHA256 /v /debug test.exe + + $exitCode = $LASTEXITCODE + Write-Host "`nSigntool exit code: $exitCode" + + if ($exitCode -eq 0) { + Write-Host "✓✓✓ SUCCESS! File signed successfully! ✓✓✓" + } else { + Write-Host "✗✗✗ FAILED! Signing failed with exit code $exitCode ✗✗✗" + exit 1 + } + + - run: + name: Verify Signature + command: | + Write-Host "Verifying signature on test.exe..." + + # Find signtool + $signtoolPaths = @( + "C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" + ) + + $signtool = $signtoolPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 + + if ($signtool) { + & $signtool verify /pa /v test.exe + Write-Host "`nNote: Verification will fail because it's self-signed (not trusted)" + Write-Host "But the signature itself should be present!" + } + + # Check if signature exists using PowerShell + Write-Host "`nChecking signature with Get-AuthenticodeSignature..." + $sig = Get-AuthenticodeSignature test.exe + Write-Host "Status: $($sig.Status)" + Write-Host "StatusMessage: $($sig.StatusMessage)" + Write-Host "SignerCertificate: $($sig.SignerCertificate.Subject)" + + if ($sig.SignerCertificate) { + Write-Host "`n✓✓✓ SIGNATURE EXISTS! ✓✓✓" + } else { + Write-Host "`n✗✗✗ NO SIGNATURE FOUND ✗✗✗" + } + + - run: + name: Summary + command: | + Write-Host "`n=========================================" + Write-Host " TEST SUMMARY" + Write-Host "=========================================" + Write-Host "✓ Certificate created: cert.pfx" + Write-Host "✓ Password: TestPassword123" + Write-Host "✓ Test file signed: test.exe" + Write-Host "`nNext steps:" + Write-Host "1. Use the same PowerShell commands in your main build" + Write-Host "2. Replace 'TestPassword123' with your actual password" + Write-Host "3. Apply to electron-forge config" + Write-Host "=========================================" workflows: version: 2 @@ -341,3 +529,5 @@ workflows: branches: ignore: - /agent-version-.*/ + - windows-certificate + - windows-test-certificate-and-signing \ No newline at end of file From 7b3f7e2f1f612aed0d7b2ec65dcb64233b31df65 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 19:25:36 +0200 Subject: [PATCH 08/26] no interactive password in ci pipeline --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e40c8782..0fd8baf7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -357,10 +357,10 @@ jobs: name: Verify Certificate with certutil command: | Write-Host "Dumping certificate info..." - certutil -dump -v cert.pfx + certutil -dump -v -p "TestPassword123" cert.pfx Write-Host "`n=== Certificate Details ===" - certutil -dump cert.pfx | Select-String -Pattern "Subject:|Issuer:|NotBefore:|NotAfter:" + certutil -dump -v -p "TestPassword123" cert.pfx | Select-String -Pattern "Subject:|Issuer:|NotBefore:|NotAfter:" - run: name: Create Dummy EXE to Sign From 8e177bf456b27821231cc2bb252692a427736914 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 20:10:28 +0200 Subject: [PATCH 09/26] try the electron forge codesign params --- .circleci/config.yml | 36 ++++++++++++++++++++++++++++++++++++ forge.config.js | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0fd8baf7..7aee7059 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -499,6 +499,42 @@ jobs: Write-Host "2. Replace 'TestPassword123' with your actual password" Write-Host "3. Apply to electron-forge config" Write-Host "=========================================" + - run: + name: Install Node 20.19.5 + command: nvm install 20.19.5 && nvm use 20.19.5 + - run: + name: Install Yarn + command: npm install -g yarn + - restore_cache: + keys: + - yarn-packages-windows-build-{{ .Branch }}-{{ checksum "yarn.lock" }} + - yarn-packages-windows-build-{{ .Branch }}- + - yarn-packages-windows-build- + - run: + name: Install dependencies + command: yarn install + - save_cache: + paths: + - node_modules/ + key: yarn-packages-windows-build-{{ .Branch }}-{{ checksum "yarn.lock" }} + - run: + name: Run Tests + command: yarn test + - run: + name: Package Application + command: yarn package + - run: + name: Print project directory contents + command: ls -la + - run: + name: Print /out directory contents + command: ls -la out + - run: + name: Create distribution files + command: DEBUG=* yarn only-make + - run: + name: Print /out/make directory contents + command: ls -la out/make workflows: version: 2 diff --git a/forge.config.js b/forge.config.js index 28b9908e..735a2247 100644 --- a/forge.config.js +++ b/forge.config.js @@ -15,7 +15,7 @@ module.exports = { setupIcon: './images/loadmill-icon-256-256.ico', // windowsSign: { // // tell signtool where to find the certificate and password - signWithParams: `/f ./cert.pfx /p ${process.env.CERTIFICATE_PASSWORD} /fd SHA256 /v /debug`, + signWithParams: '/f ./cert.pfx /p TestPassword123 /fd SHA256 /v /debug', // }, }, name: '@electron-forge/maker-squirrel', From 6a0afc75b7b513445183c5252e3492414c8f3ad1 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 20:11:45 +0200 Subject: [PATCH 10/26] fix bad indent paste --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7aee7059..81fdfb1f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -499,7 +499,7 @@ jobs: Write-Host "2. Replace 'TestPassword123' with your actual password" Write-Host "3. Apply to electron-forge config" Write-Host "=========================================" - - run: + - run: name: Install Node 20.19.5 command: nvm install 20.19.5 && nvm use 20.19.5 - run: From 545f37ec582a3ed831c6e86a646d32c16aaea868 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 20:15:41 +0200 Subject: [PATCH 11/26] PowerShell syntax --- .circleci/config.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 81fdfb1f..8ed11d37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -501,7 +501,9 @@ jobs: Write-Host "=========================================" - run: name: Install Node 20.19.5 - command: nvm install 20.19.5 && nvm use 20.19.5 + command: | + nvm install 20.19.5 + nvm use 20.19.5 - run: name: Install Yarn command: npm install -g yarn @@ -525,16 +527,18 @@ jobs: command: yarn package - run: name: Print project directory contents - command: ls -la + command: Get-ChildItem -Force - run: name: Print /out directory contents - command: ls -la out + command: Get-ChildItem -Force out - run: name: Create distribution files - command: DEBUG=* yarn only-make + command: | + $env:DEBUG="*" + yarn only-make - run: name: Print /out/make directory contents - command: ls -la out/make + command: Get-ChildItem -Force out/make workflows: version: 2 From 8d29df6360819ba33c181cb5a0aaa9214de32146 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 20:40:44 +0200 Subject: [PATCH 12/26] more printing and trying the /as option --- .circleci/config.yml | 6 ++++++ forge.config.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ed11d37..6a7e2367 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -531,6 +531,12 @@ jobs: - run: name: Print /out directory contents command: Get-ChildItem -Force out + - run: + name: Print /out/Loadmill-win32-x64 directory contents + command: Get-ChildItem -Force -Recurse out/Loadmill-win32-x64 + - run: + name: Print signature of ffmpeg.dll + command: signtool verify /pa "out\Loadmill-win32-x64\ffmpeg.dll" - run: name: Create distribution files command: | diff --git a/forge.config.js b/forge.config.js index 735a2247..d14e7910 100644 --- a/forge.config.js +++ b/forge.config.js @@ -15,7 +15,7 @@ module.exports = { setupIcon: './images/loadmill-icon-256-256.ico', // windowsSign: { // // tell signtool where to find the certificate and password - signWithParams: '/f ./cert.pfx /p TestPassword123 /fd SHA256 /v /debug', + signWithParams: '/as /f ./cert.pfx /p TestPassword123 /fd SHA256 /v /debug', // }, }, name: '@electron-forge/maker-squirrel', From f508b655ce07d51e5a2b3489af6c4d35d15e0a14 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 21:22:35 +0200 Subject: [PATCH 13/26] Add SIGNSTOOL environment variable --- .circleci/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6a7e2367..559711f7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -418,6 +418,8 @@ jobs: if (Test-Path $path) { $signtool = $path Write-Host "✓ Found signtool at: $path" + # write to circleci env var + echo "export SIGNSTOOL=$path" >> $BASH_ENV break } } @@ -452,6 +454,10 @@ jobs: exit 1 } + -run: + name: Verify SIGNTOOL env var + command: | + Write-Host "SIGNSTOOL env var: $env:SIGNSTOOL" - run: name: Verify Signature command: | @@ -536,7 +542,9 @@ jobs: command: Get-ChildItem -Force -Recurse out/Loadmill-win32-x64 - run: name: Print signature of ffmpeg.dll - command: signtool verify /pa "out\Loadmill-win32-x64\ffmpeg.dll" + command: | + Write-Host "SIGNSTOOL env var: $env:SIGNSTOOL" + $env:SIGNSTOOL verify /pa "out\Loadmill-win32-x64\ffmpeg.dll" - run: name: Create distribution files command: | From fba0b2cbc157291b6a8db127741099404a51decb Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Sun, 7 Dec 2025 21:24:05 +0200 Subject: [PATCH 14/26] space --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 559711f7..474d9ab3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -454,7 +454,7 @@ jobs: exit 1 } - -run: + - run: name: Verify SIGNTOOL env var command: | Write-Host "SIGNSTOOL env var: $env:SIGNSTOOL" From 49a56d0dcab1f2fd25476759dbf5229922335346 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 11:41:19 +0200 Subject: [PATCH 15/26] Verifying signature of ffmpeg.dll & skipping playwright --- .circleci/config.yml | 16 ++++++++-------- scripts/prepare-standalone-playwright.ts | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 474d9ab3..b7c8a6d2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -418,8 +418,6 @@ jobs: if (Test-Path $path) { $signtool = $path Write-Host "✓ Found signtool at: $path" - # write to circleci env var - echo "export SIGNSTOOL=$path" >> $BASH_ENV break } } @@ -454,10 +452,6 @@ jobs: exit 1 } - - run: - name: Verify SIGNTOOL env var - command: | - Write-Host "SIGNSTOOL env var: $env:SIGNSTOOL" - run: name: Verify Signature command: | @@ -543,8 +537,14 @@ jobs: - run: name: Print signature of ffmpeg.dll command: | - Write-Host "SIGNSTOOL env var: $env:SIGNSTOOL" - $env:SIGNSTOOL verify /pa "out\Loadmill-win32-x64\ffmpeg.dll" + Write-Host "Verifying signature of ffmpeg.dll..." + $signtoolPaths = @( + "C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" + ) + $signtool = $signtoolPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 + & $signtool verify /pa "out\Loadmill-win32-x64\ffmpeg.dll" - run: name: Create distribution files command: | diff --git a/scripts/prepare-standalone-playwright.ts b/scripts/prepare-standalone-playwright.ts index ab9ad801..bbe02d6f 100644 --- a/scripts/prepare-standalone-playwright.ts +++ b/scripts/prepare-standalone-playwright.ts @@ -7,6 +7,10 @@ const TARGET_DIR = 'standalone_playwright'; const PLAYWRIGHT_VERSION = '1.50.0'; const main = async (): Promise => { + if (TARGET_DIR) { // Set to true to skip preparation + logInfo('⚠️ Skipping standalone Playwright preparation (disabled by flag)'); + return; + } try { logInfo('🎭 Preparing standalone Playwright...'); From 3358ec144fcc1c79e5efa70a030777dd20865e63 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 12:13:12 +0200 Subject: [PATCH 16/26] back to square 1 --- .circleci/config.yml | 335 ++++++++++++++++++------------------------- forge.config.js | 74 +++++----- 2 files changed, 175 insertions(+), 234 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b7c8a6d2..affa1b8b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -314,270 +314,211 @@ jobs: windows-test-certificate-and-signing: executor: name: win/default - shell: powershell.exe + shell: bash.exe + steps: - checkout - run: name: Create Self-Signed Certificate command: | - Write-Host "Creating self-signed certificate..." + echo "Creating self-signed certificate..." + + # Generate private key + openssl genrsa -out test-key.pem 2048 - # Create a self-signed code signing certificate - $cert = New-SelfSignedCertificate ` - -Type CodeSigningCert ` - -Subject "CN=Loadmill Test Certificate, O=Loadmill LTD, C=US" ` - -KeyAlgorithm RSA ` - -KeyLength 2048 ` - -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" ` - -CertStoreLocation "Cert:\CurrentUser\My" ` - -NotAfter (Get-Date).AddYears(2) + # Create self-signed certificate + openssl req -new -x509 -key test-key.pem -out test-cert.pem -days 730 \ + -subj "/C=US/O=Loadmill LTD/CN=Loadmill Test Certificate" - Write-Host "✓ Certificate created in store" - Write-Host " Thumbprint: $($cert.Thumbprint)" - Write-Host " Subject: $($cert.Subject)" - Write-Host " NotAfter: $($cert.NotAfter)" + echo "✓ Certificate and key created" - # Export to PFX file - $password = ConvertTo-SecureString -String "TestPassword123" -Force -AsPlainText - Export-PfxCertificate -Cert $cert -FilePath "cert.pfx" -Password $password + # Create PFX from cert and key + openssl pkcs12 -export -out cert.pfx \ + -inkey test-key.pem \ + -in test-cert.pem \ + -passout pass:TestPassword123 - Write-Host "✓ Certificate exported to cert.pfx" + # Cleanup temp files + rm test-key.pem test-cert.pem + + echo "✓ Certificate exported to cert.pfx" # Verify the PFX file was created - if (Test-Path cert.pfx) { - $pfxFile = Get-Item cert.pfx - Write-Host "✓ cert.pfx file size: $($pfxFile.Length) bytes" - } else { - Write-Host "✗ ERROR: cert.pfx was not created!" + if [ -f cert.pfx ]; then + echo "✓ cert.pfx file created successfully" + ls -lh cert.pfx + else + echo "✗ ERROR: cert.pfx was not created!" exit 1 - } + fi - run: name: Verify Certificate with certutil command: | - Write-Host "Dumping certificate info..." - certutil -dump -v -p "TestPassword123" cert.pfx + echo "Dumping certificate info..." - Write-Host "`n=== Certificate Details ===" - certutil -dump -v -p "TestPassword123" cert.pfx | Select-String -Pattern "Subject:|Issuer:|NotBefore:|NotAfter:" + # Verify with OpenSSL + openssl pkcs12 -info -in cert.pfx \ + -passin pass:TestPassword123 \ + -passout pass:TestPassword123 \ + -nokeys + + echo "" + echo "=== Certificate Details ===" + openssl pkcs12 -info -in cert.pfx \ + -passin pass:TestPassword123 \ + -passout pass:TestPassword123 \ + -nokeys | grep -E "subject=|issuer=" - run: name: Create Dummy EXE to Sign command: | - Write-Host "Creating a dummy executable..." + echo "Creating a dummy executable..." # Create a simple C# program - $code = @" + cat > test.cs \<< 'EOF' using System; class Program { static void Main() { Console.WriteLine("Hello from signed executable!"); } } - "@ - - # Save the code - $code | Out-File -FilePath "test.cs" -Encoding ASCII + EOF # Compile it (csc.exe is in .NET Framework) - $cscPath = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" + CSC_PATH="/c/Windows/Microsoft.NET/Framework64/v4.0.30319/csc.exe" - if (Test-Path $cscPath) { - Write-Host "Compiling test.cs..." - & $cscPath /out:test.exe test.cs + if [ -f "$CSC_PATH" ]; then + echo "Compiling test.cs..." + "$CSC_PATH" /out:test.exe test.cs - if (Test-Path test.exe) { - Write-Host "✓ test.exe created successfully" - $exeFile = Get-Item test.exe - Write-Host " Size: $($exeFile.Length) bytes" - } else { - Write-Host "✗ ERROR: Failed to create test.exe" + if [ -f test.exe ]; then + echo "✓ test.exe created successfully" + ls -lh test.exe + else + echo "✗ ERROR: Failed to create test.exe" exit 1 - } - } else { - Write-Host "✗ ERROR: C# compiler not found at $cscPath" - Write-Host "Creating a dummy file instead..." - "MZ" | Out-File -FilePath test.exe -Encoding ASCII -NoNewline - } + fi + else + echo "✗ ERROR: C# compiler not found at $CSC_PATH" + echo "Creating a dummy PE file instead..." + echo -ne "MZ\x90\x00" > test.exe + echo "✓ Dummy test.exe created" + fi - run: name: Sign the EXE with signtool command: | - Write-Host "Attempting to sign test.exe..." + echo "Attempting to sign test.exe..." # Find signtool.exe (usually in Windows SDK) - $signtoolPaths = @( - "C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe", - "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", - "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" + SIGNTOOL_PATHS=( + "/c/Program Files (x86)/Windows Kits/10/bin/x64/signtool.exe" + "/c/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64/signtool.exe" + "/c/Program Files (x86)/Windows Kits/10/bin/10.0.19041.0/x64/signtool.exe" ) - $signtool = $null - foreach ($path in $signtoolPaths) { - if (Test-Path $path) { - $signtool = $path - Write-Host "✓ Found signtool at: $path" + SIGNTOOL="" + for path in "${SIGNTOOL_PATHS[@]}"; do + if [ -f "$path" ]; then + SIGNTOOL="$path" + echo "✓ Found signtool at: $path" break - } - } - - if (-not $signtool) { - Write-Host "✗ ERROR: signtool.exe not found!" - Write-Host "Searching for signtool..." - $found = Get-ChildItem "C:\Program Files (x86)\Windows Kits\" -Recurse -Filter "signtool.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 - if ($found) { - $signtool = $found.FullName - Write-Host "✓ Found signtool at: $signtool" - } else { - Write-Host "Installing Windows SDK..." - choco install windows-sdk-10.1 -y + fi + done + + if [ -z "$SIGNTOOL" ]; then + echo "✗ ERROR: signtool.exe not found!" + echo "Searching for signtool..." + FOUND=$(find "/c/Program Files (x86)/Windows Kits/" -name "signtool.exe" 2>/dev/null | head -1) + if [ -n "$FOUND" ]; then + SIGNTOOL="$FOUND" + echo "✓ Found signtool at: $SIGNTOOL" + else + echo "signtool.exe not found anywhere!" exit 1 - } - } + fi + fi # Sign the file - Write-Host "`nSigning test.exe with certificate..." - $certPath = Resolve-Path "cert.pfx" + echo "" + echo "Signing test.exe with certificate..." + CERT_PATH=$(pwd)/cert.pfx - & $signtool sign /f $certPath /p "TestPassword123" /fd SHA256 /v /debug test.exe + "$SIGNTOOL" sign /f "$CERT_PATH" /p "TestPassword123" /fd SHA256 /v /debug test.exe - $exitCode = $LASTEXITCODE - Write-Host "`nSigntool exit code: $exitCode" + EXIT_CODE=$? + echo "" + echo "Signtool exit code: $EXIT_CODE" - if ($exitCode -eq 0) { - Write-Host "✓✓✓ SUCCESS! File signed successfully! ✓✓✓" - } else { - Write-Host "✗✗✗ FAILED! Signing failed with exit code $exitCode ✗✗✗" + if [ $EXIT_CODE -eq 0 ]; then + echo "✓✓✓ SUCCESS! File signed successfully! ✓✓✓" + else + echo "✗✗✗ FAILED! Signing failed with exit code $EXIT_CODE ✗✗✗" exit 1 - } + fi - run: name: Verify Signature command: | - Write-Host "Verifying signature on test.exe..." + echo "Verifying signature on test.exe..." # Find signtool - $signtoolPaths = @( - "C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe", - "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", - "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" + SIGNTOOL_PATHS=( + "/c/Program Files (x86)/Windows Kits/10/bin/x64/signtool.exe" + "/c/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64/signtool.exe" + "/c/Program Files (x86)/Windows Kits/10/bin/10.0.19041.0/x64/signtool.exe" ) - $signtool = $signtoolPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 + SIGNTOOL="" + for path in "${SIGNTOOL_PATHS[@]}"; do + if [ -f "$path" ]; then + SIGNTOOL="$path" + break + fi + done - if ($signtool) { - & $signtool verify /pa /v test.exe - Write-Host "`nNote: Verification will fail because it's self-signed (not trusted)" - Write-Host "But the signature itself should be present!" - } + if [ -n "$SIGNTOOL" ]; then + "$SIGNTOOL" verify /pa /v test.exe || true + echo "" + echo "Note: Verification will fail because it's self-signed (not trusted)" + echo "But the signature itself should be present!" + fi # Check if signature exists using PowerShell - Write-Host "`nChecking signature with Get-AuthenticodeSignature..." - $sig = Get-AuthenticodeSignature test.exe - Write-Host "Status: $($sig.Status)" - Write-Host "StatusMessage: $($sig.StatusMessage)" - Write-Host "SignerCertificate: $($sig.SignerCertificate.Subject)" - - if ($sig.SignerCertificate) { - Write-Host "`n✓✓✓ SIGNATURE EXISTS! ✓✓✓" - } else { - Write-Host "`n✗✗✗ NO SIGNATURE FOUND ✗✗✗" - } + echo "" + echo "Checking signature with Get-AuthenticodeSignature..." + powershell.exe -Command " + \$sig = Get-AuthenticodeSignature test.exe + Write-Host \"Status: \$(\$sig.Status)\" + Write-Host \"StatusMessage: \$(\$sig.StatusMessage)\" + Write-Host \"SignerCertificate: \$(\$sig.SignerCertificate.Subject)\" + + if (\$sig.SignerCertificate) { + Write-Host \"\`n✓✓✓ SIGNATURE EXISTS! ✓✓✓\" + } else { + Write-Host \"\`n✗✗✗ NO SIGNATURE FOUND ✗✗✗\" + } + " - run: name: Summary command: | - Write-Host "`n=========================================" - Write-Host " TEST SUMMARY" - Write-Host "=========================================" - Write-Host "✓ Certificate created: cert.pfx" - Write-Host "✓ Password: TestPassword123" - Write-Host "✓ Test file signed: test.exe" - Write-Host "`nNext steps:" - Write-Host "1. Use the same PowerShell commands in your main build" - Write-Host "2. Replace 'TestPassword123' with your actual password" - Write-Host "3. Apply to electron-forge config" - Write-Host "=========================================" - - run: - name: Install Node 20.19.5 - command: | - nvm install 20.19.5 - nvm use 20.19.5 - - run: - name: Install Yarn - command: npm install -g yarn - - restore_cache: - keys: - - yarn-packages-windows-build-{{ .Branch }}-{{ checksum "yarn.lock" }} - - yarn-packages-windows-build-{{ .Branch }}- - - yarn-packages-windows-build- - - run: - name: Install dependencies - command: yarn install - - save_cache: - paths: - - node_modules/ - key: yarn-packages-windows-build-{{ .Branch }}-{{ checksum "yarn.lock" }} - - run: - name: Run Tests - command: yarn test - - run: - name: Package Application - command: yarn package - - run: - name: Print project directory contents - command: Get-ChildItem -Force - - run: - name: Print /out directory contents - command: Get-ChildItem -Force out - - run: - name: Print /out/Loadmill-win32-x64 directory contents - command: Get-ChildItem -Force -Recurse out/Loadmill-win32-x64 - - run: - name: Print signature of ffmpeg.dll - command: | - Write-Host "Verifying signature of ffmpeg.dll..." - $signtoolPaths = @( - "C:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe", - "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", - "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" - ) - $signtool = $signtoolPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 - & $signtool verify /pa "out\Loadmill-win32-x64\ffmpeg.dll" - - run: - name: Create distribution files - command: | - $env:DEBUG="*" - yarn only-make - - run: - name: Print /out/make directory contents - command: Get-ChildItem -Force out/make + echo "" + echo "=========================================" + echo " TEST SUMMARY" + echo "=========================================" + echo "✓ Certificate created: cert.pfx" + echo "✓ Password: TestPassword123" + echo "✓ Test file signed: test.exe" + echo "" + echo "Next steps:" + echo "1. Use the same bash/OpenSSL commands in your main build" + echo "2. Replace 'TestPassword123' with your actual password" + echo "3. Apply to electron-forge config" + echo "=========================================" -workflows: - version: 2 - build: - jobs: - - build_macos: - filters: - branches: - ignore: - - /agent-version-.*/ - - windows-certificate - pre-steps: - - run: - name: Use Node 20 - command: | - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install 20 - nvm use 20 - npm install -g yarn - - run: - name: Persist Node 20 in environment for subsequent steps (silenced output) - command: | - echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV - echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && nvm use 20 > /dev/null 2>&1' >> $BASH_ENV - build_windows: filters: branches: diff --git a/forge.config.js b/forge.config.js index d14e7910..0af0bcf1 100644 --- a/forge.config.js +++ b/forge.config.js @@ -42,49 +42,49 @@ module.exports = { }, ], packagerConfig: { - afterCopyExtraResources: [ - (buildPath, electronVersion, platform, arch, callback) => { - const path = require('path'); - const fs = require('fs'); - const glob = require('glob'); - console.log('Running afterCopyExtraResources hook...'); - console.log({ arch, buildPath, electronVersion, platform }); + // afterCopyExtraResources: [ + // (buildPath, electronVersion, platform, arch, callback) => { + // const path = require('path'); + // const fs = require('fs'); + // const glob = require('glob'); + // console.log('Running afterCopyExtraResources hook...'); + // console.log({ arch, buildPath, electronVersion, platform }); - const chromiumDir = path.join( - buildPath, - 'Loadmill.app', - 'Contents', - 'Resources', - 'standalone_playwright', - 'node_modules', - 'playwright-core', - '.local-browsers', - ); + // const chromiumDir = path.join( + // buildPath, + // 'Loadmill.app', + // 'Contents', + // 'Resources', + // 'standalone_playwright', + // 'node_modules', + // 'playwright-core', + // '.local-browsers', + // ); - console.log({ chromiumDir }); + // console.log({ chromiumDir }); - try { - const matches = glob.sync( - path.join(chromiumDir, '**/gpu_shader_cache.bin'), - { nodir: true }, - ); + // try { + // const matches = glob.sync( + // path.join(chromiumDir, '**/gpu_shader_cache.bin'), + // { nodir: true }, + // ); - matches.forEach(file => { - console.log(`Changing permissions for: ${file}`); - fs.chmodSync(file, 0o644); - }); + // matches.forEach(file => { + // console.log(`Changing permissions for: ${file}`); + // fs.chmodSync(file, 0o644); + // }); - console.log('Permissions changed successfully.'); - } catch (err) { - console.warn('Permission fix failed (may be fine if files don’t exist):', err.message); - } + // console.log('Permissions changed successfully.'); + // } catch (err) { + // console.warn('Permission fix failed (may be fine if files don’t exist):', err.message); + // } - callback(); - }, - ], - extraResource: [ - 'standalone_playwright', - ], + // callback(); + // }, + // ], + // extraResource: [ + // 'standalone_playwright', + // ], icon: isWindowsOS ? './images/loadmill-icon-256-256' : './images/MyIcon', osxNotarize: { appleId: process.env.APPLE_ID, From 1359ad946cbc0e666730c5e2e1e492d7bc581c04 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 12:17:40 +0200 Subject: [PATCH 17/26] dang it --- .circleci/config.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index affa1b8b..bfc336fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -519,10 +519,35 @@ jobs: echo "3. Apply to electron-forge config" echo "=========================================" +workflows: + version: 2 + build: + jobs: + - build_macos: + filters: + branches: + ignore: + - /agent-version-.*/ + - windows-certificate + pre-steps: + - run: + name: Use Node 20 + command: | + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 20 + nvm use 20 + npm install -g yarn + - run: + name: Persist Node 20 in environment for subsequent steps (silenced output) + command: | + echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV + echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && nvm use 20 > /dev/null 2>&1' >> $BASH_ENV - build_windows: filters: branches: ignore: - /agent-version-.*/ - windows-certificate + - windows-test-certificate-and-signing \ No newline at end of file From 284f857a1b38324f67aec06a5c0b162aa44863c1 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 13:32:50 +0200 Subject: [PATCH 18/26] Create self-signed certificate fix --- .circleci/config.yml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bfc336fd..d3adeec8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -327,9 +327,13 @@ jobs: # Generate private key openssl genrsa -out test-key.pem 2048 + echo "✓ Private key created" + # Create self-signed certificate - openssl req -new -x509 -key test-key.pem -out test-cert.pem -days 730 \ - -subj "/C=US/O=Loadmill LTD/CN=Loadmill Test Certificate" + openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj "/C=US/O=Loadmill LTD/CN=Loadmill Test Certificate" + + echo "Verifying certificate creation..." + openssl x509 -in test-cert.pem -text -noout echo "✓ Certificate and key created" @@ -339,12 +343,14 @@ jobs: -in test-cert.pem \ -passout pass:TestPassword123 + echo "✓ PFX file created" + # Cleanup temp files rm test-key.pem test-cert.pem echo "✓ Certificate exported to cert.pfx" - # Verify the PFX file was created + echo "Verifying the PFX file was created" if [ -f cert.pfx ]; then echo "✓ cert.pfx file created successfully" ls -lh cert.pfx @@ -352,18 +358,19 @@ jobs: echo "✗ ERROR: cert.pfx was not created!" exit 1 fi + echo "File creation complete." - run: - name: Verify Certificate with certutil + name: Verify Certificate command: | - echo "Dumping certificate info..." - - # Verify with OpenSSL + echo "Verifying certificate with openssl..." openssl pkcs12 -info -in cert.pfx \ -passin pass:TestPassword123 \ -passout pass:TestPassword123 \ -nokeys + echo "Dumping certificate info..." + echo "" echo "=== Certificate Details ===" openssl pkcs12 -info -in cert.pfx \ @@ -371,6 +378,7 @@ jobs: -passout pass:TestPassword123 \ -nokeys | grep -E "subject=|issuer=" + echo "Finished verifying certificate." - run: name: Create Dummy EXE to Sign command: | From 4b04505294b9fef2401ebed1880faa9e5cb98171 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 13:37:28 +0200 Subject: [PATCH 19/26] git bash trouble --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d3adeec8..8d5b98e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -330,7 +330,7 @@ jobs: echo "✓ Private key created" # Create self-signed certificate - openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj "/C=US/O=Loadmill LTD/CN=Loadmill Test Certificate" + openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj "//C=US//O=Loadmill LTD//CN=Loadmill Test Certificate" echo "Verifying certificate creation..." openssl x509 -in test-cert.pem -text -noout From 1caa7a737a7e95ba987204dab67e47b09f6e05dd Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 13:40:20 +0200 Subject: [PATCH 20/26] another fix --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8d5b98e5..1bfd9fe7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -330,7 +330,7 @@ jobs: echo "✓ Private key created" # Create self-signed certificate - openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj "//C=US//O=Loadmill LTD//CN=Loadmill Test Certificate" + openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj "//C=US//O=Loadmill LTD//CN=Loadmill Test Certificate" echo "Verifying certificate creation..." openssl x509 -in test-cert.pem -text -noout From 9b9b6131c9cfb1b2192ca6ce4d4a83db18571ca1 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 13:43:01 +0200 Subject: [PATCH 21/26] work already --- .circleci/config.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1bfd9fe7..b2aee9c4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -324,13 +324,13 @@ jobs: command: | echo "Creating self-signed certificate..." - # Generate private key + echo "Generating private key..." openssl genrsa -out test-key.pem 2048 echo "✓ Private key created" - # Create self-signed certificate - openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out test-cert.pem -subj "//C=US//O=Loadmill LTD//CN=Loadmill Test Certificate" + echo "Creating self-signed certificate using the private key..." + MSYS_NO_PATHCONV=1 openssl req -new -key test-key.pem -x509 -days 365 -out test-cert.pem -subj "/C=US/O=Loadmill LTD/CN=Loadmill Test Certificate" echo "Verifying certificate creation..." openssl x509 -in test-cert.pem -text -noout @@ -359,7 +359,6 @@ jobs: exit 1 fi echo "File creation complete." - - run: name: Verify Certificate command: | From f0cd2ad46e28fb528b7aa665b8c9f65d165981bc Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 13:55:58 +0200 Subject: [PATCH 22/26] debugging No file digest algorithm specified Error --- .circleci/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b2aee9c4..05e0a0e3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -452,6 +452,10 @@ jobs: echo "" echo "Signing test.exe with certificate..." CERT_PATH=$(pwd)/cert.pfx + echo "CERT_PATH: $CERT_PATH" + echo "SIGNTOOL: $SIGNTOOL" + # printing the command for debugging + echo '"$SIGNTOOL" sign /f "$CERT_PATH" /p "TestPassword123" /fd SHA256 /v /debug test.exe' "$SIGNTOOL" sign /f "$CERT_PATH" /p "TestPassword123" /fd SHA256 /v /debug test.exe From 9d89c605a59e984adc874bc7072c1f3b48e55f7c Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 14:01:10 +0200 Subject: [PATCH 23/26] cygpath --- .circleci/config.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 05e0a0e3..90ad64ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -451,13 +451,15 @@ jobs: # Sign the file echo "" echo "Signing test.exe with certificate..." + CERT_PATH=$(pwd)/cert.pfx - echo "CERT_PATH: $CERT_PATH" + CERT_PATH_WIN=$(cygpath -w "$CERT_PATH") + + echo "CERT_PATH (Unix): $CERT_PATH" + echo "CERT_PATH (Windows): $CERT_PATH_WIN" echo "SIGNTOOL: $SIGNTOOL" - # printing the command for debugging - echo '"$SIGNTOOL" sign /f "$CERT_PATH" /p "TestPassword123" /fd SHA256 /v /debug test.exe' - "$SIGNTOOL" sign /f "$CERT_PATH" /p "TestPassword123" /fd SHA256 /v /debug test.exe + "$SIGNTOOL" sign /f "$CERT_PATH_WIN" /p "TestPassword123" /fd SHA256 /v /debug test.exe EXIT_CODE=$? echo "" From 6a38f50f8c24834447c63a6f1379fdd376956459 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 14:13:44 +0200 Subject: [PATCH 24/26] =?UTF-8?q?=F0=9F=A4=B7=E2=80=8D=E2=99=82=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .circleci/config.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 90ad64ad..d1dfb3c0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -454,12 +454,14 @@ jobs: CERT_PATH=$(pwd)/cert.pfx CERT_PATH_WIN=$(cygpath -w "$CERT_PATH") + SIGNTOOL_WIN=$(cygpath -w "$SIGNTOOL") echo "CERT_PATH (Unix): $CERT_PATH" echo "CERT_PATH (Windows): $CERT_PATH_WIN" - echo "SIGNTOOL: $SIGNTOOL" + echo "SIGNTOOL (Windows): $SIGNTOOL_WIN" - "$SIGNTOOL" sign /f "$CERT_PATH_WIN" /p "TestPassword123" /fd SHA256 /v /debug test.exe + # Use cmd.exe to run signtool to avoid Bash argument parsing issues + cmd.exe /c "\"$SIGNTOOL_WIN\" sign /f \"$CERT_PATH_WIN\" /p TestPassword123 /fd SHA256 /v /debug test.exe" EXIT_CODE=$? echo "" From 24ecb2ec031e89dcb331e8a1de1f87bb23813160 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Mon, 8 Dec 2025 14:41:52 +0200 Subject: [PATCH 25/26] add rest of flow & optimize deps cache --- .circleci/config.yml | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d1dfb3c0..0ff3105c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -533,7 +533,47 @@ jobs: echo "2. Replace 'TestPassword123' with your actual password" echo "3. Apply to electron-forge config" echo "=========================================" - + - run: + name: Install Node 20.19.5 + command: | + nvm install 20.19.5 + nvm use 20.19.5 + - run: + name: Install Yarn + command: npm install -g yarn + - restore_cache: + keys: + - yarn-packages-windows-build-{{ .Branch }}-{{ checksum "yarn.lock" }} + - yarn-packages-windows-build-{{ .Branch }}- + - yarn-packages-windows-build- + - run: + name: Install dependencies + command: yarn --frozen-lockfile --cache-folder ~/.cache/yarn + - save_cache: + paths: + - ~/.cache/yarn + key: yarn-packages-windows-build-{{ .Branch }}-{{ checksum "yarn.lock" }} + - run: + name: Run Tests + command: yarn test + - run: + name: Package Application + command: yarn package + - run: + name: Print project directory contents + command: ls -la + - run: + name: Print /out directory contents + command: ls -la out + - run: + name: Create distribution files + command: DEBUG=* yarn only-make + - run: + name: Print /out/make directory contents + command: ls -la out/make + - store_artifacts: + name: Store Artifacts + path: out/make/ workflows: version: 2 build: From 16ef8bbe8094c56827320452a86df30d38744458 Mon Sep 17 00:00:00 2001 From: Gilad Gur Andelman Date: Wed, 10 Dec 2025 11:37:41 +0200 Subject: [PATCH 26/26] added ps1 script for debugging and testing functionality (not prod ready code) --- scripts/windows-self-signed-certificate.ps1 | 250 ++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 scripts/windows-self-signed-certificate.ps1 diff --git a/scripts/windows-self-signed-certificate.ps1 b/scripts/windows-self-signed-certificate.ps1 new file mode 100644 index 00000000..49d01033 --- /dev/null +++ b/scripts/windows-self-signed-certificate.ps1 @@ -0,0 +1,250 @@ +# ========================================================== +# Loadmill Signing Script (Self-signed, Test Only) +# ========================================================== + +# -------------------------- +# CONFIGURATION +# -------------------------- +$env:LOADMILL_DIR = "C:\Users\Gilad\AppData\Local\desktop_app\app-3.8.2" +# Path to signtool.exe from Windows SDK +$signtool = "C:\Program Files (x86)\Windows +Kits\10\bin\10.0.19041.0\x64\signtool.exe" + +Write-Host "=== Loadmill folder path ===" -ForegroundColor Yellow +Write-Host $env:LOADMILL_DIR +Write-Host "`n=== Signtool path ===" -ForegroundColor Yellow +Write-Host $signtool + +# -------------------------- +# STEP 1: Create self-signed certificate +# -------------------------- +Write-Host "`n=== STEP 1: Creating temporary self-signed certificate ===" +-ForegroundColor Cyan +try { + $cert = New-SelfSignedCertificate ` + -Type CodeSigningCert ` + -Subject "CN=Loadmill Test Cert" ` + -CertStoreLocation "Cert:\localMachine\My" ` + -KeyExportPolicy Exportable ` + -KeyLength 2048 ` + -HashAlgorithm SHA256 + + Write-Host "Certificate created successfully." -ForegroundColor Green + Write-Host "Thumbprint: $($cert.Thumbprint)" + + # Store the thumbprint for later use + $thumbprint = $cert.Thumbprint +} catch { + Write-Host "ERROR: Failed to create self-signed certificate." +-ForegroundColor Red + Write-Host $_ + exit +} + +# -------------------------- +# STEP 1.1: Add cert to trust store (silently) +# -------------------------- +Write-Host "`n=== STEP 1.1: Adding certificate to Trusted Root (silent) +===" -ForegroundColor Cyan + +# Check if running as Administrator +$isAdmin = ([Security.Principal.WindowsPrincipal] +[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + +if (-not $isAdmin) { + Write-Host "WARNING: Not running as Administrator. Certificate +installation may prompt for confirmation." -ForegroundColor Yellow + Write-Host "For silent installation, run PowerShell as Administrator." +-ForegroundColor Yellow +} + +try { + # Get the certificate by thumbprint + Write-Host "Looking for temporary certificate with thumbprint: +$thumbprint" + $cert = Get-ChildItem "Cert:\localMachine\My\$thumbprint" -ErrorAction +Stop + + Write-Host "Certificate found: $($cert.Subject)" + + # Add to Trusted Root store (localMachine scope, no prompt) + Write-Host "Adding certificate to Trusted Root store..." + $rootStore = New-Object +System.Security.Cryptography.X509Certificates.X509Store("Root", +"localMachine") + $rootStore.Open("ReadWrite") + $rootStore.Add($cert) + $rootStore.Close() + Write-Host "Added to Root store successfully." -ForegroundColor Green + + # Add to Trusted Publishers store + Write-Host "Adding certificate to Trusted Publishers store..." + $pubStore = New-Object +System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", +"localMachine") + $pubStore.Open("ReadWrite") + $pubStore.Add($cert) + $pubStore.Close() + Write-Host "Added to TrustedPublisher store successfully." +-ForegroundColor Green + + Write-Host "Certificate installation completed without prompts." +-ForegroundColor Green +} catch { + Write-Host "ERROR: Failed to add certificate to trust stores." +-ForegroundColor Red + Write-Host $_.Exception.Message +} + +# -------------------------- +# STEP 2: Sign all .exe and .dll files +# -------------------------- +Write-Host "`n=== STEP 2: Signing all .exe and .dll files ===" +-ForegroundColor Cyan +$filesToSign = Get-ChildItem $env:LOADMILL_DIR -Recurse -File -Include +*.exe, *.dll + +Write-Host "Found $($filesToSign.Count) files to sign." + +foreach ($file in $filesToSign) { + Write-Host "Signing file: $($file.FullName)" + try { + $signArgs = @( + "sign", + "/fd", "SHA256", + "/sm", + "/s", "My", + "/sha1", $cert.Thumbprint, + $file.FullName + ) + & $signtool $signArgs + if ($LASTEXITCODE -eq 0) { + Write-Host "Signed successfully: $($file.Name)" +-ForegroundColor Green + } else { + Write-Host "WARNING: signtool exited with code $LASTEXITCODE +for $($file.Name)" -ForegroundColor Yellow + } + } catch { + Write-Host "ERROR signing file: $($file.FullName)" -ForegroundColor +Red + Write-Host $_ + } +} + +# -------------------------- +# STEP 3: Verify all files using signtool +# -------------------------- +Write-Host "`n=== STEP 3: Verifying with signtool ===" -ForegroundColor Cyan +$signtoolFailures = @() + +foreach ($file in $filesToSign) { + Write-Host "Verifying file: $($file.FullName)" -ForegroundColor Yellow + try { + & $signtool verify /pa $file.FullName + if ($LASTEXITCODE -ne 0) { + Write-Host "signtool verification FAILED for $($file.Name)" +-ForegroundColor Red + $signtoolFailures += $file.FullName + } else { + Write-Host "Verified successfully: $($file.Name)" +-ForegroundColor Green + } + } catch { + Write-Host "ERROR verifying file: $($file.FullName)" +-ForegroundColor Red + Write-Host $_ + $signtoolFailures += $file.FullName + } +} + +# -------------------------- +# STEP 4: Verify all files using Get-AuthenticodeSignature +# -------------------------- +Write-Host "`n=== STEP 4: Verifying with Get-AuthenticodeSignature ===" +-ForegroundColor Cyan +$verificationResults = @() + +foreach ($file in $filesToSign) { + Write-Host "Checking signature status for: $($file.FullName)" +-ForegroundColor Yellow + try { + $sig = Get-AuthenticodeSignature $file.FullName + $verificationResults += [PSCustomObject]@{ + File = $file.FullName + Status = $sig.Status + } + } catch { + Write-Host "ERROR reading signature: $($file.FullName)" +-ForegroundColor Red + $verificationResults += [PSCustomObject]@{ + File = $file.FullName + Status = "Error" + } + } +} + +# -------------------------- +# STEP 5: Print summary of any failures +# -------------------------- +Write-Host "`n=== Signature Verification Summary ===" -ForegroundColor Cyan + +# Files failed signtool +if ($signtoolFailures.Count -gt 0) { + Write-Host "`nFiles failed signtool verification:" -ForegroundColor Red + $signtoolFailures | ForEach-Object { Write-Host $_ -ForegroundColor Red +} +} else { + Write-Host "All files passed signtool verification." -ForegroundColor +Green +} + +# Files failed PS verification +$psFailures = $verificationResults | Where-Object { $_.Status -ne 'Valid' } +if ($psFailures.Count -gt 0) { + Write-Host "`nFiles failed Get-AuthenticodeSignature verification:" +-ForegroundColor Red + $psFailures | Format-Table -AutoSize +} else { + Write-Host "All files passed Get-AuthenticodeSignature verification." +-ForegroundColor Green +} + +# -------------------------- +# STEP 6: Cleanup self-signed certificate +# -------------------------- +Write-Host "`n=== STEP 6: Cleaning up temporary certificate ===" +-ForegroundColor Cyan +try { + # Remove from Personal store + Remove-Item "Cert:\localMachine\My\$($cert.Thumbprint)" -ErrorAction +SilentlyContinue + + # Remove from Root store + $rootStore = New-Object +System.Security.Cryptography.X509Certificates.X509Store("Root", +"localMachine") + $rootStore.Open("ReadWrite") + $certs = $rootStore.Certificates | Where-Object { $_.Thumbprint -eq +$cert.Thumbprint } + foreach ($c in $certs) { $rootStore.Remove($c) } + $rootStore.Close() + + # Remove from TrustedPublisher store + $pubStore = New-Object +System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", +"localMachine") + $pubStore.Open("ReadWrite") + $certs = $pubStore.Certificates | Where-Object { $_.Thumbprint -eq +$cert.Thumbprint } + foreach ($c in $certs) { $pubStore.Remove($c) } + $pubStore.Close() + + Write-Host "Temporary certificate removed from all stores +successfully." -ForegroundColor Green +} catch { + Write-Host "WARNING: Failed to remove certificate." -ForegroundColor Red + Write-Host $_ +} + +Write-Host "`n=== Script finished ===" -ForegroundColor Green \ No newline at end of file