Cross-platform path manipulation utilities for handling file paths across different operating systems. These utilities ensure consistent path handling whether your action runs on Windows, macOS, or Linux runners.
Path utilities in @actions/core provide functions to convert file paths between different formats, ensuring that your action works correctly regardless of the runner's operating system. This is particularly important when working with paths that will be used across different systems or when interfacing with tools that expect specific path formats.
Converts any path to POSIX format (Unix/Linux style with forward slashes).
/**
* toPosixPath converts the given path to the posix form. On Windows, \\ will be
* replaced with /.
* @param pth - Path to transform
* @returns String in POSIX path format
*/
function toPosixPath(pth: string): string;Usage Examples:
import { toPosixPath } from '@actions/core';
// Convert Windows path to POSIX
const windowsPath = 'C:\\Users\\runner\\project\\src\\app.js';
const posixPath = toPosixPath(windowsPath);
console.log(posixPath); // 'C:/Users/runner/project/src/app.js'
// Convert mixed separators to POSIX
const mixedPath = 'project\\src/components\\Button.tsx';
const cleanPath = toPosixPath(mixedPath);
console.log(cleanPath); // 'project/src/components/Button.tsx'
// Already POSIX paths are unchanged
const alreadyPosix = '/home/runner/work/repo/file.txt';
const stillPosix = toPosixPath(alreadyPosix);
console.log(stillPosix); // '/home/runner/work/repo/file.txt'
// Convert relative paths
const relativePath = '.\\dist\\index.js';
const posixRelative = toPosixPath(relativePath);
console.log(posixRelative); // './dist/index.js'Converts any path to Windows format (with backslashes).
/**
* toWin32Path converts the given path to the win32 form. On Linux, / will be
* replaced with \\.
* @param pth - Path to transform
* @returns String in Win32 path format
*/
function toWin32Path(pth: string): string;Usage Examples:
import { toWin32Path } from '@actions/core';
// Convert POSIX path to Windows
const posixPath = '/home/runner/work/repo/src/app.js';
const windowsPath = toWin32Path(posixPath);
console.log(windowsPath); // '\\home\\runner\\work\\repo\\src\\app.js'
// Convert typical Unix paths
const unixPath = 'project/src/components/Button.tsx';
const winPath = toWin32Path(unixPath);
console.log(winPath); // 'project\\src\\components\\Button.tsx'
// Convert mixed separators to Windows
const mixedPath = 'C:/Program Files/MyApp\\config.json';
const cleanWinPath = toWin32Path(mixedPath);
console.log(cleanWinPath); // 'C:\\Program Files\\MyApp\\config.json'
// Already Windows paths are unchanged
const alreadyWindows = 'C:\\Users\\runner\\project\\file.txt';
const stillWindows = toWin32Path(alreadyWindows);
console.log(stillWindows); // 'C:\\Users\\runner\\project\\file.txt'Converts any path to the current platform's native format using the appropriate path separator.
/**
* toPlatformPath converts the given path to a platform-specific path. It does
* this by replacing instances of / and \ with the platform-specific path
* separator.
* @param pth - The path to platformize
* @returns String in platform-specific path format
*/
function toPlatformPath(pth: string): string;Usage Examples:
import { toPlatformPath } from '@actions/core';
// Convert path to current platform format
const anyPath = 'project/src\\components/Button.tsx';
const platformPath = toPlatformPath(anyPath);
// On Windows: 'project\\src\\components\\Button.tsx'
// On Unix/Linux/macOS: 'project/src/components/Button.tsx'
console.log(platformPath);
// Handle absolute paths
const absolutePath = 'C:/Program Files/MyApp\\bin/tool.exe';
const platformAbsolute = toPlatformPath(absolutePath);
// On Windows: 'C:\\Program Files\\MyApp\\bin\\tool.exe'
// On Unix: 'C:/Program Files/MyApp/bin/tool.exe'
console.log(platformAbsolute);
// Relative paths
const relativePath = '../config\\settings.json';
const platformRelative = toPlatformPath(relativePath);
// On Windows: '..\\config\\settings.json'
// On Unix: '../config/settings.json'
console.log(platformRelative);import { toPlatformPath, toPosixPath, info } from '@actions/core';
import * as fs from 'fs';
import * as path from 'path';
async function processFiles(filePaths: string[]) {
for (const filePath of filePaths) {
// Convert to platform-specific path for file operations
const platformPath = toPlatformPath(filePath);
try {
// Check if file exists using platform path
if (fs.existsSync(platformPath)) {
info(`Processing file: ${platformPath}`);
// Read file content
const content = fs.readFileSync(platformPath, 'utf8');
// Process content...
const processed = processContent(content);
// Write back to file
fs.writeFileSync(platformPath, processed);
// Log using POSIX format for consistency in logs
info(`Completed processing: ${toPosixPath(filePath)}`);
} else {
warning(`File not found: ${platformPath}`);
}
} catch (error) {
error(`Error processing ${platformPath}: ${error.message}`);
}
}
}import { toPosixPath, toWin32Path, info } from '@actions/core';
function generateUrls(localPaths: string[]) {
const baseUrl = 'https://cdn.example.com/assets/';
const urls: string[] = [];
for (const localPath of localPaths) {
// Always use POSIX format for URLs
const posixPath = toPosixPath(localPath);
// Remove leading ./ if present
const cleanPath = posixPath.replace(/^\.\//, '');
// Create URL
const url = baseUrl + cleanPath;
urls.push(url);
info(`Local: ${localPath} -> URL: ${url}`);
}
return urls;
}
// Example usage
const localFiles = [
'.\\dist\\js\\app.js',
'./dist/css/styles.css',
'dist/images\\logo.png'
];
const cdnUrls = generateUrls(localFiles);
// Results in clean URLs like:
// https://cdn.example.com/assets/dist/js/app.js
// https://cdn.example.com/assets/dist/css/styles.css
// https://cdn.example.com/assets/dist/images/logo.pngimport { toPlatformPath, toPosixPath, exportVariable, info } from '@actions/core';
import * as path from 'path';
function setupBuildPaths() {
const sourcePaths = [
'src/main.ts',
'src\\components\\App.tsx',
'public/assets/images/logo.png',
'config\\webpack.config.js'
];
// Convert all paths to platform format for local operations
const platformPaths = sourcePaths.map(p => toPlatformPath(p));
// Set up environment variables with platform paths
exportVariable('SOURCE_DIR', toPlatformPath('src'));
exportVariable('BUILD_DIR', toPlatformPath('dist'));
exportVariable('PUBLIC_DIR', toPlatformPath('public'));
// Create build manifest with POSIX paths (for consistency)
const buildManifest = {
sources: sourcePaths.map(p => toPosixPath(p)),
timestamp: new Date().toISOString(),
platform: process.platform
};
// Write manifest using platform path
const manifestPath = toPlatformPath('dist/build-manifest.json');
fs.writeFileSync(manifestPath, JSON.stringify(buildManifest, null, 2));
info(`Build paths configured for platform: ${process.platform}`);
info(`Manifest written to: ${manifestPath}`);
}import { toPosixPath, info, warning } from '@actions/core';
function comparePaths(path1: string, path2: string): boolean {
// Normalize both paths to POSIX format for comparison
const normalized1 = toPosixPath(path1).toLowerCase();
const normalized2 = toPosixPath(path2).toLowerCase();
return normalized1 === normalized2;
}
function findDuplicatePaths(paths: string[]): string[] {
const seen = new Set<string>();
const duplicates: string[] = [];
for (const path of paths) {
const normalized = toPosixPath(path).toLowerCase();
if (seen.has(normalized)) {
duplicates.push(path);
warning(`Duplicate path detected: ${path}`);
} else {
seen.add(normalized);
}
}
return duplicates;
}
// Example usage
const inputPaths = [
'src/app.js',
'src\\app.js', // Same as above on Windows
'SRC/APP.JS', // Same if case-insensitive filesystem
'src/utils.js'
];
const duplicates = findDuplicatePaths(inputPaths);
if (duplicates.length > 0) {
warning(`Found ${duplicates.length} duplicate paths`);
}import { toPosixPath, toPlatformPath, info } from '@actions/core';
async function createArchive(files: string[], archiveName: string) {
// Normalize all file paths to POSIX for archive consistency
const archiveEntries = files.map(file => ({
localPath: toPlatformPath(file), // For reading from filesystem
archivePath: toPosixPath(file) // For storing in archive
}));
info(`Creating archive: ${archiveName}`);
for (const entry of archiveEntries) {
info(`Adding ${entry.localPath} as ${entry.archivePath}`);
// Read from local filesystem using platform path
const content = fs.readFileSync(entry.localPath);
// Add to archive using POSIX path (most archive formats expect this)
archive.addFile(entry.archivePath, content);
}
await archive.finalize();
info('Archive created successfully');
}
async function extractArchive(archivePath: string, extractToDir: string) {
const platformExtractDir = toPlatformPath(extractToDir);
info(`Extracting archive to: ${platformExtractDir}`);
await archive.extract(archivePath, (entryPath: string, content: Buffer) => {
// Convert archive path (usually POSIX) to platform path for extraction
const extractPath = path.join(platformExtractDir, toPlatformPath(entryPath));
// Ensure directory exists
const dir = path.dirname(extractPath);
fs.mkdirSync(dir, { recursive: true });
// Write file
fs.writeFileSync(extractPath, content);
info(`Extracted: ${entryPath} -> ${extractPath}`);
});
}import { toPlatformPath, toPosixPath, info, warning } from '@actions/core';
interface ConfigPaths {
source: string;
output: string;
includes: string[];
excludes: string[];
}
function normalizeConfigPaths(config: any): ConfigPaths {
return {
source: toPlatformPath(config.source || 'src'),
output: toPlatformPath(config.output || 'dist'),
includes: (config.includes || []).map((p: string) => toPlatformPath(p)),
excludes: (config.excludes || []).map((p: string) => toPlatformPath(p))
};
}
function serializeConfigPaths(config: ConfigPaths): any {
// Always serialize paths in POSIX format for cross-platform compatibility
return {
source: toPosixPath(config.source),
output: toPosixPath(config.output),
includes: config.includes.map(p => toPosixPath(p)),
excludes: config.excludes.map(p => toPosixPath(p))
};
}
// Example usage
const rawConfig = {
source: 'src\\typescript',
output: 'dist/js',
includes: ['**/*.ts', 'lib\\**\\*.js'],
excludes: ['**/*.test.ts', 'node_modules\\**']
};
const normalizedConfig = normalizeConfigPaths(rawConfig);
info('Configuration paths normalized for current platform');
// When saving configuration
const serializedConfig = serializeConfigPaths(normalizedConfig);
fs.writeFileSync('build-config.json', JSON.stringify(serializedConfig, null, 2));
info('Configuration saved with POSIX paths for cross-platform compatibility');The path utilities work well with Node.js built-in path module:
import { toPlatformPath, toPosixPath } from '@actions/core';
import * as path from 'path';
// Combine with Node.js path operations
const basePath = toPlatformPath('project/src');
const fileName = 'app.js';
const fullPath = path.join(basePath, fileName);
// For logging, convert back to POSIX for consistency
info(`Processing: ${toPosixPath(fullPath)}`);
// For URL generation, always use POSIX
const relativePath = path.relative(process.cwd(), fullPath);
const urlPath = toPosixPath(relativePath);
const assetUrl = `https://cdn.example.com/${urlPath}`;toPlatformPath() for file system operations: Always convert paths to platform format before reading/writing filestoPosixPath() for logging and URLs: Consistent POSIX format in logs and web URLs