Improve desktop script runs with timestamps & logs
Improve script execution in the desktop app by introducing timestamped filenames and detailed logging. These changes aim to facilitate easier debugging, auditing and overall better user experience. Key changes: - Add timestamps in filenames for temporary files to aid in troubleshooting and auditing. - Add application logging throughout the script execution process to enhance troubleshooting capabilities. Other supporting changes: - Refactor `TemporaryFileCodeRunner` with subfunctions for improved readability, maintenance, reusability and extensibility. - Refactor unit tests for `TemporaryFileCodeRunner` for improved granularity and simplicity. - Create centralized definition of supported operating systems by privacy.sexy to ensure robust and consistent test case creation. - Simplify the `runCode` method by removing the file extension parameter; now handled internally by `FileNameGenerator`.
This commit is contained in:
@@ -1,37 +1,88 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { CodeRunner } from '@/application/CodeRunner';
|
||||
import { Logger } from '@/application/Common/Log/Logger';
|
||||
import { ElectronLogger } from '../Log/ElectronLogger';
|
||||
import { SystemOperations } from './SystemOperations/SystemOperations';
|
||||
import { createNodeSystemOperations } from './SystemOperations/NodeSystemOperations';
|
||||
import { generateOsTimestampedFileName } from './FileNameGenerator';
|
||||
|
||||
export type FileNameGenerator = (os: OperatingSystem) => string;
|
||||
|
||||
export class TemporaryFileCodeRunner implements CodeRunner {
|
||||
constructor(
|
||||
private readonly system: SystemOperations = createNodeSystemOperations(),
|
||||
private readonly fileNameGenerator: FileNameGenerator = generateOsTimestampedFileName,
|
||||
private readonly logger: Logger = ElectronLogger,
|
||||
) { }
|
||||
|
||||
public async runCode(
|
||||
code: string,
|
||||
folderName: string,
|
||||
fileExtension: string,
|
||||
tempScriptFolderName: string,
|
||||
os: OperatingSystem,
|
||||
): Promise<void> {
|
||||
const dir = this.system.location.combinePaths(
|
||||
this.logger.info(`Starting running code for OS: ${OperatingSystem[os]}`);
|
||||
try {
|
||||
const fileName = this.fileNameGenerator(os);
|
||||
const filePath = await this.createTemporaryFile(fileName, tempScriptFolderName, code);
|
||||
await this.executeFile(filePath, os);
|
||||
this.logger.info(`Successfully executed script at ${filePath}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error executing script: ${error.message}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async createTemporaryFile(
|
||||
fileName: string,
|
||||
tempScriptFolderName: string,
|
||||
contents: string,
|
||||
): Promise<string> {
|
||||
const directoryPath = this.system.location.combinePaths(
|
||||
this.system.operatingSystem.getTempDirectory(),
|
||||
folderName,
|
||||
tempScriptFolderName,
|
||||
);
|
||||
await this.system.fileSystem.createDirectory(dir, true);
|
||||
const filePath = this.system.location.combinePaths(dir, `run.${fileExtension}`);
|
||||
await this.system.fileSystem.writeToFile(filePath, code);
|
||||
await this.system.fileSystem.setFilePermissions(filePath, '755');
|
||||
await this.createDirectoryIfNotExists(directoryPath);
|
||||
const filePath = this.system.location.combinePaths(directoryPath, fileName);
|
||||
await this.createFile(filePath, contents);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private async createFile(filePath: string, contents: string): Promise<void> {
|
||||
this.logger.info(`Creating file at ${filePath}, size: ${contents.length} characters`);
|
||||
await this.system.fileSystem.writeToFile(filePath, contents);
|
||||
this.logger.info(`File created successfully at ${filePath}`);
|
||||
}
|
||||
|
||||
private async createDirectoryIfNotExists(directoryPath: string): Promise<void> {
|
||||
this.logger.info(`Checking and ensuring directory exists: ${directoryPath}`);
|
||||
await this.system.fileSystem.createDirectory(directoryPath, true);
|
||||
this.logger.info(`Directory confirmed at: ${directoryPath}`);
|
||||
}
|
||||
|
||||
private async executeFile(filePath: string, os: OperatingSystem): Promise<void> {
|
||||
await this.setFileExecutablePermissions(filePath);
|
||||
const command = getExecuteCommand(filePath, os);
|
||||
await this.executeCommand(command);
|
||||
}
|
||||
|
||||
private async setFileExecutablePermissions(filePath: string): Promise<void> {
|
||||
this.logger.info(`Setting execution permissions for file at ${filePath}`);
|
||||
await this.system.fileSystem.setFilePermissions(filePath, '755');
|
||||
this.logger.info(`Execution permissions set successfully for ${filePath}`);
|
||||
}
|
||||
|
||||
private async executeCommand(command: string): Promise<void> {
|
||||
this.logger.info(`Executing command: ${command}`);
|
||||
await this.system.command.execute(command);
|
||||
this.logger.info('Command executed successfully.');
|
||||
}
|
||||
}
|
||||
|
||||
function getExecuteCommand(
|
||||
scriptPath: string,
|
||||
currentOperatingSystem: OperatingSystem,
|
||||
os: OperatingSystem,
|
||||
): string {
|
||||
switch (currentOperatingSystem) {
|
||||
switch (os) {
|
||||
case OperatingSystem.Linux:
|
||||
return `x-terminal-emulator -e '${scriptPath}'`;
|
||||
case OperatingSystem.macOS:
|
||||
@@ -42,6 +93,6 @@ function getExecuteCommand(
|
||||
case OperatingSystem.Windows:
|
||||
return scriptPath;
|
||||
default:
|
||||
throw Error(`unsupported os: ${OperatingSystem[currentOperatingSystem]}`);
|
||||
throw Error(`unsupported os: ${OperatingSystem[os]}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user