Skip to content

Commit

Permalink
Bun support
Browse files Browse the repository at this point in the history
  • Loading branch information
Unitech authored and Unitech committed Nov 13, 2024
1 parent 7fc9df1 commit d9e6964
Show file tree
Hide file tree
Showing 48 changed files with 691 additions and 145 deletions.
15 changes: 11 additions & 4 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ name: Node.js CI
on: [push, pull_request]

jobs:

node-tests:
runs-on: ubuntu-latest
timeout-minutes: 30

strategy:
matrix:
node-version: [22.x, 20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version: [22.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -19,8 +19,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Install Python
run: sudo apt install python3
- name: Install PHP CLI
Expand All @@ -39,6 +37,15 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Remove Node.js installed by setup-node action (if any)
run: |
if command -v node &> /dev/null; then
sudo rm -rf "$(which node)"
fi
if command -v npm &> /dev/null; then
sudo rm -rf "$(which npm)"
fi
- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Install dependencies using Bun
Expand Down
4 changes: 2 additions & 2 deletions bin/pm2
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash
#!/bin/sh

SCRIPT_PATH="$(dirname "$0")/../lib/binaries/CLI.js"

# Check if 'bun' is available, otherwise use 'node'
if command -v bun &> /dev/null
if command -v bun >/dev/null 2>&1
then
bun "$SCRIPT_PATH" "$@"
else
Expand Down
4 changes: 2 additions & 2 deletions bin/pm2-dev
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash
#!/bin/sh

SCRIPT_PATH="$(dirname "$0")/../lib/binaries/DevCLI.js"

# Check if 'bun' is available, otherwise use 'node'
if command -v bun &> /dev/null
if command -v bun >/dev/null 2>&1
then
bun "$SCRIPT_PATH" "$@"
else
Expand Down
4 changes: 2 additions & 2 deletions bin/pm2-docker
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash
#!/bin/sh

SCRIPT_PATH="$(dirname "$0")/../lib/binaries/Runtime4Docker.js"

# Check if 'bun' is available, otherwise use 'node'
if command -v bun &> /dev/null
if command -v bun >/dev/null 2>&1
then
bun "$SCRIPT_PATH" "$@"
else
Expand Down
4 changes: 2 additions & 2 deletions bin/pm2-runtime
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash
#!/bin/sh

SCRIPT_PATH="$(dirname "$0")/../lib/binaries/Runtime4Docker.js"

# Check if 'bun' is available, otherwise use 'node'
if command -v bun &> /dev/null
if command -v bun >/dev/null 2>&1
then
bun "$SCRIPT_PATH" "$@"
else
Expand Down
Binary file added bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var csts = {
ERROR_EXIT : 1,
CODE_UNCAUGHTEXCEPTION : 1,

IS_BUN : typeof Bun !== 'undefined',
IS_WINDOWS : (process.platform === 'win32' || process.platform === 'win64' || /^(msys|cygwin)$/.test(process.env.OSTYPE)),
ONLINE_STATUS : 'online',
STOPPED_STATUS : 'stopped',
Expand Down
1 change: 1 addition & 0 deletions examples/cluster-http/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var http = require('http');

var server = http.createServer(function(req, res) {
res.writeHead(200);
console.log(`New query`)
res.end('hey');
}).listen(process.env.PORT || 8089, '0.0.0.0', function() {
console.log('App listening on port 8089');
Expand Down
14 changes: 11 additions & 3 deletions lib/API/Modules/NPM.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,20 +179,28 @@ function install(CLI, module_name, opts, cb) {

// Builtin Node Switch
function getNPMCommandLine(module_name, install_path) {
if (which('npm')) {
if (which('bun')) {
return spawn.bind(this, 'bun', ['install', module_name, '--loglevel=error', '--cwd', `"${install_path}"` ], {
stdio : 'inherit',
env: process.env,
windowsHide: true,
shell : true
})
}
else if (which('npm')) {
return spawn.bind(this, cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error', '--prefix', `"${install_path}"` ], {
stdio : 'inherit',
env: process.env,
windowsHide: true,
shell : true
shell : true
})
}
else {
return spawn.bind(this, cst.BUILTIN_NODE_PATH, [cst.BUILTIN_NPM_PATH, 'install', module_name, '--loglevel=error', '--prefix', `"${install_path}"`], {
stdio : 'inherit',
env: process.env,
windowsHide: true,
shell : true
shell : true
})
}
}
Expand Down
14 changes: 7 additions & 7 deletions lib/API/Serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ var url = require('url');
var path = require('path');
var debug = require('debug')('pm2:serve');

var probe = require('@pm2/io');
var errorMeter = probe.meter({
name : '404/sec',
samples : 1,
timeframe : 60
})
// var probe = require('@pm2/io');
// var errorMeter = probe.meter({
// name : '404/sec',
// samples : 1,
// timeframe : 60
// })
/**
* list of supported content types.
*/
Expand Down Expand Up @@ -270,7 +270,7 @@ function serveFile(uri, request, response) {
console.error('[%s] Error while serving %s with content-type %s : %s',
new Date(), filePath, contentType, error.message || error);
}
errorMeter.mark();
//errorMeter.mark();
if (error.code === 'ENOENT') {
if (options.spa && !request.wantHomepage) {
request.wantHomepage = true;
Expand Down
17 changes: 15 additions & 2 deletions lib/API/Startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ module.exports = function(CLI) {
*/
function isNotRoot(startup_mode, platform, opts, cb) {
Common.printOut(`${cst.PREFIX_MSG}To ${startup_mode} the Startup Script, copy/paste the following command:`);

let pm2_bin_path = require.main.filename

if (pm2_bin_path.includes('/lib/binaries/CLI.js') === true) {
pm2_bin_path = pm2_bin_path.replace('/lib/binaries/CLI.js', '/bin/pm2')
}

if (opts.user) {
console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 ' + opts.args[1].name() + ' ' + platform + ' -u ' + opts.user + ' --hp ' + process.env.HOME);
return cb(new Error('You have to run this with elevated rights'));
}
return sexec('whoami', {silent: true}, function(err, stdout, stderr) {
console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' ' + require.main.filename + ' ' + opts.args[1].name() + ' ' + platform + ' -u ' + stdout.trim() + ' --hp ' + process.env.HOME);
console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' ' + pm2_bin_path + ' ' + opts.args[1].name() + ' ' + platform + ' -u ' + stdout.trim() + ' --hp ' + process.env.HOME);
return cb(new Error('You have to run this with elevated rights'));
});
}
Expand Down Expand Up @@ -336,7 +343,13 @@ module.exports = function(CLI) {
else
envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))

template = template.replace(/%PM2_PATH%/g, process.mainModule.filename)
let pm2_bin_path = require.main.filename

if (pm2_bin_path.includes('/lib/binaries/CLI.js') === true) {
pm2_bin_path = pm2_bin_path.replace('/lib/binaries/CLI.js', '/bin/pm2')
}

template = template.replace(/%PM2_PATH%/g, pm2_bin_path)
.replace(/%NODE_PATH%/g, envPath)
.replace(/%USER%/g, user)
.replace(/%HOME_PATH%/g, opts.hp ? path.resolve(opts.hp, '.pm2') : cst.PM2_ROOT_PATH)
Expand Down
3 changes: 2 additions & 1 deletion lib/API/UX/pm2-ls.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,5 +478,6 @@ module.exports = function(list, commander) {
if (sysmonit && sysmonit[0])
miniMonitBar(sysmonit[0])

checkIfProcessAreDumped(list)
// Disable warning message of process list not saved
//checkIfProcessAreDumped(list)
}
11 changes: 4 additions & 7 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,7 @@ Client.prototype.launchDaemon = function(opts, cb) {
if (!process.env.PM2_DISCRETE_MODE)
Common.printOut(that.conf.PREFIX_MSG + 'Spawning PM2 daemon with pm2_home=' + this.pm2_home);

var interpreter = 'node';

if (which('node') == null)
interpreter = process.execPath;
var interpreter = process.execPath;

var child = require('child_process').spawn(interpreter, node_args, {
detached : true,
Expand All @@ -261,7 +258,7 @@ Client.prototype.launchDaemon = function(opts, cb) {
'SILENT' : that.conf.DEBUG ? !that.conf.DEBUG : true,
'PM2_HOME' : that.pm2_home
}, process.env),
stdio : ['ipc', out, err]
stdio : [null, out, err, 'ipc']
});

function onError(e) {
Expand All @@ -271,13 +268,13 @@ Client.prototype.launchDaemon = function(opts, cb) {

child.once('error', onError);

child.unref();
if (this.conf.IS_BUN === false)
child.unref();

child.once('message', function(msg) {
debug('PM2 daemon launched with return message: ', msg);
child.removeListener('error', onError);
child.disconnect();

if (opts && opts.interactor == false)
return cb(null, child);

Expand Down
13 changes: 10 additions & 3 deletions lib/Common.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ Common.sink.determineExecMode = function(app) {
*/
if (!app.exec_mode &&
(app.instances >= 1 || app.instances === 0 || app.instances === -1) &&
app.exec_interpreter.indexOf('node') > -1) {
(app.exec_interpreter.includes('node') === true || app.exec_interpreter.includes('bun') === true)) {
app.exec_mode = 'cluster_mode';
} else if (!app.exec_mode) {
app.exec_mode = 'fork_mode';
Expand Down Expand Up @@ -444,6 +444,12 @@ Common.sink.resolveInterpreter = function(app) {
var extName = path.extname(app.pm_exec_path);
var betterInterpreter = extItps[extName];

// Bun support
if (noInterpreter && (extName == '.js' || extName == '.ts') && cst.IS_BUN === true) {
noInterpreter = false
app.exec_interpreter = process.execPath;
}

// No interpreter defined and correspondance in schema hashmap
if (noInterpreter && betterInterpreter) {
app.exec_interpreter = betterInterpreter;
Expand All @@ -458,8 +464,9 @@ Common.sink.resolveInterpreter = function(app) {
}
}
// Else if no Interpreter detect if process is binary
else if (noInterpreter)
app.exec_interpreter = isBinary(app.pm_exec_path) ? 'none' : 'node';
else if (noInterpreter) {
app.exec_interpreter = isBinary(app.pm_exec_path) ? 'none' : process.execPath;
}
else if (app.exec_interpreter.indexOf('node@') > -1)
resolveNodeInterpreter(app);

Expand Down
1 change: 1 addition & 0 deletions lib/Daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Daemon.prototype.innerStart = function(cb) {
fmt.field('Process dump file', cst.DUMP_FILE_PATH);
fmt.field('Concurrent actions', cst.CONCURRENT_ACTIONS);
fmt.field('SIGTERM timeout', cst.KILL_TIMEOUT);
fmt.field('Runtime Binary', process.execPath);
fmt.sep();
};

Expand Down
49 changes: 45 additions & 4 deletions lib/God.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@ var Configuration = require('./Configuration.js');
/**
* Override cluster module configuration
*/
cluster.setupMaster({
windowsHide: true,
exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')
});

if (cst.IS_BUN == true) {
cluster.setupMaster({
windowsHide: true,
exec : path.resolve(path.dirname(module.filename), 'ProcessContainerBun.js')
});
}
else {
cluster.setupMaster({
windowsHide: true,
exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')
});
}

/**
* Expose God
Expand Down Expand Up @@ -217,8 +226,12 @@ God.executeApp = function executeApp(env, cb) {
return cb(null, clu);
}

var cb_called = false

/** Callback when application is launched */
var readyCb = function ready(proc) {
cb_called = true

// If vizion enabled run versioning retrieval system
if (proc.pm2_env.vizion !== false && proc.pm2_env.vizion !== "false")
God.finalizeProcedure(proc);
Expand Down Expand Up @@ -249,7 +262,22 @@ God.executeApp = function executeApp(env, cb) {

God.clusters_db[clu.pm2_env.pm_id] = clu;


if (cst.IS_BUN) {
// When starting an app that does not listen on a port
// Bun do not call 'online' event
// This is a temporary workaround
var a = setTimeout(() => {
if (clu.pm2_env)
God.clusters_db[clu.pm2_env.pm_id].state = 'online'
return readyCb(clu)
}, 500)
}

clu.once('error', function(err) {
if (cst.IS_BUN)
clearTimeout(a)

console.error(err.stack || err);
try {
clu.destroy && clu.destroy();
Expand All @@ -261,15 +289,28 @@ God.executeApp = function executeApp(env, cb) {
});

clu.once('disconnect', function() {
if (cst.IS_BUN)
clearTimeout(a)

console.log('App name:%s id:%s disconnected', clu.pm2_env.name, clu.pm2_env.pm_id);
});

clu.once('exit', function cluExit(code, signal) {
if (cst.IS_BUN) {
clearTimeout(a)
if (cb_called == false)
readyCb(clu);
}
//God.writeExitSeparator(clu.pm2_env, code, signal)

God.handleExit(clu, code || 0, signal || 'SIGINT');
});

return clu.once('online', function () {
if (cst.IS_BUN) {
clearTimeout(a);
}

if (!clu.pm2_env.wait_ready)
return readyCb(clu);

Expand Down
Loading

0 comments on commit d9e6964

Please sign in to comment.