conflict resolve

pull/5370/head
STetsing 5 months ago
parent 066c425452
commit 07e38c4b20
  1. 136
      apps/remixdesktop/aftersign.js
  2. 3
      apps/remixdesktop/insiders.json
  3. 2
      apps/remixdesktop/notarizedmg.sh
  4. 47
      apps/remixdesktop/package.json
  5. 9
      apps/remixdesktop/run_ci_test.sh
  6. 1
      apps/remixdesktop/src/lib/remixd.ts
  7. 2
      apps/remixdesktop/src/lib/utils.ts
  8. 4
      apps/remixdesktop/src/plugins/appUpdater.ts
  9. 2
      apps/remixdesktop/src/plugins/compilerLoader.ts
  10. 34
      apps/remixdesktop/src/plugins/fsPlugin.ts
  11. 283
      apps/remixdesktop/src/plugins/isoGitPlugin.ts
  12. 175
      apps/remixdesktop/src/plugins/slitherPlugin.ts
  13. 81
      apps/remixdesktop/src/plugins/xtermPlugin.ts
  14. 84
      apps/remixdesktop/src/tools/git.ts
  15. 13
      apps/remixdesktop/test/nighwatch.app.ts
  16. 5
      apps/remixdesktop/test/tests/app/gist.test.ts
  17. 6
      apps/remixdesktop/test/tests/app/offline.test.ts

@ -1,66 +1,69 @@
const { notarize } = require('@electron/notarize') const { notarize } = require('@electron/notarize')
const fs = require('fs') const fs = require('fs')
const { exec } = require('child_process') // Import the exec function 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') // read the environment variables from process
if (electronPlatformName !== 'darwin' || !process.env.CIRCLE_BRANCH) { console.log(process.env.DO_NOT_NOTARIZE)
return
}
const appName = context.packager.appInfo.productFilename if (process.env.DO_NOT_NOTARIZE) {
const appPath = `${appOutDir}/${appName}.app` console.log('NOTARIZING DISABLED')
exports.default = async function notarizing(context) {
// Function to promisify the exec command return []
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);
});
});
} }
} else {
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context // Provided by electron-builder
// Function to check if the app is stapled console.log('NOTARIZING')
// Async function to check the stapling status
async function checkStapleStatus() { if (electronPlatformName !== 'darwin' || !process.env.CIRCLE_BRANCH) {
try { return
console.log(`xcrun stapler validate "${appPath}"`) }
await execShellCommand(`xcrun stapler validate "${appPath}"`);
console.log('App is already stapled. No action needed.'); const appName = context.packager.appInfo.productFilename
return true const appPath = `${appOutDir}/${appName}.app`
} catch (error) {
console.log(`App is not stapled: ${error.message}`); // Function to promisify the exec command
return false 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}"`) async function runNotarize() {
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 { console.log('NOTARIZING + ', `xcrun stapler staple "${appPath}"`)
const r = await notarize({ console.log({
appBundleId: 'org.ethereum.remix-ide', // Your app's bundle ID appBundleId: 'org.ethereum.remix-ide', // Your app's bundle ID
appPath: `${appOutDir}/${appName}.app`, // Path to your .app appPath: `${appOutDir}/${appName}.app`, // Path to your .app
appleId: process.env.APPLE_ID, // Your Apple ID appleId: process.env.APPLE_ID, // Your Apple ID
@ -68,23 +71,34 @@ exports.default = async function notarizing(context) {
teamId: process.env.APPLE_TEAM_ID, // Your Apple Developer team ID (optional) teamId: process.env.APPLE_TEAM_ID, // Your Apple Developer team ID (optional)
}) })
console.log(r) 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)
})
// Stapling the app console.log(r)
console.log('STAPLING', `xcrun stapler staple "${appPath}"`)
await execShellCommand(`xcrun stapler staple "${appPath}"`) // Stapling the app
console.log('STAPLING', `xcrun stapler staple "${appPath}"`)
} catch (error) { await execShellCommand(`xcrun stapler staple "${appPath}"`)
console.error('Error during notarization:', error)
}
} } catch (error) {
console.error('Error during notarization:', error)
throw new Error('Error during notarization', error)
}
if(!await checkStapleStatus()){ }
await runNotarize()
await checkStapleStatus() if (!await checkStapleStatus()) {
}else{ await runNotarize()
return [] await checkStapleStatus()
} else {
return []
}
} }
} }

@ -5,7 +5,8 @@
"generateUpdatesFilesForAllChannels": false, "generateUpdatesFilesForAllChannels": false,
"icon": "assets", "icon": "assets",
"files": [ "files": [
"build/**/*" "build/**/*",
"node_modules/node-pty/**/*"
], ],
"afterSign": "aftersign.js", "afterSign": "aftersign.js",
"afterAllArtifactBuild": "afterbuild.js", "afterAllArtifactBuild": "afterbuild.js",

@ -11,7 +11,7 @@ echo $DMG_PATHS
xcrun notarytool store-credentials "notarytool-password" \ xcrun notarytool store-credentials "notarytool-password" \
--apple-id ${APPLE_ID} \ --apple-id ${APPLE_ID} \
--team-id ${APPLE_TEAM_ID} \ --team-id ${APPLE_TEAM_ID} \
--password ${APPLE_ID_PASSWORD} --password ${APPLE_ID_PASSWORD} || exit 1
# Use jq to parse the DMGs array and read each line # Use jq to parse the DMGs array and read each line
while IFS= read -r DMG_PATH; do while IFS= read -r DMG_PATH; do

