Merge branch 'signmacosdesktop' of https://github.com/ethereum/remix-project into desktope2e

pull/4837/head
filip mertens 8 months ago
commit 8c2cda5eb9
  1. 129
      .circleci/config.yml
  2. 1
      .gitignore
  3. 192
      apps/remix-ide/ci/update_desktop_release_assets.ts
  4. 7
      apps/remix-ide/src/app.js
  5. 55
      apps/remix-ide/src/app/plugins/electron/appUpdaterPlugin.ts
  6. 30
      apps/remixdesktop/afterbuild.js
  7. 90
      apps/remixdesktop/aftersign.js
  8. 13
      apps/remixdesktop/entitlements.mac.plist
  9. 39
      apps/remixdesktop/notarizedmg.sh
  10. 54
      apps/remixdesktop/package.json
  11. 3
      apps/remixdesktop/src/engine.ts
  12. 121
      apps/remixdesktop/src/plugins/appUpdater.ts
  13. 2
      apps/remixdesktop/src/plugins/compilerLoader.ts
  14. 2
      apps/remixdesktop/src/preload.ts
  15. 1031
      apps/remixdesktop/yarn.lock
  16. 1
      package.json
  17. 16
      yarn.lock

@ -160,15 +160,24 @@ jobs:
yarn add node-pty
yarn --ignore-optional
yarn add @remix-project/remix-ws-templates
PUBLISH_FOR_PULL_REQUEST='true' yarn dist
yarn dist
rm -rf release/*-unpacked
- save_cache:
key: remixdesktop-linux-deps-{{ checksum "apps/remixdesktop/yarn.lock" }}
paths:
- apps/remixdesktop/node_modules
- run:
name: "remove unnecessary files"
command: |
rm -rf ~/remix-project/apps/remixdesktop/release/.icon*
rm -rf ~/remix-project/apps/remixdesktop/release/builder*
- store_artifacts:
path: apps/remixdesktop/release/
destination: remixdesktop-linux
- persist_to_workspace:
root: apps/remixdesktop
paths:
- "release"
build-remixdesktop-windows:
@ -205,7 +214,7 @@ jobs:
cp -r dist/apps/remix-ide apps/remixdesktop/build
cd apps/remixdesktop/
yarn
PUBLISH_FOR_PULL_REQUEST='true' yarn dist
yarn dist
rm -rf release/*-unpacked
- save_cache:
key: remixdesktop-windows-deps-{{ checksum "apps/remixdesktop/yarn.lock" }}
@ -265,28 +274,66 @@ jobs:
command: |
Get-ChildItem -Path 'C:\Program Files (x86)\Windows Kits\10\App Certification Kit' -Filter signtool.exe -Recurse
- run:
name: "Signtool-Signing"
name: read env
shell: powershell.exe
command: |
& $env:Signtool sign /sha1 $env:SM_CODE_SIGNING_CERT_SHA1_HASH /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 $env:RemixSetupExe
# Specify the path to your package.json file
$packageJsonPath = "C:\Users\circleci\remix-project\apps\remixdesktop\package.json"
# Check if the file exists
if (Test-Path $packageJsonPath) {
# Read the content of the package.json file
$packageJsonContent = Get-Content $packageJsonPath -Raw | ConvertFrom-Json
# Check if the 'version' field exists in the package.json
if ($packageJsonContent.'version' -ne $null) {
# Store the version value in an environment variable
$version = $packageJsonContent.version
$file = "C:\Users\circleci\remix-project\release\Remix-Desktop-Setup-$($version).exe"
Write-Host "Version $(file) stored in PACKAGE_VERSION environment variable."
"Set-Variable -Name 'PACKAGE_VERSION' -Value '$file' -Scope Global" > SetEnvVars.ps1
dir Env:
} else {
Write-Host "Error: 'version' field not found in package.json."
}
} else {
Write-Host "Error: package.json file not found at $packageJsonPath."
}
- run:
name: "Signtool-Signing"
shell: powershell.exe
command: |
. .\SetEnvVars.ps1
dir Env:
& $env:Signtool sign /sha1 $env:SM_CODE_SIGNING_CERT_SHA1_HASH /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 $PACKAGE_VERSION
- run:
name: "Signtool-Verification"
shell: powershell.exe
command: |
$verify_output = $(& $env:Signtool verify /v /pa $env:RemixSetupExe)
. .\SetEnvVars.ps1
$verify_output = $(& $env:Signtool verify /v /pa $PACKAGE_VERSION)
echo ${verify_output}
if (!$verify_output.Contains("Number of files successfully Verified: 1")) {
echo 'Verification failed'
exit 1
}
- run:
name: "remove unnecessary files"
shell: bash.exe
command: |
rm -rf ~/remix-project/release/.icon*
rm -rf ~/remix-project/release/builder*
- store_artifacts:
path: ~/remix-project/release/
destination: remixdesktop-windows
- persist_to_workspace:
root: ~/remix-project/
paths:
- "release"
environment:
SM_CLIENT_CERT_FILE: 'C:\Certificate_pkcs12.p12'
Signtool: 'C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe'
SSM: 'C:\Program Files\DigiCert\DigiCert One Signing Manager Tools'
RemixSetupExe: 'C:\Users\circleci\remix-project\release\Remix IDE.exe'
build-remixdesktop-mac:
macos:
@ -298,6 +345,19 @@ jobs:
- checkout
- attach_workspace:
at: .
- run:
name: Install Apple Certificate
command: |
echo $APPLE_CERTIFICATE_BASE64 | base64 --decode > /tmp/certificate.p12
security create-keychain -p ci-password build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p ci-password build.keychain
curl -o DeveloperIDG2CA.cer "https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer"
sudo security import DeveloperIDG2CA.cer -k /Library/Keychains/System.keychain -T /usr/bin/codesign
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain DeveloperIDG2CA.cer
security import /tmp/certificate.p12 -k build.keychain -P $APPLE_CERTIFICATE_PASSWORD -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k ci-password build.keychain
security find-identity -v -p codesigning
- run: unzip ./persist/desktopbuild.zip
- run:
command: |
@ -319,21 +379,61 @@ jobs:
# use USE_HARD_LINK=false https://github.com/electron-userland/electron-builder/issues/3179
- run:
command: |
nvm use 20.0.0
nvm use 20
mkdir apps/remixdesktop/build
cp -r dist/apps/remix-ide apps/remixdesktop/build
cd apps/remixdesktop
yarn
- run:
command: |
nvm use 20
cd apps/remixdesktop
yarn installRipGrepMacOXarm64
PUBLISH_FOR_PULL_REQUEST='true' USE_HARD_LINKS=false yarn dist --mac --arm64
PUBLISH_FOR_PULL_REQUEST='false' USE_HARD_LINKS=false yarn dist --arm64
- run:
command: |
nvm use 20
cd apps/remixdesktop
yarn installRipGrepMacOXx64
PUBLISH_FOR_PULL_REQUEST='true' USE_HARD_LINKS=false yarn dist --mac --x64
rm -rf release/mac*
PUBLISH_FOR_PULL_REQUEST='false' USE_HARD_LINKS=false yarn dist --x64
- run:
name: Notarize the app
command: |
brew install jq
cd apps/remixdesktop
zsh notarizedmg.sh
- run:
name: "remove unnecessary files"
command: |
rm -rf ~/remix-project/apps/remixdesktop/release/.icon*
rm -rf ~/remix-project/apps/remixdesktop/release/builder*
rm -rf ~/remix-project/apps/remixdesktop/release/*.blockmap
rm -rf ~/remix-project/apps/remixdesktop/release/_.*
- store_artifacts:
path: apps/remixdesktop/release/
destination: remixdesktop-mac
- persist_to_workspace:
root: apps/remixdesktop
paths:
- "release"
uploadartifacts:
docker:
- image: cimg/node:20.0.0-browsers
resource_class:
xlarge
working_directory: ~/remix-project
steps:
- checkout
- attach_workspace:
at: .
- restore_cache:
keys:
- v1-deps-{{ checksum "yarn.lock" }}
- run: yarn
- run:
name: "Upload Artifacts"
command: npx ts-node apps/remix-ide/ci/update_desktop_release_assets.ts
lint:
docker:
- image: cimg/node:20.0.0-browsers
@ -581,6 +681,11 @@ workflows:
- test-remixdesktop-linux:
requires:
- build-desktop
- uploadartifacts:
requires:
- build-remixdesktop-mac
- build-remixdesktop-linux
- sign-remixdesktop-windows
- build-plugin:
matrix:
parameters:

1
.gitignore vendored

@ -15,6 +15,7 @@ soljson.js
*_group*.*.ts
*_group*.ts
stats.json
release
# compiled output

@ -0,0 +1,192 @@
import {Octokit} from 'octokit'
import * as fs from 'fs'
import * as path from 'path'
import YAML from 'yaml'
import crypto from 'crypto'
const owner = 'bunsenstraat'
const repo = 'remix-desktop'
const headers = {
'X-GitHub-Api-Version': '2022-11-28',
}
const octokit = new Octokit({
auth: process.env.GH_TOKEN,
})
async function getAllReleases() {
const releases = await octokit.request('GET /repos/{owner}/{repo}/releases', {
owner: owner,
repo: repo,
headers: headers,
})
return releases.data
}
async function uploadReleaseAsset(release, name, file) {
const upload_url = release.upload_url
console.log(`Uploading ${name} to ${upload_url}`)
octokit.request({
method: "POST",
url: upload_url,
headers: {
"content-type": "text/plain",
},
data: fs.readFileSync(file),
name,
label: name
});
}
async function getVersionFromPackageJson() {
// ignore ts error
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require(__dirname + '/../../../apps/remixdesktop/package.json')
return packageJson.version
}
async function readReleaseFilesFromLocalDirectory() {
const directoryPath = path.join(__dirname, '../../../release')
const files = fs.readdirSync(directoryPath)
return files
}
async function removeAsset(asset) {
await octokit.request('DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}', {
owner: owner,
repo: repo,
asset_id: asset.id,
headers: headers,
})
}
async function hashFile(file): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha512').setEncoding('base64');
// hash.on('error', reject).setEncoding(encoding);
fs.createReadStream(
file,
Object.assign({}, {}, {
highWaterMark: 1024 * 1024,
/* better to use more memory but hash faster */
})
)
.on('error', reject)
.on('end', () => {
hash.end();
console.log('hash done');
console.log(hash.read());
resolve(hash.digest('base64'));
})
.pipe(
hash,
{
end: false,
}
);
});
}
async function main() {
const allReleases = await getAllReleases()
const version = await getVersionFromPackageJson()
console.log(`preparing release version: ${version}`)
let release
allReleases.find((r) => {
if (r.tag_name === `v${version}`) {
release = r
}
})
if (!release) {
console.log('No release found.')
// create release
console.log(`Creating release ${version}`)
const r = await octokit.request('POST /repos/{owner}/{repo}/releases', {
owner: owner,
repo: repo,
tag_name: `v${version}`,
name: `${version}`,
draft: true,
headers: headers,
})
release = r.data
}
let ymlFiles = await readReleaseFilesFromLocalDirectory()
ymlFiles = ymlFiles.filter((file) => file.endsWith('.yml') && file.startsWith('latest'))
console.log(`Found ${ymlFiles.length} yml files to upload`)
// read and parse yml latest files
// the yml files contain the sha512 hash and file size of the executable
// we need to recalculate the hash and file size of the executable
// and update the yml files
// this is because the executable is resigned after the yml files are created
for (const file of ymlFiles) {
const content = fs.readFileSync(path.join(__dirname, '../../../release', file), 'utf8')
const parsed = YAML.parse(content)
const hashes:{
url: string,
sha512: string,
size: number
}[] = []
if(parsed.files) {
console.log(`Found`, parsed.files)
for (const f of parsed.files) {
const executable = f.url
const exists = fs.existsSync(path.join(__dirname, '../../../release', executable))
if (!exists) {
console.log(`File ${executable} does not exist on local fs. Skipping...`)
continue
}else{
console.log(`File ${executable} exists on local fs. Recalculating hash...`)
// calculate sha512 hash of executable
const hash:string = await hashFile(path.join(__dirname, '../../../release', executable))
console.log(hash)
// calculate file size of executable
const stats = fs.statSync(path.join(__dirname, '../../../release', executable))
const fileSizeInBytes = stats.size
console.log(fileSizeInBytes)
hashes.push({
url: executable,
sha512: hash,
size: fileSizeInBytes
})
if(parsed.path === executable) {
parsed.sha512 = hash
parsed.size = fileSizeInBytes
}
}
}
}
console.log(hashes)
parsed.files = hashes
const newYml = YAML.stringify(parsed)
fs.writeFileSync(path.join(__dirname, '../../../release', file), newYml)
}
let files = await readReleaseFilesFromLocalDirectory()
files = files.filter((file) => file.endsWith('.zip') || file.endsWith('.dmg') || file.endsWith('.exe') || file.endsWith('.AppImage') || file.endsWith('.snap') || file.endsWith('.deb') || file.startsWith('latest'))
console.log(`Found ${files.length} files to upload`)
console.log(files)
if (!release.draft) {
console.log(`Release ${version} is not a draft. Aborting...`)
return
}
// upload files
for (const file of files) {
// check if it is already uploaded
const asset = release.assets.find((a) => a.label === file)
if (asset) {
console.log(`Asset ${file} already uploaded... replacing it`)
// remove it first
await removeAsset(asset)
}
await uploadReleaseAsset(release, file, path.join(__dirname, '../../../release', file))
}
}
main()

