Skip to content

Commit

Permalink
feat: use asynchronous version of the exec file function (#522)
Browse files Browse the repository at this point in the history
* feat: add `exec` function

Signed-off-by: Alfi Maulana <[email protected]>

* feat: use `exec` function in `configureProject` and `buildProject` functions

Signed-off-by: Alfi Maulana <[email protected]>

---------

Signed-off-by: Alfi Maulana <[email protected]>
  • Loading branch information
threeal authored Nov 21, 2024
1 parent 5dcae62 commit 478382d
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 48 deletions.
60 changes: 46 additions & 14 deletions dist/action.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { getContext } from "./context.js";
try {
const context = getContext();

configureProject(context);
await configureProject(context);

await setOutput("build-dir", context.buildDir);

if (context.build.enabled) {
buildProject(context);
await buildProject(context);
}
} catch (err) {
logError(err);
Expand Down
32 changes: 12 additions & 20 deletions src/cmake.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const defaultContext: Context = {
},
};

jest.unstable_mockModule("node:child_process", () => ({
execFileSync: jest.fn(),
jest.unstable_mockModule("./exec.js", () => ({
exec: jest.fn(),
}));

describe("configure a CMake project", () => {
Expand Down Expand Up @@ -101,18 +101,14 @@ describe("configure a CMake project", () => {
for (const testCase of testCases) {
it(`should execute the correct command ${testCase.name}`, async () => {
const { configureProject } = await import("./cmake.js");
const { execFileSync } = await import("node:child_process");
const { exec } = await import("./exec.js");

jest.mocked(execFileSync).mockReset();
jest.mocked(exec).mockReset();

configureProject({ ...defaultContext, ...testCase.context });
await configureProject({ ...defaultContext, ...testCase.context });

expect(execFileSync).toHaveBeenCalledTimes(1);
expect(execFileSync).toHaveBeenLastCalledWith(
"cmake",
testCase.expectedArgs,
{ stdio: "inherit" },
);
expect(exec).toHaveBeenCalledTimes(1);
expect(exec).toHaveBeenLastCalledWith("cmake", testCase.expectedArgs);
});
}
});
Expand Down Expand Up @@ -149,18 +145,14 @@ describe("build a CMake project", () => {
for (const testCase of testCases) {
it(`should execute the correct command ${testCase.name}`, async () => {
const { buildProject } = await import("./cmake.js");
const { execFileSync } = await import("node:child_process");
const { exec } = await import("./exec.js");

jest.mocked(execFileSync).mockReset();
jest.mocked(exec).mockReset();

buildProject({ ...defaultContext, ...testCase.context });
await buildProject({ ...defaultContext, ...testCase.context });

expect(execFileSync).toHaveBeenCalledTimes(1);
expect(execFileSync).toHaveBeenLastCalledWith(
"cmake",
testCase.expectedArgs,
{ stdio: "inherit" },
);
expect(exec).toHaveBeenCalledTimes(1);
expect(exec).toHaveBeenLastCalledWith("cmake", testCase.expectedArgs);
});
}
});
30 changes: 18 additions & 12 deletions src/cmake.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { execFileSync } from "node:child_process";
import { exec } from "./exec.js";
import type { Context } from "./context.js";

/**
* Configures the build system of a CMake project.
* Configures the build system for a CMake project.
*
* @param context - The action context.
* Constructs and runs the `cmake` command to configure the project with the specified
* source directory, build directory, generator, options, and additional arguments.
*
* @param context - The action context containing configuration details.
* @returns A promise that resolves when the build system is successfully configured.
*/
export function configureProject(context: Context): void {
export async function configureProject(context: Context): Promise<void> {
const configureArgs = [];

if (context.sourceDir) {
Expand All @@ -16,22 +20,24 @@ export function configureProject(context: Context): void {
configureArgs.push("-B", context.buildDir);

if (context.configure.generator) {
configureArgs.push(...["-G", context.configure.generator]);
configureArgs.push("-G", context.configure.generator);
}

configureArgs.push(...context.configure.options.map((opt) => "-D" + opt));
configureArgs.push(...context.configure.args);

execFileSync("cmake", configureArgs, { stdio: "inherit" });
await exec("cmake", configureArgs);
}

/**
* Build a CMake project.
* Builds a CMake project.
*
* Runs the `cmake --build` command to build the project using the specified
* build directory and additional arguments.
*
* @param context - The action context.
* @param context - The action context containing build details.
* @returns A promise that resolves when the project is successfully built.
*/
export function buildProject(context: Context): void {
execFileSync("cmake", ["--build", context.buildDir, ...context.build.args], {
stdio: "inherit",
});
export async function buildProject(context: Context): Promise<void> {
await exec("cmake", ["--build", context.buildDir, ...context.build.args]);
}
13 changes: 13 additions & 0 deletions src/exec.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { exec } from "./exec.js";

describe("execute commands", () => {
it("should successfully execute a command", async () => {
await exec("node", ["--version"]);
});

it("should fail to execute a command", async () => {
await expect(exec("node", ["--invalid"])).rejects.toThrow(
"Command exited with status code 9",
);
});
});
26 changes: 26 additions & 0 deletions src/exec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { spawn } from "node:child_process";

/**
* Executes a command with the given arguments.
*
* The command is executed with `stdin` ignored and both `stdout` and `stderr` inherited by the parent process.
*
* @param command The command to execute.
* @param args The arguments to pass to the command.
* @returns A promise that resolves when the command exits successfully or rejects if it exits with a non-zero status code or encounters an error.
*/
export async function exec(command: string, args: string[]): Promise<void> {
return new Promise<void>((resolve, reject) => {
const proc = spawn(command, args, {
stdio: ["ignore", "inherit", "inherit"],
});
proc.on("error", reject);
proc.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command exited with status code ${code}`));
}
});
});
}

0 comments on commit 478382d

Please sign in to comment.