I am building an app based on electron vue. My electron app is supposed to start its backend running from a docker container. To do this I call a bash file with the docker run command in it.
const {spawn} = require('child_process')
const dckrrn = spawn('sh', dockercall)
dckrrn.stdout.on('data', (data) => {
console.log(`stdout: ${data}`)
})
dckrrn.stderr.on('data', (data) => {
console.log(`stderr: ${data}`)
})
dckrrn.on('close', (code) => {
console.log(`child process exited with code ${code}`)
})
Everything works fine in development mode but when I try it in my packaged app it complains:
stderr: pathtobashscript.sh line 13 docker: command not found.
It seems for some reason the spawned child process is unaware of the docker installation on the system. What am I doing wrong? What is the correct way to achieve this? Should I try the execfile function? Thanks for your time!
PS:
Sorry that I cannot provide you with a reproducible example, the total app with backend is around 7gb.
PPS:
some interesting sidenotes:
which docker
returns nothing, and:
pwd
returns: /
PPPS: I tried including the docker path at the beginning of my bash script but with no success:
PATH="/usr/local/bin/docker:${PATH}"
export PATH
4PS:
I managed to get the docker running by adding shell: true to the environment. The problem I have now is that the docker folder mappings do not work anymore. So I guess I also have to make them visible to the env somehow.
const {spawn} = require('child_process')
const dckrrn = spawn('sh', dockercall, {
env: {
shell: true
}
})
solved by adding shell: true to the env:
const {spawn} = require('child_process')
const dckrrn = spawn('sh', dockercall, {
env: {
shell: true
}
})
Related
I'm using Node v18 with the experimental testrunner. I use express as a dev dependency for http integration tests which works fine but there is one test freezing or stopping the testrunner ( it doesn't continue )
I'm using TS but can also reproduce it with JS, the test file HttpTests.js contains
import assert from 'assert/strict';
import express from 'express';
import test from 'node:test';
test('Http', async () => {
const server = express();
server.listen(3000);
assert.ok(false);
});
Running this with the npm script "test": "node --test $(find . -name '*Tests.js')" breaks the test runner.
Any ideas what is wrong or missing?
Why am I not using the default execution model?
Since I'm using TS I had to find a way to use ts-node with the testrunner. You can find more information here
https://github.com/nodejs/node/issues/43675
So currently my TS project is using this npm script, which works fine
Reproduction
I created a minimal reproduction repository with and without TypeScript
https://github.com/matthiashermsen/reproduce-broken-test-ts
https://github.com/matthiashermsen/reproduce-broken-test-js
For reproduction purposes run mkdir reproduction && cd reproduction && npm init -y && npm install express. After that create a test directory with a file HttpTests.js containing the content as shown above. Change the package.json to
{
"name": "reproduction",
"type": "module",
"scripts": {
"test": "node --test $(find . -name '*Tests.js')"
}
}
and run the script, the testrunner should not finish.
The testrunner is still experimental
Yes I know. But there are many tests in the project that work perfectly fine. Some sample code
await t.test('subtest - saves data.', async () => {
const expectedResult = {};
const api = express();
const port = await getRandomPort();
const server = api
.use(express.json())
.post('/save', (request, response) => {
response.json(expectedResult);
})
.listen(port);
const httpDataProvider = new HttpDataProvider({ url: `http://localhost:${port}` });
const actualResult = await httpDataProvider.saveSomething();
assert.deepEqual(actualResult, expectedResult);
server.close();
});
The issue is the async activity that you start (server.listen()) but don't stop before the test errors out (by an exception thrown by assert.ok(false)).
Your second test case will probably also stall if actualResult doesn't deep-equal expectedResult because of the same issue (server.close() won't be called).
A workaround would be to always make sure the server gets closed in the end:
test('Http', async () => {
const app = express();
const server = app.listen(3000);
try {
assert.ok(false);
} finally {
server.close();
}
});
Most test frameworks provide "before/after" functionality that can be used to set up or tear down auxiliary objects before and after a test.
Okay, so when I run the following spawnBot() function in an Electron window, cd throws an error saying that the requested resource could not be found. The following code is part of a file located at /toggle-gui/scripts.js, the electron window page is located at /toggle-gui/pages/index.html and I want to cd into /toggle-gui/imported_bots/toggle-base.
const { spawn } = require('child_process');
const { ipcRenderer } = require('electron');
var selectedBotDir = "./imported_bots/toggle-base";
var bot;
function spawnBot(){
console.log("Attempting to start Toggle...")
console.log("Currently selected bot directory: " + selectedBotDir)
try{
bot = spawn(`cd ${selectedBotDir}; npm install; npm start`, {shell: true, detached: true});
console.log("Process ID: " + bot.pid);
bot.stdout.on('data', data => {
console.log(data);
});
bot.stderr.on('data', data => {
console.log("ERROR: " + data);
});
return bot;
}
catch(error){
console.log(error);
console.log("Toggle failed to start. There should be extra logging output above.")
}
}
Some help here would be greatly appreciated.
The use of ./ in paths makes them relative, which means "treat this path as a subdirectory of the directory you're currently in." However, this "directory you're currently in" does not have to be the directory your Node.js code is located in but rather the directory the shell process you're creating using spawn () was spawned in, which, by default will be set to the directory you're running your Electron app from (i.e. where you ran npm run and not where your script is located).
Thus, you have to tell Node.js in what directory the shell process executing your OS code should be run in, which can be done by passing a cwd (current working directory) parameter to span () as stated in its documentation. The current script's directory can be accessed in Node.js using __dirname, which would lead to the following code:
bot = spawn(`cd ${selectedBotDir}; npm install; npm start`, {shell: true, detached: true, cwd: __dirname});
However, using this method you can get rid of the cd step entirely as you can directly set the cwd to be equal to selectedBotDir, given that that path actually starts with the current directory:
var selectedBotDir = `${__dirname}/imported_bots/toggle-base`;
// ...
try {
bot = spawn(`npm install; npm start`, {shell: true, detached: true, cwd: selectedBotDir});
// ...
} // ...
Please be aware, however, that this could break in multiple ways once you decide to package and distribute your app. By default, Electron apps will be packed into TAR-like archives, ASAR files, which will then be the __dirname. However, the OS cannot directly read from ASAR files and will once again fail to "find the requested resource". Also, if you're planning to distribute your app, don't expect users to also have NPM installed.
I try to start and stop a Tomcat instance on Windows using Electron.
I have modified the electron-quick-start project to stop my Tomcat instance with a batch file which calls Tomcat's shutdown.bat when all Electron windows are closed or before my application exits.
However, when I close my application, there is no output from the shutdownTomcat.on ("data", ...) and shutdownTomcat.on ("exit", ...) listeners. The only output is from a console.log ("Hello world") from my app.on ("before-quit", ...).
I chose this approach because I am new to Electron and want to test NodeJS' spawn's behaviour.
When I use my code outside of the app.on () listeners, the output is shown but my Tomcat instance is not being stopped. However, my startTomcat.bat file, which calls Tomcat's startup.bat, works without any problems.
I already read NodeJS' childProcess' documentation ("Spawning .bat and .cmd files on Windows"), but I cannot get it to work; which leads to my question, namely where the problem is and what I'm doing wrong.
My Main.js file I use for the main process:
const { app, BrowserWindow } = require('electron');
const { spawn } = require('child_process');
const path = require('path');
const start = path.resolve("./start.bat");
const startTomcat = spawn('cmd.exe', ['/c', start], {
cwd: process.cwd(),
detached: true,
});
// ...
app.on('before-quit',()=> {
const shutdownTomcat = spawn('cmd.exe', ['/c', stop], {
detached: true
// stdio: 'ignore'
});
shutdownTomcat.stdout.on('data', (data) => {
console.log("This data won't show up...", data);
});
shutdownTomcat.stderr.on('data', (data) => {
console.log(data.toString());
});
shutdownTomcat.on('exit', (code) => {
console.log(`Child Shutdown Tomcat exited with code ${code}`);
});
console.log("Hello World");
});
And finally, the batch file (stop.bat) I'm using to call Tomcat's shutdown.bat:
cd /D "%~dp0"
cd "..\\apache-tomcat\\bin"
call shutdown.bat
Most probably your electron application is already terminated by the time your events would have fired. Therefore there is no longer reference to your spawned process and listeners.
You can try event.preventDefault() to cancel the app quitting. Then you can explicitly app.quit() once you are done (but beware of infinitely looping through your before-quit listener; you may remove it or app.exit(0)).
I am working on a NodeJs application which helps the developers in my company with the development of our Electron-based product. It does some automatization and at the end, it starts the Electron-app automatically.
Starting the Electron app from inside NodeJs is not a problem. normally the apps are started with a bash script which looks like:
#!/bin/sh
HOME=$PWD/home-dir ./node_modules/.bin/electron myAppDir
myAppDir is the directory with my Electron-App, could be a JavaScript file as well.
Worth to mention, that ./node_modules/.bin/electron is just a symlink to ./node_modules/electron/cli.js
I did following:
const app = execFile('/the/path/to/the/bash/script', [], {
windowsHide: true,
},(error, stdout, stderr) => {
if (error) {
throw error;
}
warn('The app was terminated');
});
This starts the app just fine. However if I do app.kill('SIGTERM'); it outputs the 'The app was terminated' but the app itself does not close.
I tried to execute the node_modules/.bin/electron or the ./node_modules/electron/cli.js instead:
const app = execFile('/the/path/to/node_modules/.bin/electron', ['myAppDir'], {
windowsHide: true,
detached: true,
env: {
HOME: 'path/to/home'),
}
I can launch the Electron app but again - it does not close the running app when I do app.kill('SIGTERM');
EDIT:
My assumption is, that the electron launcher actually spawns a new subprocess, thus killing the launcher does not stops the actual launched app.
This is the content of ./node_modules/.bin/electron (or ./node_modules/electron/cli.js respectively)
#!/usr/bin/env node
var electron = require('./')
var proc = require('child_process')
var child = proc.spawn(electron, process.argv.slice(2), {stdio: 'inherit'})
child.on('close', function (code) {
process.exit(code)
})
You can use this library in you node app https://www.npmjs.com/package/systeminformation and with its .processes(cb) method find running electron app and kill it in the callback of the processes method.
My goal is to do the following steps in my "bundle" process to export an electron app:
open meteor directory
run meteor
when meteor is listening, open the meteor shell
pass the "electrify" command to meteor shell
when electrify build is complete, return.
So far, I have the following
require('shelljs/make');
require('shelljs/global');
var path = require('path');
target.all = function () {
target.bundle();
}
target.bundle = function () {
console.log('Building for architecture: ' + process.platform + platform.arch);
var meteorPath = path.join(__dirname, 'meteor');
cd(meteorPath);
// this runs for a while, make it asyncronous
var meteorCommand = exec('meteor', { async: true });
meteorCommand.stdout.on('data', function (data) {
// ???
});
}
This yields expected output to stdout.
Building for: darwinx64
[[[[[ ~/Code/test/meteor-test ]]]]]
=> Started proxy.
=> Started MongoDB.
=> Babel active on file extensions: es6.js, es6, jsx
I20150728-10:06:05.899(-4)? electrify: installing electrified dependencies
I20150728-10:06:07.005(-4)? electrify: launching electron
=> Started your app.
=> App running at: http://localhost:3000/
However, I am facing a problem... meteor has a startup process and needs to be running before I can use meteor shell, which is the final step where it says "App running at:".
How can I wait for this event to happen before opening the meteor shell?