@ -1,7 +1,7 @@
{ {
"name": "remixdesktop", "name": "remixdesktop",
"version": "1.0.5-insiders", "version": "1.0.8-insiders",
"main": "build/apps/remixdesktop/src/main.js", "main": "build/main.js",
"license": "MIT", "license": "MIT",
"type": "commonjs", "type": "commonjs",
"description": "Remix IDE Desktop", "description": "Remix IDE Desktop",
@ -13,6 +13,9 @@
"name": "Remix Team", "name": "Remix Team",
"email": "remix@ethereum.org" "email": "remix@ethereum.org"
}, },
"engines": {
"node": "20.2.0"
},
"bugs": { "bugs": {
"url": "https://github.com/ethereum/remix-project/issues" "url": "https://github.com/ethereum/remix-project/issues"
}, },
@ -22,15 +25,17 @@
"category": "public.app-category.productivity" "category": "public.app-category.productivity"
}, },
"scripts": { "scripts": {
"start:dev": "tsc && cp -R node_modules/yarn build/tools/ && cross-env NODE_ENV=development electron --inspect=5858 .", "start:dev": "yarn webpack --config webpack.config.js && electron --inspect=5858 .",
"start:production": "tsc && && cp -R node_modules/yarn build/tools/ && cross-env NODE_ENV=production electron .", "start:production": "cross-env NODE_ENV=production yarn webpack --config webpack.config.js && electron .",
"dist": "tsc && cp -R node_modules/yarn build/tools/ && electron-builder -p never", "dist": "cross-env NODE_ENV=production yarn webpack --config webpack.config.js && electron-builder -p never",
"tscbuild": "tsc && cp -R node_modules/yarn build/tools/ && electron-builder -p never",
"esbuild": "cross-env NODE_ENV=production node esbuild.js && electron-builder -p never",
"installRipGrepMacOXx64": "rm -rf node_modules/@vscode/ripgrep/bin && npm_config_arch=x64 node node_modules/@vscode/ripgrep/lib/postinstall.js", "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", "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", "postinstall": "electron-builder install-app-deps",
"test": "yarn run build:e2e && nightwatch --config build-e2e/remixdesktop/test/nighwatch.app.js", "test": "yarn run build:e2e && nightwatch --config build-e2e/remixdesktop/test/nighwatch.app.js",
"test:isogit": "yarn run test --useIsoGit --test build-e2e/remixdesktop/test/tests/app/git.test.js", "test:isogit": "yarn run test --use-isogit",
"test:offline": "yarn run test --useOffline --test build-e2e/remixdesktop/test/tests/app/offline.test.js", "test:offline": "yarn run test --use-offline --test build-e2e/remixdesktop/test/tests/app/offline.test.js",
"build:e2e": "tsc -p tsconfig.e2e.json" "build:e2e": "tsc -p tsconfig.e2e.json"
}, },
"devDependencies": { "devDependencies": {
@ -38,18 +43,24 @@
"@types/byline": "^4.2.35", "@types/byline": "^4.2.35",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/nightwatch": "^2.3.23", "@types/nightwatch": "^2.3.23",
"@xenova/transformers": "^2.17.2",
"chromedriver": "116", "chromedriver": "116",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"deep-equal": "^2.2.3", "deep-equal": "^2.2.3",
"electron": "^26.0.0", "electron": "^26.0.0",
"electron-builder": "24.9.1", "electron-builder": "24.9.1",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"esbuild": "^0.23.1",
"nightwatch": "2.3", "nightwatch": "2.3",
"node-fetch": "2.6.1", "node-loader": "^2.0.0",
"onnxruntime-web": "^1.18.0",
"selenium-standalone": "^9.3.1", "selenium-standalone": "^9.3.1",
"tree-kill": "^1.2.2",
"ts-loader": "^9.5.1",
"tsconfig-paths-webpack-plugin": "^4.1.0",
"typescript": "^5.1.3", "typescript": "^5.1.3",
"webpack": "^5.92.1",
"webpack-cli": "^5.1.4",
"webpack-merge": "^6.0.1",
"webpack-node-externals": "^3.0.0",
"yarn": "^1.22.21" "yarn": "^1.22.21"
}, },
"dependencies": { "dependencies": {
@ -61,25 +72,17 @@
"@remixproject/plugin-electron": "0.3.43", "@remixproject/plugin-electron": "0.3.43",
"@vscode/ripgrep": "^1.15.6", "@vscode/ripgrep": "^1.15.6",
"add": "^2.0.6", "add": "^2.0.6",
"axios": "^1.6.8", "axios": "^1.7.4",
"byline": "^5.0.0", "byline": "^5.0.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"create-require": "^1.1.1",
"electron-updater": "^6.1.8", "electron-updater": "^6.1.8",
"esm": "^3.2.25", "express": "^4.20.0",
"express": "^4.19.2",
"i": "^0.3.7",
"isomorphic-git": "^1.24.2", "isomorphic-git": "^1.24.2",
"matomo-tracker": "^2.2.4", "matomo-tracker": "^2.2.4",
"node-pty": "^0.10.1", "node-pty": "^1.0.0",
"npm": "^10.8.1", "octokit": "^3.1.2",
"semver": "^7.5.4" "semver": "^7.5.4"
}, },
"imports": {
"#transformers": {
"require": "@xenova/transformers"
}
},
"optionalDependencies": { "optionalDependencies": {
"@remix-project/remix-ws-templates": "^1.0.27" "@remix-project/remix-ws-templates": "^1.0.27"
} }

@ -4,13 +4,12 @@ TEST_EXITCODE=0
yarn run build:e2e && node ./splice_tests.js yarn run build:e2e && node ./splice_tests.js
TESTFILES=$(node ./splice_tests.js | circleci tests split --split-by=timings) TESTFILES=$(node ./splice_tests.js | circleci tests split --split-by=timings)
for TESTFILE in $TESTFILES; do for TESTFILE in $TESTFILES; do
yarn run test --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || TEST_EXITCODE=1 yarn run test --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || yarn run test --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || TEST_EXITCODE=1
done done
if [ "$CIRCLE_NODE_INDEX" -eq 0 ]; then
yarn test:isogit if [ "$CIRCLE_NODE_INDEX" -eq 1 ]; then
elif [ "$CIRCLE_NODE_INDEX" -eq 1 ]; then yarn test:offline || TEST_EXITCODE=1
yarn test:offline
fi fi
echo "$TEST_EXITCODE" echo "$TEST_EXITCODE"

