Skip to content

Commit

Permalink
feat: Enable macos (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
sylc authored Jan 1, 2023
1 parent 3dc2b21 commit d3e2290
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 85 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
name: GitHub Actions Demo
name: CI
on: [push]
jobs:
dkill-test:
runs-on: ${{ matrix.os }}
timeout-minutes: 3
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
os: [
ubuntu-latest,
windows-latest,
macos-latest
]

steps:
- uses: denoland/setup-deno@v1
Expand All @@ -15,7 +19,7 @@ jobs:
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"

- name: Check out repository code
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: lint
run: deno lint
Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"deno.lint": true,
"deno.suggest.imports.hosts": {
"https://deno.land": true
}
},
"editor.formatOnSave": true
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ See [docs](https://doc.deno.land/https://deno.land/x/dkill/mod.ts)

- Windows: Windows 8 or above
- Linux: On linux the cmd `ss` is used, which works on ubuntu 16.04 and above.
- Mac: Not implemented. PR welcome.
- MacOS: The command `lsof` is used. Interactive mode and listing the exact
command is currently not implemented

## Inspiration

Expand Down
75 changes: 46 additions & 29 deletions cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertNotEquals, delay } from "./deps_test.ts";
import { assertEquals, assertNotEquals, delay } from "./deps_test.ts";