@ -57,7 +57,8 @@ import { electronTemplates } from './app/plugins/electron/templatesPlugin'
import { xtermPlugin } from './app/plugins/electron/xtermPlugin'
import { ripgrepPlugin } from './app/plugins/electron/ripgrepPlugin'
import { compilerLoaderPlugin, compilerLoaderPluginDesktop } from './app/plugins/electron/compilerLoaderPlugin'
import { appUpdaterPlugin } from './app/plugins/electron/appUpdaterPlugin'
import {OpenAIGpt} from './app/plugins/openaigpt'
const isElectron = require('is-electron')
@ -377,6 +378,8 @@ class AppComponent {
this.engine.register([xterm])
const ripgrep = new ripgrepPlugin()
this.engine.register([ripgrep])
const appUpdater = new appUpdaterPlugin()
this.engine.register([appUpdater])
}
const compilerloader = isElectron()? new compilerLoaderPluginDesktop(): new compilerLoaderPlugin()
@ -500,7 +503,7 @@ class AppComponent {
await this.appManager.activatePlugin(['solidity-script', 'remix-templates'])
if (isElectron()){
await this.appManager.activatePlugin(['isogit', 'electronconfig', 'electronTemplates', 'xterm', 'ripgrep'])
await this.appManager.activatePlugin(['isogit', 'electronconfig', 'electronTemplates', 'xterm', 'ripgrep', 'appUpdater'])
}
this.appManager.on(

@ -0,0 +1,55 @@
import { ElectronPlugin } from '@remixproject/engine-electron'
const profile = {
displayName: 'appUpdater',
name: 'appUpdater',
description: 'appUpdater',
}
export class appUpdaterPlugin extends ElectronPlugin {
constructor() {
console.log('appUpdaterPlugin')
super(profile)
}
onActivation(): void {
this.on('appUpdater', 'askForUpdate', () => {
console.log('askForUpdate')
const upgradeModal = {
id: 'confirmUpdate',
title: 'An update is available',
message: `A new version of Remix Desktop is available. Do you want to update?`,
modalType: 'modal',
okLabel: 'Yes',
cancelLabel: 'No',
okFn: () => {
this.call('appUpdater', 'download')
},
cancelFn: () => {
},
hideFn: () => null
}
this.call('notification', 'modal', upgradeModal)
})
this.on('appUpdater', 'downloadReady', () => {
console.log('downloadReady')
const upgradeModal = {
id: 'confirmInstall',
title: 'An update is ready to install',
message: `A new version of Remix Desktop is ready to install. Do you want to install it now? This will close Remix Desktop.`,
modalType: 'modal',
okLabel: 'Yes',
cancelLabel: 'No',
okFn: () => {
this.call('appUpdater', 'install')
},
cancelFn: () => {
},
hideFn: () => null
}
this.call('notification', 'modal', upgradeModal)
})
}
}

@ -0,0 +1,30 @@
const fs = require('fs');
exports.default = async function afterbuild(context) {
// do not run when not on macOS or when not on CIRCLECI
if (process.platform !== 'darwin' || !process.env.CIRCLE_BRANCH) {
return;
}
console.log('AFTER BUILD', context);
const artifactPaths = context.artifactPaths;
const newDmgs = artifactPaths.filter((dmg) => dmg.endsWith('.dmg')).map((dmg) => dmg); // Removed unnecessary quotes for consistency
let existingDmgs = [];
try {
// Attempt to read the existing dmgs.json file
const data = fs.readFileSync('dmgs.json', 'utf8');
const parsedData = JSON.parse(data);
existingDmgs = parsedData.dmgs || []; // Ensure existingDmgs is an array
} catch (error) {
// If there's an error reading the file (e.g., file does not exist), proceed with an empty array
console.log('No existing dmgs.json or error reading file, creating new one.');
}
// Combine existing and new dmgs, avoiding duplicates
const combinedDmgs = [...new Set([...existingDmgs, ...newDmgs])];
// Write/overwrite the dmgs.json with the combined list of dmgs
fs.writeFileSync('dmgs.json', JSON.stringify({ dmgs: combinedDmgs }, null, 2));
};

@ -0,0 +1,90 @@
const { notarize } = require('@electron/notarize')
const fs = require('fs')
const { exec } = require('child_process') // Import the exec function
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context // Provided by electron-builder
console.log('NOTARIZING')
if (electronPlatformName !== 'darwin' || !process.env.CIRCLE_BRANCH) {
return
}
const appName = context.packager.appInfo.productFilename
const appPath = `${appOutDir}/${appName}.app`
// Function to promisify the exec command
function execShellCommand(cmd) {
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (error) {
reject(new Error(`Error: ${error.message}`));
return;
}
if (stderr) {
reject(new Error(`Stderr: ${stderr}`));
return;
}
console.log(`stdout: ${stdout}`);
resolve(stdout);
});
});
}
// Function to check if the app is stapled
// Async function to check the stapling status
async function checkStapleStatus() {
try {
console.log(`xcrun stapler validate "${appPath}"`)
await execShellCommand(`xcrun stapler validate "${appPath}"`);
console.log('App is already stapled. No action needed.');
return true
} catch (error) {
console.log(`App is not stapled: ${error.message}`);
return false
}
}
async function runNotarize() {
console.log('NOTARIZING + ', `xcrun stapler staple "${appPath}"`)
console.log({
appBundleId: 'org.ethereum.remix-ide', // Your app's bundle ID
appPath: `${appOutDir}/${appName}.app`, // Path to your .app
appleId: process.env.APPLE_ID, // Your Apple ID
appleIdPassword: process.env.APPLE_ID_PASSWORD, // App-specific password
teamId: process.env.APPLE_TEAM_ID, // Your Apple Developer team ID (optional)
})
try {
const r = await notarize({
appBundleId: 'org.ethereum.remix-ide', // Your app's bundle ID
appPath: `${appOutDir}/${appName}.app`, // Path to your .app
appleId: process.env.APPLE_ID, // Your Apple ID
appleIdPassword: process.env.APPLE_ID_PASSWORD, // App-specific password
teamId: process.env.APPLE_TEAM_ID, // Your Apple Developer team ID (optional)
})
console.log(r)
// Stapling the app
console.log('STAPLING', `xcrun stapler staple "${appPath}"`)
await execShellCommand(`xcrun stapler staple "${appPath}"`)
} catch (error) {
console.error('Error during notarization:', error)
}
}
if(!await checkStapleStatus()){
await runNotarize()
await checkStapleStatus()
}else{
return []
}
}

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>