@ -28,6 +28,7 @@ export class ElectronBasePluginRemixdClient extends ElectronBasePluginClient {
this.onload(async () => { this.onload(async () => {
this.on('fs' as any, 'workingDirChanged', async (path: string) => { this.on('fs' as any, 'workingDirChanged', async (path: string) => {
console.log('workingDirChanged base remixd', path)
this.currentSharedFolder = path this.currentSharedFolder = path
}) })
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir') this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir')

@ -19,6 +19,6 @@ function normalizePath (path) {
return path return path
} }
export { absolutePath } export { absolutePath, normalizePath }

@ -14,7 +14,6 @@ const profile = {
export class AppUpdaterPlugin extends ElectronBasePlugin { export class AppUpdaterPlugin extends ElectronBasePlugin {
clients: AppUpdaterPluginClient[] = [] clients: AppUpdaterPluginClient[] = []
constructor() { constructor() {
console.log('AppUpdaterPlugin')
super(profile, clientProfile, AppUpdaterPluginClient) super(profile, clientProfile, AppUpdaterPluginClient)
this.methods = [...super.methods] this.methods = [...super.methods]
@ -78,14 +77,11 @@ const clientProfile: Profile = {
class AppUpdaterPluginClient extends ElectronBasePluginClient { class AppUpdaterPluginClient extends ElectronBasePluginClient {
constructor(webContentsId: number, profile: Profile) { constructor(webContentsId: number, profile: Profile) {
console.log('AppUpdaterPluginClient')
super(webContentsId, profile) super(webContentsId, profile)
} }
async onActivation(): Promise<void> { async onActivation(): Promise<void> {
console.log('onActivation', 'appUpdaterPluginClient')
this.onload(async () => { this.onload(async () => {
console.log('onload', 'appUpdaterPluginClient')
this.emit('loaded') this.emit('loaded')
if(isE2E) return if(isE2E) return
await this.checkForUpdates() await this.checkForUpdates()

@ -12,7 +12,7 @@ export const baseURLWasm = 'https://binaries.soliditylang.org/wasm'
const appExpress = express() const appExpress = express()
// used in e2e tests // used in e2e tests
const useOffline = process.argv.includes('--useOffline'); const useOffline = process.argv.includes('--use-offline');
console.log('cacheDir', cacheDir) console.log('cacheDir', cacheDir)
appExpress.use(express.static(cacheDir)) appExpress.use(express.static(cacheDir))

@ -32,6 +32,14 @@ const getBaseName = (pathName: string): string => {
return path.basename(pathName) return path.basename(pathName)
} }
function onlyUnique(value: recentFolder, index: number, self: recentFolder[]) {
return self.findIndex((rc, index) => rc.path === value.path) === index
}
const deplucateFolderList = (list: recentFolder[]): recentFolder[] => {
return list.filter(onlyUnique)
}
export class FSPlugin extends ElectronBasePlugin { export class FSPlugin extends ElectronBasePlugin {
clients: FSPluginClient[] = [] clients: FSPluginClient[] = []
constructor() { constructor() {
@ -40,28 +48,28 @@ export class FSPlugin extends ElectronBasePlugin {
} }
async onActivation(): Promise<void> { async onActivation(): Promise<void> {
const config = await this.call('electronconfig' as any, 'readConfig') const config = await this.call('electronconfig', 'readConfig')
const openedFolders = (config && config.openedFolders) || [] const openedFolders = (config && config.openedFolders) || []
const recentFolders = (config && config.recentFolders) || [] const recentFolders: recentFolder[] = (config && config.recentFolders) || []
this.call('electronconfig', 'writeConfig', {...config, this.call('electronconfig', 'writeConfig', {...config,
recentFolders: recentFolders, recentFolders: deplucateFolderList(recentFolders),
openedFolders: openedFolders}) openedFolders: openedFolders})
const foldersToDelete: string[] = [] const foldersToDelete: string[] = []
if (openedFolders && openedFolders.length) { if (recentFolders && recentFolders.length) {
for (const folder of openedFolders) { for (const folder of recentFolders) {
try { try {
const stat = await fs.stat(folder) const stat = await fs.stat(folder.path);
if (stat.isDirectory()) { if (stat.isDirectory()) {
// do nothing // do nothing
} }
} catch (e) { } catch (e) {
console.log('error opening folder', folder, e) console.log('error opening folder', folder, e)
foldersToDelete.push(folder) foldersToDelete.push(folder.path)
} }
} }
if (foldersToDelete.length) { if (foldersToDelete.length) {
const newFolders = openedFolders.filter((f: string) => !foldersToDelete.includes(f)) const newFolders = recentFolders.filter((f: recentFolder) => !foldersToDelete.includes(f.path))
this.call('electronconfig', 'writeConfig', {recentFolders: newFolders}) this.call('electronconfig', 'writeConfig', {recentFolders: deplucateFolderList(newFolders)})
} }
} }
createWindow() createWindow()
@ -85,6 +93,13 @@ export class FSPlugin extends ElectronBasePlugin {
client.openFolder(path) client.openFolder(path)
} }
} }
openFolderInSameWindow(webContentsId: any, path?: string): void {
const client = this.clients.find((c) => c.webContentsId === webContentsId)
if (client) {
client.openFolderInSameWindow(path)
}
}
} }
const clientProfile: Profile = { const clientProfile: Profile = {
@ -346,6 +361,7 @@ class FSPluginClient extends ElectronBasePluginClient {
path, path,
timestamp, timestamp,
}) })
config.recentFolders = deplucateFolderList(config.recentFolders)
writeConfig(config) writeConfig(config)
} }

@ -1,12 +1,11 @@
import { PluginClient } from "@remixproject/plugin"; import {Profile} from '@remixproject/plugin-utils'
import { Profile } from "@remixproject/plugin-utils"; import {ElectronBasePlugin, ElectronBasePluginClient} from '@remixproject/plugin-electron'
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import fs from 'fs/promises' import fs from 'fs/promises'
import git from 'isomorphic-git' import git from 'isomorphic-git'
import { dialog } from "electron";
import http from 'isomorphic-git/http/web' import http from 'isomorphic-git/http/web'
import { gitProxy } from "../tools/git"; import {gitProxy} from '../tools/git'
import { remote } from "../types"; import {isoGit} from '@remix-git'
import {branchDifference, branchInputType, checkoutInputType, cloneInputType, commitChange, commitInputType, compareBranchesInput, currentBranchInput, fetchInputType, initInputType, logInputType, pullInputType, pushInputType, remote, resolveRefInput, statusInput} from '@remix-api'
const profile: Profile = { const profile: Profile = {
name: 'isogit', name: 'isogit',
@ -14,7 +13,7 @@ const profile: Profile = {
description: 'isogit plugin', description: 'isogit plugin',
} }
// used in e2e tests // used in e2e tests
const useIsoGit = process.argv.includes('--useIsoGit'); const useIsoGit = process.argv.includes('--use-isogit')
export class IsoGitPlugin extends ElectronBasePlugin { export class IsoGitPlugin extends ElectronBasePlugin {
clients: IsoGitPluginClient[] = [] clients: IsoGitPluginClient[] = []
constructor() { constructor() {
@ -22,33 +21,18 @@ export class IsoGitPlugin extends ElectronBasePlugin {
} }
startClone(webContentsId: any): void { startClone(webContentsId: any): void {
const client = this.clients.find(c => c.webContentsId === webContentsId) const client = this.clients.find((c) => c.webContentsId === webContentsId)
if (client) { if (client) {
client.startClone() client.startClone()
} }
} }
} }
const parseInput = (input: any) => {
return {
corsProxy: 'https://corsproxy.remixproject.org/',
http,
onAuth: (url: any) => {
url
const auth = {
username: input.token,
password: ''
}
return auth
}
}
}
const clientProfile: Profile = { const clientProfile: Profile = {
name: 'isogit', name: 'isogit',
displayName: 'isogit', displayName: 'isogit',
description: 'isogit plugin', description: 'isogit plugin',
methods: ['init', 'localStorageUsed', 'version', 'addremote', 'delremote', 'remotes', 'fetch', 'clone', 'export', 'import', 'status', 'log', 'commit', 'add', 'remove', 'reset', 'rm', 'lsfiles', 'readblob', 'resolveref', 'branches', 'branch', 'checkout', 'currentbranch', 'push', 'pin', 'pull', 'pinList', 'unPin', 'setIpfsConfig', 'zip', 'setItem', 'getItem', 'openFolder'] methods: ['init', 'localStorageUsed', 'version', 'addremote', 'delremote', 'remotes', 'fetch', 'clone', 'export', 'import', 'status', 'log', 'commit', 'add', 'remove', 'rm', 'readblob', 'resolveref', 'branches', 'branch', 'checkout', 'currentbranch', 'push', 'pin', 'pull', 'pinList', 'unPin', 'setIpfsConfig', 'zip', 'setItem', 'getItem', 'openFolder', 'getCommitChanges', 'compareBranches', 'startClone', 'updateSubmodules'],
} }
class IsoGitPluginClient extends ElectronBasePluginClient { class IsoGitPluginClient extends ElectronBasePluginClient {
@ -59,15 +43,15 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
this.onload(async () => { this.onload(async () => {
this.on('fs' as any, 'workingDirChanged', async (path: string) => { this.on('fs' as any, 'workingDirChanged', async (path: string) => {
this.workingDir = path this.workingDir = path
this.gitIsInstalled = await gitProxy.version() ? true : false this.gitIsInstalled = (await gitProxy.version()) && !useIsoGit ? true : false
}) })
this.workingDir = await this.call('fs' as any, 'getWorkingDir') this.workingDir = await this.call('fs' as any, 'getWorkingDir')
this.gitIsInstalled = await gitProxy.version() && !useIsoGit ? true : false this.gitIsInstalled = (await gitProxy.version()) && !useIsoGit ? true : false
}) })
} }
async version() { async version() {
return gitProxy.version() return this.gitIsInstalled ? gitProxy.version() : 'built-in'
} }
async getGitConfig() { async getGitConfig() {
@ -77,13 +61,12 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
} }
} }
async status(cmd: any) { async status(cmd: statusInput) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
if (this.workingDir === '') { if (this.workingDir === '') {
return [] return []
} }
@ -94,30 +77,25 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
} }
const status = await git.statusMatrix({ const status = await git.statusMatrix({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
//console.log('STATUS', status, await this.getGitConfig())
return status return status
} }
async log(cmd: any) { async log(cmd: logInputType) {
/* we will use isomorphic git for now const token = await this.call('config' as any, 'getAppParameter', 'settings/gist-access-token')
if(this.gitIsInstalled){
const log = await gitProxy.log(this.workingDir, cmd.ref)
console.log('LOG', log)
return log
}
*/
if (this.workingDir === '') { if (this.workingDir === '') {
return [] return []
} }
const log = await git.log({ const log = await git.log({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
depth: cmd.depth || 10,
}) })
return log return log
@ -129,62 +107,45 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
} }
const add = await git.add({ const add = await git.add({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
return add return add
} }
async rm(cmd: any) { async rm(cmd: any) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
const rm = await git.remove({ const rm = await git.remove({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
return rm return rm
} }
async reset(cmd: any) { async commit(cmd: commitInputType) {
if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory')
}
const reset = await git.resetIndex({
...await this.getGitConfig(),
...cmd
})
return reset
}
async commit(cmd: any) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
if (this.gitIsInstalled) { if (this.gitIsInstalled) {
const status = await gitProxy.commit(this.workingDir, cmd.message) const status = await gitProxy.commit(this.workingDir, cmd)
return status return status
} }
const commit = await git.commit({ const commit = await git.commit({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
return commit return commit
} }
async init(input: any) { async init(input: initInputType) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
@ -193,158 +154,119 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return status return status
} }
await git.init({ await git.init({
...await this.getGitConfig(), ...(await this.getGitConfig()),
defaultBranch: (input && input.branch) || 'main' defaultBranch: (input && input.defaultBranch) || 'main',
}) })
} }
async branch(cmd: any) { async branch(cmd: branchInputType) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
return null return null
} }
const branch = await git.branch({ const branch = await git.branch({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
return branch return branch
} }
async lsfiles(cmd: any) { async resolveref(cmd: resolveRefInput) {
if (!this.workingDir || this.workingDir === '') {
return []
}
const lsfiles = await git.listFiles({
...await this.getGitConfig(),
...cmd
})
return lsfiles
}
async resolveref(cmd: any) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
return null return null
} }
const resolveref = await git.resolveRef({ const resolveref = await git.resolveRef({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
return resolveref return resolveref
} }
async readblob(cmd: any) { async readblob(cmd: any) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
const readblob = await git.readBlob({ const readblob = await git.readBlob({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd ...cmd,
}) })
return readblob return readblob
} }
async checkout(cmd: any) { async checkout(cmd: checkoutInputType) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
if (this.gitIsInstalled) {
const checkout = await git.checkout({ return await gitProxy.checkout(this.workingDir, cmd)
...await this.getGitConfig(), } else {
...cmd const checkout = await git.checkout({
}) ...(await this.getGitConfig()),
...cmd,
return checkout })
return checkout
}
} }
async push(cmd: any) { async push(input: pushInputType) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
if (this.gitIsInstalled) { if (this.gitIsInstalled) {
await gitProxy.push(this.workingDir, cmd.remote, cmd.ref, cmd.remoteRef, cmd.force) return await gitProxy.push(this.workingDir, input)
} else { } else {
const push = await isoGit.push(input, await this.getGitConfig(), this)
const push = await git.push({
...await this.getGitConfig(),
...cmd,
...parseInput(cmd.input)
})
return push return push
} }
} }
async pull(cmd: any) { async pull(input: pullInputType) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
if (this.gitIsInstalled) { if (this.gitIsInstalled) {
await gitProxy.pull(this.workingDir, cmd.remote, cmd.ref, cmd.remoteRef) return await gitProxy.pull(this.workingDir, input)
} else { } else {
const pull = await isoGit.pull(input, await this.getGitConfig(), this)
const pull = await git.pull({
...await this.getGitConfig(),
...cmd,
...parseInput(cmd.input)
})
return pull return pull
} }
} }
async fetch(cmd: any) { async fetch(input: fetchInputType) {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
throw new Error('No working directory') throw new Error('No working directory')
} }
if (this.gitIsInstalled) { if (this.gitIsInstalled) {
await gitProxy.fetch(this.workingDir, cmd.remote, cmd.remoteRef) await gitProxy.fetch(this.workingDir, input)
} else { } else {
const fetch = await isoGit.fetch(input, await this.getGitConfig(), this)
const fetch = await git.fetch({
...await this.getGitConfig(),
...cmd,
...parseInput(cmd.input)
})
return fetch return fetch
} }
} }
async clone(cmd: any) { async clone(cmd: cloneInputType) {
if (this.gitIsInstalled) { if (this.gitIsInstalled) {
try { try {
await gitProxy.clone(cmd.url, cmd.dir) this.call('terminal' as any, 'log', 'Cloning using git... please wait.')
await gitProxy.clone(cmd)
} catch (e) { } catch (e) {
throw e throw e
} }
} else { } else {
try { try {
this.call('terminal' as any, 'log', 'Cloning using builtin git... please wait.') this.call('terminal' as any, 'log', 'Cloning using builtin git... please wait.')
const clone = await git.clone({ const clone = await isoGit.clone(cmd, await this.getGitConfig(), this)
...await this.getGitConfig(),
...cmd,
...parseInput(cmd.input),
dir: cmd.dir || this.workingDir
})
return clone return clone
} catch (e) { } catch (e) {
console.log('CLONE ERROR', e) console.log('CLONE ERROR', e)
@ -353,85 +275,74 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
} }
} }
async addremote(cmd: any) { async addremote(input: remote) {
const addremote = await git.addRemote({ const addremote = await git.addRemote({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd url: input.url,
remote: input.name,
}) })
return addremote return addremote
} }
async delremote(cmd: any) { async delremote(input: remote) {
const delremote = await git.deleteRemote({ const delremote = await git.deleteRemote({
...await this.getGitConfig(), ...(await this.getGitConfig()),
...cmd remote: input.name,
}) })
return delremote return delremote
} }
async remotes() {
remotes = async () => {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
return [] return []
} }
let remotes: remote[] = [] const defaultConfig = await this.getGitConfig()
remotes = (await git.listRemotes({ ...await this.getGitConfig() })).map((remote) => { return { name: remote.remote, url: remote.url } } return await isoGit.remotes(defaultConfig)
)
return remotes
} }
async currentbranch() { async currentbranch(input: currentBranchInput) {
if (!this.workingDir || this.workingDir === '') {
return ''
}
try {
const defaultConfig = await this.getGitConfig()
const name = await git.currentBranch(defaultConfig)
return name if (!this.workingDir || this.workingDir === '') {
} catch (e) {
return '' return ''
} }
const defaultConfig = await this.getGitConfig()
return await isoGit.currentbranch(input, defaultConfig)
} }
async branches(config: any) {
async branches() {
if (!this.workingDir || this.workingDir === '') { if (!this.workingDir || this.workingDir === '') {
return [] return []
} }
try {
let cmd: any = { ...await this.getGitConfig() }
const remotes = await this.remotes()
let branches = []
branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } })
for (const remote of remotes) {
cmd = {
...cmd,
remote: remote.name
}
const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote.name, name: branch } })
branches = [...branches, ...remotebranches]
}
return branches const defaultConfig = await this.getGitConfig()
} catch (e) { return await isoGit.branches(defaultConfig)
return []
}
} }
async startClone() { async startClone() {
this.call('filePanel' as any, 'clone') this.call('filePanel' as any, 'clone')
} }
} async getCommitChanges(commitHash1: string, commitHash2: string): Promise<commitChange[]> {
return await isoGit.getCommitChanges(commitHash1, commitHash2, await this.getGitConfig())
}
async compareBranches({branch, remote}: compareBranchesInput): Promise<branchDifference> {
return await isoGit.compareBranches({branch, remote}, await this.getGitConfig())
}
async updateSubmodules(input) {
if (this.gitIsInstalled) {
try {
return await gitProxy.updateSubmodules(this.workingDir)
} catch (e) {
throw e
}
} else {
this.call('terminal', 'log', {type: 'error', value: 'Please install git into your OS to use this functionality...'})
}
}
}

@ -2,7 +2,22 @@ import { Profile } from "@remixproject/plugin-utils";
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import { ElectronBasePluginRemixdClient } from "../lib/remixd" import { ElectronBasePluginRemixdClient } from "../lib/remixd"
import { SlitherClientMixin } from "../lib/slither"; import * as utils from '../lib/utils'
import { existsSync, readdirSync, readFileSync, unlinkSync } from "fs-extra";
export interface OutputStandard {
description: string
title: string
confidence: string
severity: string
sourceMap: any
category?: string
reference?: string
example?: any
[key: string]: any
}
const { spawn, execSync } = require('child_process') // eslint-disable-line
const profile: Profile = { const profile: Profile = {
name: 'slither', name: 'slither',
displayName: 'electron slither', displayName: 'electron slither',
@ -10,9 +25,9 @@ const profile: Profile = {
} }
export class SlitherPlugin extends ElectronBasePlugin { export class SlitherPlugin extends ElectronBasePlugin {
clients: any [] clients: any[]
constructor() { constructor() {
super(profile, clientProfile, SlitherClientMixin(SlitherPluginClient)) super(profile, clientProfile, SlitherPluginClient)
this.methods = [...super.methods] this.methods = [...super.methods]
} }
} }
@ -24,10 +39,158 @@ const clientProfile: Profile = {
methods: ['analyse'] methods: ['analyse']
} }
class SlitherPluginClient extends ElectronBasePluginRemixdClient { class SlitherPluginClient extends ElectronBasePluginRemixdClient {
constructor(webContentsId: number, profile: Profile) {
super(webContentsId, profile); mapNpmDepsDir(list) {
const remixNpmDepsPath = utils.absolutePath('.deps/npm', this.currentSharedFolder)
const localNpmDepsPath = utils.absolutePath('node_modules', this.currentSharedFolder)
const npmDepsExists = existsSync(remixNpmDepsPath)
const nodeModulesExists = existsSync(localNpmDepsPath)
let isLocalDep = false
let isRemixDep = false
let allowPathString = ''
let remapString = ''
for (const e of list) {
const importPath = e.replace(/import ['"]/g, '').trim()
const packageName = importPath.split('/')[0]
if (nodeModulesExists && readdirSync(localNpmDepsPath).includes(packageName)) {
isLocalDep = true
remapString += `${packageName}=./node_modules/${packageName} `
} else if (npmDepsExists && readdirSync(remixNpmDepsPath).includes(packageName)) {
isRemixDep = true
remapString += `${packageName}=./.deps/npm/${packageName} `
}
}
if (isLocalDep) allowPathString += './node_modules,'
if (isRemixDep) allowPathString += './.deps/npm,'
return { remapString, allowPathString }
}
transform(detectors: Record<string, any>[]): OutputStandard[] {
const standardReport: OutputStandard[] = []
for (const e of detectors) {
const obj = {} as OutputStandard
obj.description = e.description
obj.title = e.check
obj.confidence = e.confidence
obj.severity = e.impact
obj.sourceMap = e.elements.map((element) => {
delete element.source_mapping.filename_used
delete element.source_mapping.filename_absolute
return element
})
standardReport.push(obj)
}
return standardReport
}
analyse(filePath: string, compilerConfig: Record<string, any>) {
return new Promise((resolve, reject) => {
const options = { cwd: this.currentSharedFolder, shell: true }
const { currentVersion, optimize, evmVersion } = compilerConfig
if (currentVersion && currentVersion.includes('+commit')) {
// Get compiler version with commit id e.g: 0.8.2+commit.661d110
const versionString: string = currentVersion.substring(0, currentVersion.indexOf('+commit') + 16)
this.log(`[Slither Analysis]: Compiler version is ${versionString}`)
let solcOutput: Buffer
// Check solc current installed version
try {
solcOutput = execSync('solc --version', options)
} catch (err) {
this.error(err)
reject(new Error('Error in running solc command'))
}
if (!solcOutput.toString().includes(versionString)) {
this.log('[Slither Analysis]: Compiler version is different from installed solc version')
// Get compiler version without commit id e.g: 0.8.2
const version: string = versionString.substring(0, versionString.indexOf('+commit'))
// List solc versions installed using solc-select
try {
const solcSelectInstalledVersions: Buffer = execSync('solc-select versions', options)
// Check if required version is already installed
if (!solcSelectInstalledVersions.toString().includes(version)) {
this.log(`[Slither Analysis]: Installing ${version} using solc-select`)
// Install required version
execSync(`solc-select install ${version}`, options)
}
this.log(`[Slither Analysis]: Setting ${version} as current solc version using solc-select`)
// Set solc current version as required version
execSync(`solc-select use ${version}`, options)
} catch (err) {
this.error(err)
reject(new Error('Error in running solc-select command'))
}
} else this.log('[Slither Analysis]: Compiler version is same as installed solc version')
}
// Allow paths and set solc remapping for import URLs
const fileContent = readFileSync(utils.absolutePath(filePath, this.currentSharedFolder), 'utf8')
const importsArr = fileContent.match(/import ['"][^.|..](.+?)['"];/g)
let remaps = ''
if (importsArr?.length) {
const { remapString } = this.mapNpmDepsDir(importsArr)
remaps = remapString.trim()
}
const optimizeOption: string = optimize ? '--optimize' : ''
const evmOption: string = evmVersion ? `--evm-version ${evmVersion}` : ''
let solcArgs = ''
if (optimizeOption) {
solcArgs += optimizeOption + ' '
}
if (evmOption) {
if (!solcArgs.endsWith(' ')) solcArgs += ' '
solcArgs += evmOption
}
if (solcArgs) {
solcArgs = `--solc-args "${solcArgs.trimStart()}"`
}
const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : ''
const outputFile = 'remix-slither-report.json'
try {
// We don't keep the previous analysis
const outputFilePath = utils.absolutePath(outputFile, this.currentSharedFolder)
if (existsSync(outputFilePath)) unlinkSync(outputFilePath)
} catch (e) {
this.error('unable to remove the output file')
this.error(e.message)
}
const cmd = `slither ${filePath} ${solcArgs} ${solcRemaps} --json ${outputFile}`
this.log('[Slither Analysis]: Running Slither...')
// Added `stdio: 'ignore'` as for contract with NPM imports analysis which is exported in 'stderr'
// get too big and hangs the process. We process analysis from the report file only
const child = spawn(cmd, { cwd: this.currentSharedFolder, shell: true, stdio: 'ignore' })
const response = {}
child.on('close', () => {
const outputFileAbsPath: string = utils.absolutePath(outputFile, this.currentSharedFolder)
// Check if slither report file exists
if (existsSync(outputFileAbsPath)) {
let report = readFileSync(outputFileAbsPath, 'utf8')
report = JSON.parse(report)
if (report['success']) {
response['status'] = true
if (!report['results'] || !report['results'].detectors || !report['results'].detectors.length) {
response['count'] = 0
} else {
const { detectors } = report['results']
response['count'] = detectors.length
response['data'] = this.transform(detectors)
}
resolve(response)
} else {
this.log(report['error'])
reject(new Error('Error in running Slither Analysis.'))
}
} else {
this.error('Error in generating Slither Analysis Report. Make sure Slither is properly installed.')
reject(new Error('Error in generating Slither Analysis Report. Make sure Slither is properly installed.'))
}
})
})
} }
} }

@ -1,32 +1,29 @@
import { PluginClient } from '@remixproject/plugin' import {PluginClient} from '@remixproject/plugin'
import { Profile } from '@remixproject/plugin-utils' import {Profile} from '@remixproject/plugin-utils'
import { import {ElectronBasePlugin, ElectronBasePluginClient} from '@remixproject/plugin-electron'
ElectronBasePlugin,
ElectronBasePluginClient,
} from '@remixproject/plugin-electron'
import os from 'os' import os from 'os'
import * as pty from 'node-pty' import * as pty from 'node-pty'
import process from 'node:process' import process from 'node:process'
import { userInfo } from 'node:os' import {userInfo} from 'node:os'
import { findExecutable } from '../utils/findExecutable' import {findExecutable} from '../utils/findExecutable'
import { spawnSync } from 'child_process' import {exec, spawnSync} from 'child_process'
import { stripAnsi } from '../lib' import {stripAnsi} from '../lib'
import { DataBatcher } from '../lib/databatcher' import {DataBatcher} from '../lib/databatcher'
export const detectDefaultShell = () => { export const detectDefaultShell = () => {
const { env } = process const {env} = process
if (process.platform === 'win32') { if (process.platform === 'win32') {
return env.SHELL || 'powershell.exe' return env.SHELL || 'powershell.exe'
} }
try { try {
const { shell } = userInfo() const {shell} = userInfo()
if (shell) { if (shell) {
return shell return shell
} }
} catch { } } catch {}
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
return env.SHELL || '/bin/zsh' return env.SHELL || '/bin/zsh'
@ -38,10 +35,7 @@ export const detectDefaultShell = () => {
// Stores default shell when imported. // Stores default shell when imported.
const defaultShell = detectDefaultShell() const defaultShell = detectDefaultShell()
const getShellEnvArgs = [ const getShellEnvArgs = ['-ilc', 'echo -n "_SHELL_ENV_DELIMITER_"; env; echo -n "_SHELL_ENV_DELIMITER_"; exit']
'-ilc',
'echo -n "_SHELL_ENV_DELIMITER_"; env; echo -n "_SHELL_ENV_DELIMITER_"; exit',
]
const getShellEnvEnv = { const getShellEnvEnv = {
// Disables Oh My Zsh auto-update thing that can block the process. // Disables Oh My Zsh auto-update thing that can block the process.
@ -119,7 +113,7 @@ class XtermPluginClient extends ElectronBasePluginClient {
}) })
if (!(process.platform === 'win32')) { if (!(process.platform === 'win32')) {
const { stdout } = spawnSync(defaultShell, getShellEnvArgs, { const {stdout} = spawnSync(defaultShell, getShellEnvArgs, {
encoding: 'utf8', encoding: 'utf8',
}) })
this.parsedEnv = parseEnv(stdout) this.parsedEnv = parseEnv(stdout)
@ -150,7 +144,6 @@ class XtermPluginClient extends ElectronBasePluginClient {
const start_time = Date.now() const start_time = Date.now()
console.log('createTerminal', path, shell || defaultShell) console.log('createTerminal', path, shell || defaultShell)
const env = this.parsedEnv || process.env const env = this.parsedEnv || process.env
const ptyProcess = pty.spawn(shell || defaultShell, [], { const ptyProcess = pty.spawn(shell || defaultShell, [], {
@ -160,11 +153,10 @@ class XtermPluginClient extends ElectronBasePluginClient {
cwd: path || this.workingDir || process.cwd(), cwd: path || this.workingDir || process.cwd(),
env: env, env: env,
encoding: 'utf8', encoding: 'utf8',
}); })
const dataBatcher = new DataBatcher(ptyProcess.pid) const dataBatcher = new DataBatcher(ptyProcess.pid)
this.dataBatchers[ptyProcess.pid] = dataBatcher this.dataBatchers[ptyProcess.pid] = dataBatcher
ptyProcess.onData((data: string) => { ptyProcess.onData((data: string) => {
//console.log('data', data)
dataBatcher.write(Buffer.from(data)) dataBatcher.write(Buffer.from(data))
}) })
ptyProcess.onExit(() => { ptyProcess.onExit(() => {
@ -181,27 +173,48 @@ class XtermPluginClient extends ElectronBasePluginClient {
} }
async closeTerminal(pid: number): Promise<void> { async closeTerminal(pid: number): Promise<void> {
if (this.terminals) { console.log('closeTerminal', pid)
if (this.terminals[pid]) {
try { try {
this.terminals[pid].kill() if (this.terminals) {
} catch (err) {
// ignore if (this.dataBatchers[pid]) delete this.dataBatchers[pid]
if (this.terminals[pid]) {
try {
if (os.platform() === 'win32') {
// For Windows, use taskkill to terminate the process
exec(`taskkill /PID ${pid} /T /F`, (error, stdout, stderr) => {
if (error) {
console.error(`Error killing process: ${error}`);
}else{
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
}
});
} else {
this.terminals[pid].kill();
}
} catch (err) {
console.error(err)
// ignore
}
delete this.terminals[pid]
} }
delete this.terminals[pid]
} }
if (this.dataBatchers[pid]) this.emit('close', pid)
delete this.dataBatchers[pid] } catch (err) {
console.error(err)
} }
this.emit('close', pid)
} }
async resize({ cols, rows }: { cols: number; rows: number }, pid: number) { async resize({cols, rows}: {cols: number; rows: number}, pid: number) {
if (this.terminals[pid]) { if (this.terminals[pid]) {
try { try {
this.terminals[pid].resize(cols, rows) this.terminals[pid].resize(cols, rows)
} catch (_err) { } catch (_err) {
const err = _err as { stack: any } const err = _err as {stack: any}
console.error(err.stack) console.error(err.stack)
} }
} else { } else {

@ -1,6 +1,7 @@
import { exec } from 'child_process'; import { exec } from 'child_process';
import { CommitObject, ReadCommitResult } from 'isomorphic-git'; import { CommitObject, ReadCommitResult } from 'isomorphic-git';
import { promisify } from 'util'; import { promisify } from 'util';
import { cloneInputType, commitInputType, fetchInputType, pullInputType, pushInputType, checkoutInputType } from "@remix-api";
const execAsync = promisify(exec); const execAsync = promisify(exec);
const statusTransFormMatrix = (status: string) => { const statusTransFormMatrix = (status: string) => {
@ -9,12 +10,16 @@ const statusTransFormMatrix = (status: string) => {
return [0, 2, 0] return [0, 2, 0]
case 'A ': case 'A ':
return [0, 2, 2] return [0, 2, 2]
case 'R ':
return [0, 2, 2]
case 'M ': case 'M ':
return [1, 2, 2] return [1, 2, 2]
case 'MM': case 'MM':
return [1, 2, 3] return [1, 2, 3]
case ' M': case ' M':
return [1, 2, 0] return [1, 2, 1]
case 'AD':
return [0, 0, 3]
case ' D': case ' D':
return [1, 0, 1] return [1, 0, 1]
case 'D ': case 'D ':
@ -37,34 +42,87 @@ export const gitProxy = {
} }
}, },
async defaultRemoteName(path: string) {
try {
const { stdout } = await execAsync('git remote', { cwd: path });
const remotes = stdout.trim().split('\n');
return remotes[0];
} catch (error) {
throw new Error(`Failed to get the default remote name: ${error.message}`);
}
},
clone: async (url: string, path: string) => { clone: async (input: cloneInputType) => {
const { stdout, stderr } = await execAsync(`git clone ${url} "${path}"`); const { stdout, stderr } = await execAsync(`git clone ${input.url} "${input.dir}"`);
}, },
async push(path: string, remote: string, src: string, branch: string, force: boolean = false) { async push(path: string, input: pushInputType) {
const { stdout, stderr } = await execAsync(`git push ${force ? ' -f' : ''} ${remote} ${src}:${branch}`, { cwd: path }); if(!input.remote || !input.remote.name) {
input.remote = { name: await gitProxy.defaultRemoteName(path), url: '' }
}
let remoteRefString = ''
if(input.remoteRef && !input.remoteRef.name) {
remoteRefString = `:${input.remoteRef.name}`
}
const { stdout, stderr } = await execAsync(`git push ${input.force ? ' -f' : ''} ${input.remote.name}${remoteRefString} ${input.ref.name}`, { cwd: path });
}, },
async pull(path: string, remote: string, src: string, branch: string) { async pull(path: string, input: pullInputType) {
const { stdout, stderr } = await execAsync(`git pull ${remote} ${src}:${branch}`, { cwd: path }); if(!input.remote || !input.remote.name) {
input.remote = { name: await gitProxy.defaultRemoteName(path), url: '' }
}
let remoteRefString = ''
if(input.remoteRef && !input.remoteRef.name) {
remoteRefString = `:${input.remoteRef.name}`
}
const { stdout, stderr } = await execAsync(`git pull ${input.remote.name} ${input.ref.name}${remoteRefString}`, { cwd: path });
}, },
async fetch(path: string, remote: string, branch: string) { async fetch(path: string, input: fetchInputType) {
const { stdout, stderr } = await execAsync(`git fetch ${remote} ${branch}`, { cwd: path }); if(!input.remote || !input.remote.name) {
input.remote = { name: await gitProxy.defaultRemoteName(path), url: '' }
}
try {
const { stdout, stderr } = await execAsync(`git fetch ${input.remote.name} ${(input.ref && input.ref.name) ? input.ref.name : ''}`, { cwd: path });
if (stdout) {
console.log('stdout:', stdout);
}
if (stderr) {
console.error('stderr:', stderr);
}
} catch (error) {
console.error('Error during fetch:', error);
}
},
async checkout(path: string, input: checkoutInputType) {
let force = input.force ? ' -f' : '';
const { stdout, stderr } = await execAsync(`git checkout ${force} ${input.ref}`, { cwd: path });
}, },
async commit(path: string, message: string) { async commit(path: string, input: commitInputType) {
await execAsync(`git commit -m '${message}'`, { cwd: path }); await execAsync(`git commit -m '${input.message}'`, { cwd: path });
const { stdout, stderr } = await execAsync(`git rev-parse HEAD`, { cwd: path }); const { stdout, stderr } = await execAsync(`git rev-parse HEAD`, { cwd: path });
console.log('stdout commit:', stdout);
return stdout; return stdout;
}, },
async init(path: string) { async init(path: string) {
await execAsync(`git init`, { cwd: path }); await execAsync(`git init --initial-branch=main`, { cwd: path });
},
async updateSubmodules(path: string) {
const { stdout, stderr } = await execAsync(`git submodule update --init --recursive`, { cwd: path });
if (stdout) {
console.log('stdout:', stdout);
}
if (stderr) {
console.error('stderr:', stderr);
}
}, },
@ -103,7 +161,7 @@ export const gitProxy = {
return 0 return 0
}) })
console.log('files', files)
return files return files
}, },

@ -3,8 +3,8 @@ import fs from 'fs';
const useIsoGit = process.argv.includes('--useIsoGit'); const useIsoGit = process.argv.includes('--use-isogit');
const useOffline = process.argv.includes('--useOffline'); const useOffline = process.argv.includes('--use-offline');
// Function to read JSON file synchronously // Function to read JSON file synchronously
function readJSONFileSync(filename: string): any { function readJSONFileSync(filename: string): any {
@ -71,12 +71,12 @@ module.exports = {
// Check if running on CircleCI or locally // Check if running on CircleCI or locally
let args = process.env.CIRCLECI ? ["--e2e"] : ["--e2e-local"]; let args = process.env.CIRCLECI ? ["--e2e"] : ["--e2e-local"];
if(useIsoGit) args = [...args, '--useIsoGit']; if(useIsoGit) args = [...args, '--use-isogit'];
if(useOffline) args = [...args, '--useOffline']; if(useOffline) args = [...args, '--use-offline'];
// Set display size // Set display size
const windowSize = "--window-size=1920,1080"; const windowSize = "--window-size=1000,1000";
args = [...args, windowSize]; args = [...args];
switch (type) { switch (type) {
case 'Windows_NT': case 'Windows_NT':
@ -92,6 +92,7 @@ module.exports = {
break; break;
} }
console.log('binaryPath', binaryPath);
return { return {
binary: binaryPath, binary: binaryPath,
args: args args: args

@ -5,7 +5,9 @@ const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
done() done()
}, },
'start gist': !function (browser: NightwatchBrowser) { 'start gist': function (browser: NightwatchBrowser) {
browser.end()
/*
browser browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.waitForElementVisible('*[data-id="landingPageImportFromGist"]') .waitForElementVisible('*[data-id="landingPageImportFromGist"]')
@ -25,6 +27,7 @@ const tests = {
.click('[data-id="treeViewLitreeViewItemcontracts"]') .click('[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/3_Ballot.sol') .openFile('contracts/3_Ballot.sol')
.end() .end()
*/
} }
} }

@ -11,9 +11,11 @@ module.exports = {
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') .waitForElementVisible('button[data-id="landingPageImportFromTemplate"]')
.click('button[data-id="landingPageImportFromTemplate"]') .click('button[data-id="landingPageImportFromTemplate"]')
.waitForElementPresent('*[data-id="create-remixDefault"]')
.scrollAndClick('*[data-id="create-remixDefault"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.pause(3000) .pause(3000)
.windowHandles(function (result) { .windowHandles(function (result) {
console.log(result.value) console.log(result.value)

Loading…
Cancel
Save