Actions IO provides essential filesystem operations for CLI scenarios and GitHub Actions workflows. It offers TypeScript implementations of common Unix commands with cross-platform compatibility, proper error handling, and async/await support.
npm install @actions/ioimport { cp, mv, rmRF, mkdirP, which, findInPath } from "@actions/io";
import type { CopyOptions, MoveOptions } from "@actions/io";For CommonJS:
const { cp, mv, rmRF, mkdirP, which, findInPath } = require("@actions/io");import { cp, mv, rmRF, mkdirP, which } from "@actions/io";
// Create directories recursively
await mkdirP("path/to/nested/directory");
// Copy files or directories
await cp("source/file.txt", "dest/file.txt");
await cp("source/directory", "dest/directory", { recursive: true });
// Move files or directories
await mv("old/path", "new/path");
// Remove files or directories recursively
await rmRF("path/to/remove");
// Find executables in PATH
const pythonPath = await which("python", true);
console.log(`Python found at: ${pythonPath}`);Actions IO is built around cross-platform filesystem operations:
cp, mv, rm -rf, mkdir -p, and which functionalityCopy files or directories with configurable options for recursion, force overwrite, and source directory handling.
/**
* Copies a file or folder with configurable options
* @param source - Source path to copy from
* @param dest - Destination path to copy to
* @param options - Optional copy configuration
*/
function cp(
source: string,
dest: string,
options: CopyOptions = {}
): Promise<void>;
interface CopyOptions {
/** Whether to recursively copy all subdirectories. Defaults to false */
recursive?: boolean;
/** Whether to overwrite existing files in the destination. Defaults to true */
force?: boolean;
/** Whether to copy the source directory along with all the files. Only takes effect when recursive=true and copying a directory. Default is true */
copySourceDirectory?: boolean;
}Usage Examples:
import { cp } from "@actions/io";
// Copy a single file
await cp("source.txt", "destination.txt");
// Copy a file without overwriting if destination exists
await cp("source.txt", "dest.txt", { force: false });
// Copy directory recursively
await cp("src/", "build/", { recursive: true });
// Copy directory contents without creating source directory at destination
await cp("src/", "build/", {
recursive: true,
copySourceDirectory: false
});Move files or directories to new locations with optional force overwrite.
/**
* Moves a path (file or directory) to a new location
* @param source - Source path to move from
* @param dest - Destination path to move to
* @param options - Optional move configuration
*/
function mv(
source: string,
dest: string,
options: MoveOptions = {}
): Promise<void>;
interface MoveOptions {
/** Whether to overwrite existing files in the destination. Defaults to true */
force?: boolean;
}Usage Examples:
import { mv } from "@actions/io";
// Move a file
await mv("old-name.txt", "new-name.txt");
// Move without overwriting existing destination
await mv("source.txt", "existing-dest.txt", { force: false });
// Move directory
await mv("old-directory/", "new-directory/");Remove files or directories recursively with force, equivalent to rm -rf command.
/**
* Remove a path recursively with force (equivalent to rm -rf)
* @param inputPath - Path to remove
*/
function rmRF(inputPath: string): Promise<void>;Usage Examples:
import { rmRF } from "@actions/io";
// Remove a file
await rmRF("unwanted-file.txt");
// Remove directory and all contents
await rmRF("temp-directory/");
// Remove with special characters (safe on all platforms)
await rmRF("path/with spaces/file.txt");Create directories recursively, equivalent to mkdir -p command.
/**
* Make a directory recursively. Creates the full path with folders in between
* @param fsPath - Path to create
*/
function mkdirP(fsPath: string): Promise<void>;Usage Examples:
import { mkdirP } from "@actions/io";
// Create nested directories
await mkdirP("deeply/nested/directory/structure");
// Create directory (no-op if already exists)
await mkdirP("existing-directory");Find executable files in the system PATH, equivalent to which command.
/**
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
* @param tool - Name of the tool to find
* @param check - Whether to check if tool exists and throw if not found
* @returns Path to tool, or empty string if not found (when check=false)
*/
function which(tool: string, check?: boolean): Promise<string>;Usage Examples:
import { which } from "@actions/io";
// Find tool without throwing if not found
const nodePath = await which("node");
if (nodePath) {
console.log(`Node.js found at: ${nodePath}`);
}
// Find tool and throw error if not found
try {
const pythonPath = await which("python", true);
console.log(`Python found at: ${pythonPath}`);
} catch (error) {
console.error("Python not found in PATH");
}Find all occurrences of an executable in the system PATH.
/**
* Returns a list of all occurrences of the given tool on the system path
* @param tool - Name of the tool to find
* @returns Array of paths where the tool is found
*/
function findInPath(tool: string): Promise<string[]>;Usage Examples:
import { findInPath } from "@actions/io";
// Find all instances of a tool
const pythonPaths = await findInPath("python");
console.log("Python installations found:", pythonPaths);
// Check if tool exists anywhere in PATH
const gccPaths = await findInPath("gcc");
if (gccPaths.length > 0) {
console.log("GCC is available");
}All functions throw descriptive errors for common failure scenarios:
cp and mv throw when source doesn't existcp throws when trying to copy directory without recursive: true*, ", <, >, |which with check: true throws detailed platform-specific error messagesActions IO handles cross-platform differences automatically:
/ and \ appropriately.exe, .cmd, .bat extensions