Skip to content

Commit

Permalink
feat(kill, getPIDs): find and kill processes by ports and PIDs (#247)
Browse files Browse the repository at this point in the history
* feat(kill): terminate ports and PIDs

* chore: expose all process methods

* chore(cli): add `--kill-port`

* chore(cli): add `--kill-pid`

* chore: remove silient throw

* chore: allow multiple ports and PIDs

* ci: add test when no port is passed to end method

* chore: adapt `--kill-port` and `--kill-pid`

* docs: improve README.md

* chore: fix CLI PIDs typeof

* docs: improve README.md

* docs: add processes documentation

* chore: include tea

* chore: add range feature
  • Loading branch information
wellwelwel authored May 16, 2024
1 parent 0e81815 commit 13305cf
Show file tree
Hide file tree
Showing 18 changed files with 580 additions and 106 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Enjoying **Poku**? Consider giving him a star ⭐️
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><img width="16" height="16" alt="check" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/check.svg"> No eval needed 🔐<br />
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><img width="16" height="16" alt="check" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/check.svg"> No global state<br />
<img width="16" height="16" alt="check" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/check.svg"> **Parallel** and **Sequential** runs 🏃🏽🏃🏻<br />
<img width="16" height="16" alt="check" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/check.svg"> Easily handle **Background Services** and **Servers**, **Processes** and **Ports**<br />

<img width="16" height="16" alt="check" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/check.svg"> **Poku** is [**100%** documented](https://poku.io/docs)<br />
<img width="16" height="16" alt="check" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/check.svg"> Designed to be human-friendly<br />
Expand Down Expand Up @@ -187,10 +188,15 @@ deno run npm:poku

### Essentials

- [**poku**](https://poku.io/docs/category/poku) (_test runner_)
- [**assert**](https://poku.io/docs/documentation/assert) (_test assertion_)
- [**startScript**](https://poku.io/docs/documentation/startScript) (_run `package.json` scripts in a background process_)
- [**startService**](https://poku.io/docs/documentation/startService) (_run files in a background process_)
- **Test**
- [**poku**](https://poku.io/docs/category/poku) (_test runner_)
- [**assert**](https://poku.io/docs/documentation/assert) (_test assertion_)
- **Background Services**
- [**startScript**](https://poku.io/docs/documentation/startScript) (_run `package.json` scripts in a background process_)
- [**startService**](https://poku.io/docs/documentation/startService) (_run files in a background process_)
- **Processes**
- [**kill**](https://poku.io/docs/documentation/processes/kill) (_terminate Ports, Port Ranges and PIDs_)
- [**getPIDs**](https://poku.io/docs/documentation/processes/get-pids) (_get all processes IDs using ports and port ranges_)

### Helpers

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@
"files",
"list-files",
"tsx",
"fast",
"easy",
"kill",
"process",
"port",
"cross-platform",
"poku",
"pokujs"
],
Expand Down
2 changes: 1 addition & 1 deletion src/@types/background-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ export type StartServiceOptions = {
readonly platform?: Configs['platform'];
} & BackgroundProcessOptions;

export type End = (port?: number) => Promise<void>;
export type End = (port?: number | number[]) => Promise<void>;
51 changes: 41 additions & 10 deletions src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/* c8 ignore start */
import { escapeRegExp } from '../modules/list-files.js';
import { getArg, getLastParam, hasArg } from '../helpers/get-arg.js';
import { poku } from '../index.js';
import { kill, poku } from '../index.js';
import { platformIsValid } from '../helpers/get-runtime.js';
import { format } from '../helpers/format.js';

Expand All @@ -14,6 +14,9 @@ const dirs =
const platform = getArg('platform');
const filter = getArg('filter');
const exclude = getArg('exclude');
const killPort = getArg('kill-port');
const killRange = getArg('kill-range');
const killPID = getArg('kill-pid');
const parallel = hasArg('parallel');
const quiet = hasArg('quiet');
const debug = hasArg('debug');
Expand All @@ -24,13 +27,41 @@ if (hasArg('log-success'))
`The flag ${format.bold('--log-success')} is deprecated. Use ${format.bold('--debug')} instead.`
);

poku(dirs, {
platform: platformIsValid(platform) ? platform : undefined,
filter: filter ? new RegExp(escapeRegExp(filter)) : undefined,
exclude: exclude ? new RegExp(escapeRegExp(exclude)) : undefined,
parallel,
quiet,
debug,
failFast,
});
(async () => {
if (killPort) {
const ports = killPort.split(',').map((port) => Number(port));

await kill.port(ports);
}

if (killRange) {
const ranges = killRange.split(',');

for (const range of ranges) {
const ports = range.split('-').map((port) => Number(port));

const startsAt = ports[0];
const endsAt = ports[1];

await kill.range(startsAt, endsAt);
}
}

if (killPID) {
const PIDs = killPID.split(',').map((port) => Number(port));

await kill.pid(PIDs);
}

await poku(dirs, {
platform: platformIsValid(platform) ? platform : undefined,
filter: filter ? new RegExp(escapeRegExp(filter)) : undefined,
exclude: exclude ? new RegExp(escapeRegExp(exclude)) : undefined,
parallel,
quiet,
debug,
failFast,
});
})();

/* c8 ignore stop */
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { beforeEach, afterEach } from './modules/each.js';
export { publicListFiles as listFiles } from './modules/list-files.js';
export { test } from './modules/test.js';
export { startService, startScript } from './modules/create-service.js';
export { getPIDs, kill } from './modules/processes.js';
export type { Code } from './@types/code.js';
export type { Configs } from './@types/poku.js';
export type { Configs as ListFilesConfigs } from './@types/list-files.js';
Expand Down
22 changes: 6 additions & 16 deletions src/modules/create-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import {
StartServiceOptions,
} from '../@types/background-process.js';
import { sanitizePath } from './list-files.js';
import { findPID, killPID } from '../services/pid.js';
import { kill } from './processes.js';

const runningProcesses: Map<number, { end: End; port?: number }> = new Map();
const runningProcesses: Map<number, { end: End; port?: number | number[] }> =
new Map();

/* c8 ignore start */
process.once('SIGINT', async () => {
Expand Down Expand Up @@ -50,13 +51,13 @@ const backgroundProcess = (
let portBackup: number | undefined;

/* c8 ignore start */
const end: End = (port?: number) =>
const end: End = (port) =>
new Promise((resolve) => {
try {
runningProcesses.delete(PID);

if (isWindows) {
killPID.windows(PID);
kill.pid(PID);
return;
}

Expand All @@ -69,18 +70,7 @@ const backgroundProcess = (

if (port && ['bun', 'deno'].includes(runtime)) {
setTimeout(async () => {
const PIDs = isWindows
? await findPID.windows(port)
: await findPID.unix(port);

for (const subPID of PIDs) {
if (!subPID) continue;

isWindows
? await killPID.windows(subPID)
: await killPID.unix(subPID);
}

await kill.port(port);
resolve(undefined);
return;
});
Expand Down
94 changes: 94 additions & 0 deletions src/modules/processes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* c8 ignore start */

import { isWindows } from '../helpers/runner.js';
import {
getPIDs as getPIDsService,
killPID as killPIDService,
populateRange,
setPortsAndPIDs,
} from '../services/pid.js';

const getPIDsByPorts = async (port: number | number[]): Promise<number[]> => {
const ports = setPortsAndPIDs(port);
const PIDs: number[] = [];

await Promise.all(
ports.map(async (p) => {
PIDs.push(
...(await (isWindows
? getPIDsService.windows(p)
: getPIDsService.unix(p)))
);
})
);

return PIDs;
};

const getPIDsByRange = async (
startsAt: number,
endsAt: number
): Promise<number[]> => {
const ports = populateRange(startsAt, endsAt);

return await getPIDs(ports);
};

/**
* Returns an array containing the ID of all processes listening to the specified port
*/
export const getPIDs = Object.assign(getPIDsByPorts, {
/**
* Returns an array containing the ID of all processes listening to the specified range port
*/
range: getPIDsByRange,
});

const killPID = async (PID: number | number[]): Promise<void> => {
const PIDs = setPortsAndPIDs(PID);

await Promise.all(
PIDs.map(async (p) => {
isWindows
? await killPIDService.windows(p)
: await killPIDService.unix(p);
})
);
};

const killPort = async (port: number | number[]): Promise<void> => {
const PIDs = await getPIDs(port);

for (const PID of PIDs) {
if (!PID) continue;

await killPID(PID);
}
};

const killRange = async (startsAt: number, endsAt: number): Promise<void> => {
const PIDs = await getPIDs.range(startsAt, endsAt);

for (const PID of PIDs) {
if (!PID) continue;

await killPID(PID);
}
};

export const kill = {
/**
* Terminates the specified process ID
*/
pid: killPID,
/**
* Terminates all processes listening on the specified port
*/
port: killPort,
/**
* Terminates all processes listening on the specified range ports
*/
range: killRange,
};

/* c8 ignore stop */
Loading

0 comments on commit 13305cf

Please sign in to comment.