@ -0,0 +1,39 @@
#!/bin/bash
# Path to the JSON file containing the DMG paths
JSON_FILE="dmgs.json"
# Read the DMGs array from the JSON file
DMG_PATHS=$(jq -r '.dmgs[]' "$JSON_FILE")
echo $DMG_PATHS
xcrun notarytool store-credentials "notarytool-password" \
--apple-id ${APPLE_ID} \
--team-id ${APPLE_TEAM_ID} \
--password ${APPLE_ID_PASSWORD}
# Use jq to parse the DMGs array and read each line
while IFS= read -r DMG_PATH; do
# Remove single quotes from the path if present
DMG_PATH_CLEANED=$(echo $DMG_PATH | tr -d "'")
echo "Submitting $DMG_PATH_CLEANED for notarization..."
# Replace `your-app-specific-args` with the actual arguments for your app
# Ensure your notarytool command and arguments are correct for your application
xcrun notarytool submit "$DMG_PATH_CLEANED" --keychain-profile "notarytool-password" --wait
# Check the command's success
if [ $? -eq 0 ]; then
echo "Successfully submitted $DMG_PATH_CLEANED for notarization."
xcrun stapler staple "$DMG_PATH_CLEANED"
echo "Successfully stapled $DMG_PATH_CLEANED."
spctl -a -t open -vvv --context context:primary-signature "$DMG_PATH_CLEANED"
echo "Successfully checked $DMG_PATH_CLEANED."
else
echo "Failed to submit $DMG_PATH_CLEANED for notarization."
fi
done < <(jq -r '.dmgs[]' "$JSON_FILE")
echo "All DMG submissions completed."

