'Bash Scripts Are Not Executing From Production Build of Electron App

UPDATE: index.js file content added. I have this electron app that is executing some bash scrips(*.sh) files to perform some task.

Everything is working absolutely fine in the development environment but when building the production build for deb installer for Ubuntu platform, everything is working, like opening on the app, other NodeJS stuff, but bash scripts are not executing.

Problem Statement: How to execute shell scripts in the production build of an electron app for Linux(Ubuntu OS). Getting this error

app/terminal_scripts/timer.sh Not Found

Below are the detailed explanation for the app.


**Project Directory Setup**:
    ProjectName
     |
      app > css | images | js | renders
      terminal_scripts
      node_modules
      package.json
      package-lock.json

Where inside the app directory, I have all CSS, images, js, HTML, and terminal scripts.

package.json:

{
  "name": "timer",
  "productName": "Timely",
  "version": "1.0.25",
  "description": "This desktop app shows you system clock",
  "main": "app/js/main/index.js",
  "scripts": {
    "start": "electron .",
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "nodemon --exec 'electron .'",
    "dist": "electron-builder"
  },
  "homepage": ".",
  "keywords": [
    "Electron",
    "Desktop App"
  ],
  "author": "NotABot Ltd <[email protected]>",
  "contributors": [
    {
      "name": "Not A Bot",
      "email": "[email protected]"
    }
  ],
  "license": "ISC",
  "dependencies": {
    "desandro-matches-selector": "^2.0.2",
    "electron-context-menu": "^1.0.0",
    "electron-is": "^3.0.0",
    "fix-path": "^3.0.0",
    "isotope-layout": "^3.0.6",
    "jquery": "^3.5.0",
    "jquery-bridget": "^2.0.1"
  },
  "build": {
    "appId": "com.test.timely",
    "productName": "Timely",
    "linux": {
      "target": "deb",
      "category": "System"
    }
  },
  "devDependencies": {
    "electron": "^8.1.1", 
    "electron-builder": "^22.6.0"
  }
}

HTML:

<html>
  <head>
    <title>Timely</title>
  </head>
  <body>
    <button onclick="displayTime()">Display Time</button>
    <textarea rows="20" cols="90" id="command-output" disabled="true"></textarea>
   
    <script>
        const {app} = require('electron');
        function displayTime(){
            console.log("button clicked");
            let cmd = `bash app/terminal_scripts/timer.sh`;
            
            let completeMessage = 'This is the message';
            backgroundProcess(cmd, completeMessage);
        }

        function getCommandOutput() { return document.getElementById("command-output");  };
        function getStatus()      { return document.getElementById("status");  };


        function appendOutput(msg) { getCommandOutput().value += (msg+'\n'); };
        function setStatus(msg)    { getStatus().innerHTML = msg; };
        
        function backgroundProcess(cmd, completeMessage){
            const process = require('child_process');
            
            var child = process.execFile(cmd, [] , {shell: true} );
            appendOutput("Processing......");
            child.on('error', function(err) {
                appendOutput('stderr: '+err );
            });

            child.stdout.on('data', function (data) {
                appendOutput(data);
            });

            child.stderr.on('data', function (data) {
                appendOutput(data );
            });

            return new Promise((resolve, reject) => {
                child.on('close', function (code) {
                    console.log(`code is: ${code}`);
                    if (code == 0){
                        setStatus(completeMessage);
                        resolve(1);  
                    }
                    else{
                        setStatus('Exited with error code ' + code);
                        resolve(-1);
                    }
                });
            });
        }
    </script>

  </body>
</html>


Bash Script:

#!/bin/bash
timer="$(date)"
echo "$timer" 

Permission is set 777 for this shell file



Platform Information:

  1. OS: Ubuntu 18.04.4 LTS
  2. NodeJS: 13.6.0
  3. NPM: 6.14.5
  4. Electron: 8.1.1
  5. Electron Builder: 22.6.0

index.js

const {app, BrowserWindow, Menu, Tray, ipcMain, MenuItem} = require('electron');
const path = require('path');
const contextMenu = require('electron-context-menu');

let splashWindow;


function createMainWindow(){
    mainWindow = new BrowserWindow({
        minHeight: 700,
        minWidth: 800,
        webPreferences: {
            nodeIntegration: true,
            webviewTag: true
        },
        show: false
    });
    //For dev only
    // mainWindow.webContents.openDevTools();
    mainWindow.loadFile('app/renderer/index.html');
    mainWindow.maximize();
}
app.on('ready', () =>{
    createMainWindow();
});


Solution 1:[1]

I have created a new directory alongside the app directory called the termainal_scripts.

Inside this, I have my bash file timer.sh.

I figured out how to execute shell scripts, in production by using process.resourcesPath inside path.join().

So, let the fixed path be as:

let fixedURL = path.join(process.resourcesPath, '/terminal_scripts/');

Then the command to execute will be:

let cmd = `${fixedURL}timer.sh`

Solution 2:[2]

Another way is to move flies to a new directory outside app directory and call it as extraResources.

Inside that directory you can add all your bash files and for production you can use below method.

let urlPath = path.join(process.resourcesPath, '/extraResources/')

and then use let cmd = `${urlPath}timer.sh`;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2