Deno.test("killing by pid", async () => {
// create pid
Expand All @@ -11,7 +11,7 @@ Deno.test("killing by pid", async () => {
cmd: ["deno", "run", "-A", "./cli.ts", `${pTest.pid}`],
});
// wait dkill finishes
await pDkill.status();
const cliStatus = await pDkill.status();

// retreive status from test pid
const status = await pTest.status();
Expand All @@ -20,39 +20,56 @@ Deno.test("killing by pid", async () => {
pTest.close();
pDkill.close();

// ensure dkill existed cleanly
assertEquals(cliStatus.code, 0);
assertNotEquals(status.code, 0);
});
Deno.test("killing by ports", async () => {
// create a webserver
const pTest1 = Deno.run({
cmd: ["deno", "run", "-A", "./src/tests/utils.ts"],
});
const pTest2 = Deno.run({
cmd: ["deno", "run", "-A", "./src/tests/utils.ts", "8081"],
});


// give time fo the webserver to start and the port be discoverable
await delay(5000);

// call dkill
const pDkill = Deno.run({
cmd: ["deno", "run", "-A", "./cli.ts", ":8080", ":8081"],
});
// wait dkill finishes
await pDkill.status();
Deno.test({
name: "killing by ports",
fn: async () => {
// create a webserver
const pTest1 = Deno.run({
cmd: ["deno", "run", "-A", "./src/tests/utils.ts"],
});
const pTest2 = Deno.run({
cmd: ["deno", "run", "-A", "./src/tests/utils.ts", "8081"],
});

// retrieve status from test pid
const status1 = await pTest1.status();
const status2 = await pTest2.status();
// give time fo the webserver to start and the port be discoverable
await delay(5000);

// close resources
pTest1.close();
pTest2.close();
// call dkill
const pDkill = Deno.run({
cmd: [
"deno",
"run",
"-A",
"--unstable",
"./cli.ts",
"--verbose",
":8080",
":8081",
],
});
// wait dkill finishes
const cliStatus = await pDkill.status();
pDkill.close();
// ensure dkill exited cleanly
assertEquals(cliStatus.code, 0);

pDkill.close();
// throw Error('xxx')

assertNotEquals(status1.code, 0);
assertNotEquals(status2.code, 0);
// retrieve status from test pid
const status1 = await pTest1.status();
const status2 = await pTest2.status();
// close resources
pTest1.close();
pTest2.close();

assertNotEquals(status1.code, 0);
assertNotEquals(status1.code, 5); // check it wasn't a timeout
assertNotEquals(status2.code, 0);
assertNotEquals(status2.code, 5);
},
});
6 changes: 5 additions & 1 deletion cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ await new Command()
You can specify multiple targets at once: 'dkill node.exe :5000 :3000 164'`,
)
.arguments("<...targets>")
.option("-i, --interactive", "Interactive mode", {
.option("-i, --interactive", "Interactive mode (Not available on MacOS)", {
standalone: true,
})
.option("-v, --verbose", "Increase verbosity")
Expand Down Expand Up @@ -57,6 +57,10 @@ await new Command()
const procs: string[] = [];

if (opts.interactive) {
if (Deno.build.os === "darwin") {
console.error("Not implemented on macos");
Deno.exit(1);
}
// list processes
const pList = await procList();
const pickedProcesses: string[] = await Checkbox.prompt({
Expand Down
1 change: 1 addition & 0 deletions deno.jsonc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"tasks": {
"test": "deno test --allow-run --allow-net",
"testm": "deno run --allow-run --allow-net ./src/tests/utils.ts",
"dev": "deno run --allow-run --allow-net ./cli.ts",
"release": "deno run -A https://deno.land/x/release_up@0.5.0/cli.ts --regex \"(?<=@)(.*)(?=\/cli)\" --github --versionFile --changelog"
}
Expand Down
1 change: 1 addition & 0 deletions deps_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {
assert,
assertEquals,
assertNotEquals,
} from "https://deno.land/std@0.170.0/testing/asserts.ts";
export { delay } from "https://deno.land/std@0.170.0/async/mod.ts";
Expand Down
2 changes: 1 addition & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

To release a new version run the below:

`deno task release`
`deno task release`
1 change: 1 addition & 0 deletions src/dkill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export async function dkill(
if (!opts?.dryrun) {
const killedPids = KillPids(
allPidsToKill.map((pidItem) => pidItem.pid),
{ verbose: opts?.verbose },
);
allPidsToKill = allPidsToKill.map((pidItem) => ({
...pidItem,
Expand Down
58 changes: 13 additions & 45 deletions src/portToPid.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
grepPortLinux,
grepPortMacOS,
grepPortWindows,
} from "./utils/grepPort.ts";
import { runCmd } from "./utils/runCmd.ts";

/**
Expand All @@ -15,21 +20,7 @@ export async function portToPid(port: number): Promise<number[]> {
`netstat -nao | findstr :${port}`,
]);

// outstring example
// TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 28392
const parsedLines = outString
.split("\n")
.map((line) => line.match(/\S+/g) || []);

// parsedLines
// [ [ "TCP", "0.0.0.0:3000", "0.0.0.0:0", "LISTENING", "28392" ], [] ]

const pidColumnsIndex = 4;

const pids = parsedLines
.filter((arr) => arr.length !== 0) // filter last line
.map((arr) => +arr[pidColumnsIndex]) // extract pids based on columns
.filter((pid) => Number.isInteger(pid) && pid !== 0); // ensure they are numbers. pid 0 can be ignored
const pids = grepPortWindows(outString);

return [...new Set(pids)]; // remove duplicates;
} else if (os === "linux") {
Expand All @@ -38,38 +29,15 @@ export async function portToPid(port: number): Promise<number[]> {
// -n: provide local address
const outString = await runCmd(["ss", "-lnp"]);

// outstring example
// tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("deno", pid=200, fd=12))
const parsedLines = outString
.split("\n")
.map((line) => line.match(/\S+/g) || []);
const pids = grepPortLinux(outString, port);

// parsedLines
// [ [ "LISTEN", "0", "128", "0.0.0.0:8080", "0.0.0.0:*", users:(("deno", pid=200, fd=12)) ], [] ]

const portColumnIndex = 4;
const pidColumnsIndex = 6;

// remove first row of titles
parsedLines.shift();
return [...new Set(pids)]; // remove duplicates;
} else if (os === "darwin") {
const outString = await runCmd(["lsof", "-nwP", `-iTCP:${port}`]);

const pids = parsedLines
.filter((arr) => arr.length !== 0) // filter last line
.filter((arr) => {
const localAddrArr = arr[portColumnIndex].split(":");
return localAddrArr.length > 0 ? +localAddrArr[1] === port : false;
}) // filter connection for the targetted port
.map((arr) => {
// arr[pidColumnsIndex] should be like:
// users:(("deno", pid=200, fd=12))
const strArr = arr[pidColumnsIndex].match(/pid=(.*?),/);
if (!strArr) {
console.log("Line with issues", arr);
throw Error("Invalid parsing");
}
return +strArr[1];
}) // extract pids based on columns
.filter((pid) => Number.isInteger(pid) && pid !== 0); // ensure they are numbers. pid 0 can be ignored
// COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
// deno 1407 runner 13u IPv4 0x85fdd397dc6713cf 0t0 TCP *:8081 (LISTEN)
const pids = grepPortMacOS(outString);

return [...new Set(pids)]; // remove duplicates;
} else {
Expand Down
5 changes: 4 additions & 1 deletion src/procList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { runCmd } from "./utils/runCmd.ts";
import { PidItem } from "./utils/types.ts";

/**
* list all process running
* list all process running.
* WARNING. DO NOT USE FOR macos (It returns nothing)
* @returns {Promise} Array of Pid infos
*/
export async function procList(): Promise<PidItem[]> {
Expand Down Expand Up @@ -80,6 +81,8 @@ export async function procList(): Promise<PidItem[]> {
.forEach((item) => {
resultsObject[item.pid] = { ...resultsObject[item.pid], cmd: item.cmd };
});
} else if (os === "darwin") {
// TODO: skipped for now
} else {
throw Error("Platform not supported yet");
}
Expand Down
10 changes: 7 additions & 3 deletions src/tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* webserver.ts
*/
import { serve } from "../../deps_test.ts";
import { delay, serve } from "../../deps_test.ts";

const rawPort : string | number = Deno.args[0] ?? 8080;
const port = Number(rawPort)
const rawPort: string | number = Deno.args[0] ?? 8080;
const port = Number(rawPort);

const handler = (request: Request): Response => {
let body = "Your user-agent is:\n\n";
Expand All @@ -14,4 +14,8 @@ const handler = (request: Request): Response => {
};

console.log(`HTTP webserver running. Access it at: http://localhost:${port}/`);
delay(25000).then(() => {
Deno.exit(5);
});

await serve(handler, { port });
15 changes: 15 additions & 0 deletions src/utils/grepPort.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { assertEquals } from "../../deps_test.ts";
import { grepPortMacOS, grepPortWindows } from "./grepPort.ts";

Deno.test("assertMinVersion", () => {
// windows
const outw =
"TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 28392";
assertEquals(grepPortWindows(outw), [28392]);

// macOS
const outmac =
`COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
deno 1407 runner 13u IPv4 0x85fdd397dc6713cf 0t0 TCP *:8081 (LISTEN)`;
assertEquals(grepPortMacOS(outmac), [1407]);
});
Loading

0 comments on commit d3e2290

Please sign in to comment.