@ -1,6 +1,6 @@
{
"name": "remixdesktop",
"version": "0.0.11-Alpha",
"version": "0.0.26-Alpha",
"main": "build/main.js",
"license": "MIT",
"type": "commonjs",
@ -24,7 +24,7 @@
"scripts": {
"start:dev": "tsc && cp -R node_modules/yarn build/tools/ && cross-env NODE_ENV=development electron --inspect=5858 .",
"start:production": "tsc && && cp -R node_modules/yarn build/tools/ && cross-env NODE_ENV=production electron .",
"dist": "tsc && cp -R node_modules/yarn build/tools/ && electron-builder",
"dist": "tsc && cp -R node_modules/yarn build/tools/ && electron-builder -p never",
"installRipGrepMacOXx64": "rm -rf node_modules/@vscode/ripgrep/bin && npm_config_arch=x64 node node_modules/@vscode/ripgrep/lib/postinstall.js",
"installRipGrepMacOXarm64": "rm -rf node_modules/@vscode/ripgrep/bin && npm_config_arch=arm64 node node_modules/@vscode/ripgrep/lib/postinstall.js",
"postinstall": "electron-builder install-app-deps",
@ -42,14 +42,14 @@
"build:e2e": "tsc -p tsconfig.e2e.json"
},
"devDependencies": {
"@electron/rebuild": "^3.2.13",
"@electron/notarize": "^2.3.0",
"@types/byline": "^4.2.35",
"@types/express": "^4.17.21",
"@types/nightwatch": "^2.3.23",
"cross-env": "^7.0.3",
"deep-equal": "^2.2.3",
"electron": "^25.0.1",
"electron-builder": "^23.6.0",
"electron": "^26.0.0",
"electron-builder": "^24.9.1",
"electron-devtools-installer": "^3.2.0",
"nightwatch": "2.3",
"selenium-standalone": "^9.3.1",
@ -58,16 +58,17 @@
},
"dependencies": {
"@remix-project/remix-url-resolver": "^0.0.65",
"@remixproject/engine": "0.3.41",
"@remixproject/engine-electron": "0.3.41",
"@remixproject/plugin": "0.3.41",
"@remixproject/plugin-api": "^0.3.38",
"@remixproject/plugin-electron": "0.3.41",
"@remixproject/engine": "0.3.43",
"@remixproject/engine-electron": "0.3.43",
"@remixproject/plugin": "0.3.43",
"@remixproject/plugin-api": "^0.3.43",
"@remixproject/plugin-electron": "0.3.43",
"@vscode/ripgrep": "^1.15.6",
"add": "^2.0.6",
"axios": "^1.6.1",
"byline": "^5.0.0",
"chokidar": "^3.5.3",
"electron-updater": "^6.1.8",
"express": "^4.18.2",
"isomorphic-git": "^1.24.2",
"node-pty": "^0.10.1",
@ -77,7 +78,7 @@
"@remix-project/remix-ws-templates": "^1.0.27"
},
"build": {
"productName": "Remix IDE",
"productName": "Remix-Desktop",
"appId": "org.ethereum.remix-ide",
"asar": true,
"generateUpdatesFilesForAllChannels": true,
@ -85,6 +86,8 @@
"files": [
"build/**/*"
],
"afterSign": "aftersign.js",
"afterAllArtifactBuild": "afterbuild.js",
"publish": [
{
"provider": "github",
@ -96,38 +99,37 @@
],
"mac": {
"category": "public.app-category.productivity",
"target": [
{
"target": "dmg",
"arch": [
"x64",
"arm64"
]
}
],
"icon": "assets/icon.png",
"darkModeSupport": true
"darkModeSupport": true,
"hardenedRuntime" : true,
"gatekeeperAssess": false,
"entitlements": "entitlements.mac.plist",
"entitlementsInherit": "entitlements.mac.plist"
},
"dmg": {
"writeUpdateInfo": false
"writeUpdateInfo": true,
"sign": true
},
"nsis": {
"createDesktopShortcut": "always",
"allowToChangeInstallationDirectory": true,
"oneClick": false,
"shortcutName": "Remix IDE",
"shortcutName": "Remix Desktop",
"differentialPackage": false
},
"win": {
"target": [
"nsis"
],
"icon": "assets/icon.png",
"artifactName": "${productName}.${ext}"
"artifactName": "${productName}-Setup-${version}.${ext}",
"icon": "assets/icon.png"
},
"deb": {},
"linux": {
"target": [
"deb"
"deb",
"snap",
"AppImage"
],
"category": "WebBrowser",
"icon": "assets"

@ -9,6 +9,7 @@ import { ConfigPlugin } from './plugins/configPlugin';
import { TemplatesPlugin } from './plugins/templates';
import { RipgrepPlugin } from './plugins/ripgrepPlugin';
import { CompilerLoaderPlugin } from './plugins/compilerLoader';
import { AppUpdaterPlugin } from './plugins/appUpdater';
const engine = new Engine()
const appManager = new PluginManager()
@ -19,6 +20,7 @@ const configPlugin = new ConfigPlugin()
const templatesPlugin = new TemplatesPlugin()
const ripgrepPlugin = new RipgrepPlugin()
const compilerLoaderPlugin = new CompilerLoaderPlugin()
const appUpdaterPlugin = new AppUpdaterPlugin()
engine.register(appManager)
engine.register(fsPlugin)
@ -28,6 +30,7 @@ engine.register(configPlugin)
engine.register(templatesPlugin)
engine.register(ripgrepPlugin)
engine.register(compilerLoaderPlugin)
engine.register(appUpdaterPlugin)
appManager.activatePlugin('electronconfig')
appManager.activatePlugin('fs')

@ -0,0 +1,121 @@
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import { Profile } from "@remixproject/plugin-utils"
import { autoUpdater } from "electron-updater"
import { app } from 'electron';
const profile = {
displayName: 'appUpdater',
name: 'appUpdater',
description: 'appUpdater',
}
export class AppUpdaterPlugin extends ElectronBasePlugin {
clients: AppUpdaterPluginClient[] = []
constructor() {
console.log('AppUpdaterPlugin')
super(profile, clientProfile, AppUpdaterPluginClient)
this.methods = [...super.methods]
autoUpdater.autoDownload = false
autoUpdater.disableDifferentialDownload = true
autoUpdater.on('checking-for-update', () => {
console.log('Checking for update...');
this.sendToLog('Checking for update...')
})
autoUpdater.on('update-available', (info: any) => {
console.log('Update available.', info);
this.sendToLog('Update available.')
for (const client of this.clients) {
client.askForUpdate()
}
})
autoUpdater.on('update-not-available', () => {
console.log('Update not available.');
this.sendToLog('App is already up to date.')
})
autoUpdater.on('error', (err) => {
console.log('Error in auto-updater. ' + err);
this.sendToLog('Cannot find updates...')
})
autoUpdater.on('download-progress', (progressObj) => {
let log_message = "Download speed: " + progressObj.bytesPerSecond;
log_message = log_message + ' - Downloaded ' + progressObj.percent + '%';
log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')';
console.log(log_message);
this.sendToLog(log_message)
})
autoUpdater.on('update-downloaded', (info) => {
console.log('Update downloaded');
this.sendToLog('Update downloaded')
this.sendToLog('processing download... please wait...')
for(const client of this.clients) {
client.downloadReady()
}
})
}
async sendToLog(message: string): Promise<void> {
for (const client of this.clients) {
client.call('terminal', 'log', {
type: 'log',
value: message,
})
}
}
}
const clientProfile: Profile = {
name: 'appUpdater',
displayName: 'appUpdater',
description: 'appUpdater',
methods: ['checkForUpdates', 'download', 'install'],
}
class AppUpdaterPluginClient extends ElectronBasePluginClient {
constructor(webContentsId: number, profile: Profile) {
console.log('AppUpdaterPluginClient')
super(webContentsId, profile)
}
async onActivation(): Promise<void> {
console.log('onActivation', 'appUpdaterPluginClient')
this.onload(async () => {
console.log('onload', 'appUpdaterPluginClient')
this.emit('loaded')
await this.checkForUpdates()
})
}
async askForUpdate(): Promise<void> {
this.emit('askForUpdate')
}
async downloadReady(): Promise<void> {
// we do a wait here to make sure that the download is done, it's a bug in electron-updater
setTimeout(() => {
this.emit('downloadReady')
}
, 10000)
}
async download(): Promise<void> {
autoUpdater.downloadUpdate()
}
async install(): Promise<void> {
autoUpdater.quitAndInstall()
}
async checkForUpdates(): Promise<void> {
console.log('checkForUpdates')
this.call('terminal', 'log', {
type: 'log',
value: 'Remix Desktop version: ' + autoUpdater.currentVersion,
})
autoUpdater.checkForUpdates()
}
}

@ -66,9 +66,7 @@ class CompilerLoaderPluginClient extends ElectronBasePluginClient {
}
async onActivation(): Promise<void> {
console.log('onActivation', 'CompilerLoaderPluginClient')
this.onload(() => {
console.log('onload', 'CompilerLoaderPluginClient')
this.emit('loaded')
})
}

@ -6,7 +6,7 @@ console.log('preload.ts', new Date().toLocaleTimeString())
/* preload script needs statically defined API for each plugin */
const exposedPLugins = ['fs', 'git', 'xterm', 'isogit', 'electronconfig', 'electronTemplates', 'ripgrep', 'compilerloader']
const exposedPLugins = ['fs', 'git', 'xterm', 'isogit', 'electronconfig', 'electronTemplates', 'ripgrep', 'compilerloader', 'appUpdater']
let webContentsId: number | undefined

File diff suppressed because it is too large Load Diff

@ -332,7 +332,6 @@
"css-minimizer-webpack-plugin": "^4.2.2",
"csslint": "^1.0.2",
"dotenv": "^8.2.0",
"electron": "^24.4.0",
"eslint": "^8.26.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "2.26.0",

@ -6449,11 +6449,6 @@
dependencies:
undici-types "~5.26.4"
"@types/node@^18.11.18":
version "18.16.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.16.tgz#3b64862856c7874ccf7439e6bab872d245c86d8e"
integrity sha512-NpaM49IGQQAUlBhHMF82QH80J08os4ZmyF9MkpCzWAGuOHqE4gTEbhzd7L3l5LmWuZ6E0OiC1FweQ4tsiW35+g==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@ -12915,15 +12910,6 @@ electron-to-chromium@^1.4.251:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.264.tgz#2f68a062c38b7a04bf57f3e6954b868672fbdcd3"
integrity sha512-AZ6ZRkucHOQT8wke50MktxtmcWZr67kE17X/nAXFf62NIdMdgY6xfsaJD5Szoy84lnkuPWH+4tTNE3s2+bPCiw==
electron@^24.4.0:
version "24.4.0"
resolved "https://registry.yarnpkg.com/electron/-/electron-24.4.0.tgz#48d05561dab6a5835ec1a3a96852797f69824eea"
integrity sha512-A9YzLHRUA+HfYVf2daNv0jPXNCWShgcgcTaGntrZRGynQLEhDTbti9Lfmq2tjRKoEgXZ7qj+aJFw+tJZsT/Cfw==
dependencies:
"@electron/get" "^2.0.0"
"@types/node" "^18.11.18"
extract-zip "^2.0.1"
elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
@ -14233,7 +14219,7 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-zip@2.0.1, extract-zip@^2.0.0, extract-zip@^2.0.1:
extract-zip@2.0.1, extract-zip@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==

Loading…
Cancel
Save