or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

environment-state.mdindex.mdinput-output.mdjob-summaries.mdlogging-annotations.mdoidc-tokens.mdoutput-grouping.mdpath-utilities.mdplatform-detection.md
tile.json

path-utilities.mddocs/

Path Utilities

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.

Overview

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.

Capabilities

To POSIX Path

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'

To Win32 Path

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'

To Platform Path

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);

Common Patterns

Cross-Platform File Operations

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}`);
    }
  }
}

URL and Path Conversion

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.png

Build Path Handling

import { 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}`);
}

Path Normalization for Comparison

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`);
}

Archive and Extraction 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}`);
  });
}

Configuration File Path Handling

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');

Integration with Node.js Path Module

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}`;

Best Practices

  1. Use toPlatformPath() for file system operations: Always convert paths to platform format before reading/writing files
  2. Use toPosixPath() for logging and URLs: Consistent POSIX format in logs and web URLs
  3. Normalize paths for comparison: Convert to same format (usually POSIX) before comparing paths
  4. Store paths in POSIX format: When serializing configurations, use POSIX format for cross-platform compatibility
  5. Convert at boundaries: Convert paths at the interface between your code and external systems
  6. Document path formats: Clearly indicate whether functions expect/return platform-specific or POSIX paths