Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wait actual end of CompileTools.runAction #1521

Merged
merged 3 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 157 additions & 148 deletions src/api/CompileTools.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@

import path from 'path';
import vscode, { CustomExecution, EventEmitter, Pseudoterminal, TaskGroup, TaskPanelKind, WorkspaceFolder, tasks, window, workspace } from 'vscode';
import { GlobalConfiguration } from './Configuration';
import { CustomUI } from './CustomUI';
import { getLocalActions } from './local/actions';
import { getEnvConfig } from './local/env';
import vscode, { CustomExecution, EventEmitter, Pseudoterminal, TaskGroup, WorkspaceFolder, tasks, window } from 'vscode';
import { parseFSOptions } from '../filesystems/qsys/QSysFs';
import { instance } from '../instantiate';
import { Action, CommandResult, DeploymentMethod, RemoteCommand, StandardIO } from '../typings';
import { GlobalConfiguration } from './Configuration';
import { CustomUI } from './CustomUI';
import IBMi from './IBMi';
import Instance from './Instance';
import { Tools } from './Tools';
import { EvfEventInfo, refreshDiagnosticsFromLocal, refreshDiagnosticsFromServer, registerDiagnostics } from './errors/diagnostics';
import { getLocalActions } from './local/actions';
import { DeployTools } from './local/deployTools';
import { getEnvConfig } from './local/env';

const NEWLINE = `\r\n`;

Expand Down Expand Up @@ -75,7 +74,7 @@ export namespace CompileTools {
return variables;
}

export async function runAction(instance: Instance, uri: vscode.Uri, customAction?: Action, method?: DeploymentMethod) {
export async function runAction(instance: Instance, uri: vscode.Uri, customAction?: Action, method?: DeploymentMethod) : Promise<boolean> {
const connection = instance.getConnection();
const config = instance.getConfig();
const content = instance.getContent();
Expand All @@ -84,12 +83,12 @@ export namespace CompileTools {

if (uriOptions.readonly) {
window.showWarningMessage(`Cannot run Actions against readonly objects.`);
return;
return false;
}

if (config?.readOnlyMode) {
window.showWarningMessage(`Cannot run Actions while readonly mode is enabled in the connection settings.`);
return;
return false;
}

const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
Expand Down Expand Up @@ -139,11 +138,11 @@ export namespace CompileTools {
workspaceId = deployResult;
} else {
vscode.window.showWarningMessage(`Action "${chosenAction.name}" was cancelled.`);
return;
return false;
}
}

let fromWorkspace: WorkspaceFolder|undefined;
let fromWorkspace: WorkspaceFolder | undefined;

if (chosenAction.type === `file` && vscode.workspace.workspaceFolders) {
fromWorkspace = vscode.workspace.workspaceFolders[workspaceId || 0];
Expand Down Expand Up @@ -294,164 +293,174 @@ export namespace CompileTools {
}

const command = replaceValues(chosenAction.command, variables);
const exitCode = await new Promise<number>(resolve =>
tasks.executeTask({
isBackground: true,
name: chosenAction.name,
definition: { type: `ibmi` },
scope: workspaceFolder,
source: 'IBM i',
presentationOptions: {
showReuseMessage: true,
clear: GlobalConfiguration.get<boolean>(`clearOutputEveryTime`)
},
problemMatchers: [],
runOptions: {},
group: TaskGroup.Build,
execution: new CustomExecution(async (e) => {
const writeEmitter = new vscode.EventEmitter<string>();
const closeEmitter = new vscode.EventEmitter<number>();

closeEmitter.event(resolve);

const term: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: async (initialDimensions: vscode.TerminalDimensions | undefined) => {
let successful = false;
try {
const commandResult = await runCommand(instance, {
title: chosenAction.name,
environment,
command,
env: Object.fromEntries(variables),
}, writeEmitter);

await tasks.executeTask({
isBackground: true,
name: chosenAction.name,
definition: {type: `ibmi`},
scope: workspaceFolder,
source: 'IBM i',
presentationOptions: {
showReuseMessage: true,
clear: GlobalConfiguration.get<boolean>(`clearOutputEveryTime`)
},
problemMatchers: [],
runOptions: {},
group: TaskGroup.Build,
execution: new CustomExecution(async (e) => {
const writeEmitter = new vscode.EventEmitter<string>();
const closeEmitter = new vscode.EventEmitter<number>();

const term: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: async (initialDimensions: vscode.TerminalDimensions | undefined) => {
try {
const commandResult = await runCommand(instance, {
title: chosenAction.name,
environment,
command,
env: Object.fromEntries(variables),
}, writeEmitter);

const useLocalEvfevent = fromWorkspace && chosenAction.postDownload && chosenAction.postDownload.includes(`.evfevent`);

if (commandResult) {
const isIleCommand = environment === `ile`;

const possibleObject = getObjectFromCommand(commandResult.command);
if (isIleCommand && possibleObject) {
Object.assign(evfeventInfo, possibleObject);
}

const actionName = (isIleCommand && possibleObject ? `${chosenAction.name} for ${evfeventInfo.library}/${evfeventInfo.object}` : chosenAction.name);

if (commandResult.code === 0 || commandResult.code === null) {
vscode.window.showInformationMessage(`Action ${actionName} was successful.`);
} else {
vscode.window.showErrorMessage(
`Action ${actionName} was not successful.`
)
}

writeEmitter.fire(NEWLINE);

if (useLocalEvfevent) {
writeEmitter.fire(`Fetching errors from .evfevent.${NEWLINE}`);

}
else if (evfeventInfo.object && evfeventInfo.library) {
if (command.includes(`*EVENTF`)) {
writeEmitter.fire(`Fetching errors for ${evfeventInfo.library}/${evfeventInfo.object}.` + NEWLINE);
refreshDiagnosticsFromServer(instance, evfeventInfo);
const useLocalEvfevent = fromWorkspace && chosenAction.postDownload && chosenAction.postDownload.includes(`.evfevent`);

if (commandResult) {
const isIleCommand = environment === `ile`;

const possibleObject = getObjectFromCommand(commandResult.command);
if (isIleCommand && possibleObject) {
Object.assign(evfeventInfo, possibleObject);
}

const actionName = (isIleCommand && possibleObject ? `${chosenAction.name} for ${evfeventInfo.library}/${evfeventInfo.object}` : chosenAction.name);

if (commandResult.code === 0 || commandResult.code === null) {
vscode.window.showInformationMessage(`Action ${actionName} was successful.`);
successful = true;
} else {
writeEmitter.fire(`*EVENTF not found in command string. Not fetching errors for ${evfeventInfo.library}/${evfeventInfo.object}.` + NEWLINE);
vscode.window.showErrorMessage(
`Action ${actionName} was not successful.`
)
}

writeEmitter.fire(NEWLINE);

if (useLocalEvfevent) {
writeEmitter.fire(`Fetching errors from .evfevent.${NEWLINE}`);

}
else if (evfeventInfo.object && evfeventInfo.library) {
if (command.includes(`*EVENTF`)) {
writeEmitter.fire(`Fetching errors for ${evfeventInfo.library}/${evfeventInfo.object}.` + NEWLINE);
refreshDiagnosticsFromServer(instance, evfeventInfo);
} else {
writeEmitter.fire(`*EVENTF not found in command string. Not fetching errors for ${evfeventInfo.library}/${evfeventInfo.object}.` + NEWLINE);
}
}
}
}

if (chosenAction.type === `file` && chosenAction.postDownload?.length) {
if (fromWorkspace) {
const client = connection.client;
const remoteDir = config.homeDirectory;
const localDir = fromWorkspace.uri.path;

// First, we need to create or clear the relative directories in the workspace
// in case they don't exist. For example, if the path is `.logs/joblog.json`
// then we would need to create `.logs`.
const downloadDirectories = chosenAction.postDownload.map(path.parse)
.map(pathInfo => pathInfo.dir || pathInfo.base) //Get directories or files' parent directory
.filter(Tools.distinct) //Remove duplicates
.map(downloadDirectory => vscode.Uri.parse((path.posix.join(localDir, downloadDirectory)))); //Create local Uri path

for (const downloadPath of downloadDirectories) {
try {
const stat = await vscode.workspace.fs.stat(downloadPath); //Check if target exists
if(stat.type !== vscode.FileType.Directory){
if(await vscode.window.showWarningMessage(`${downloadPath} exists but is a file.`, "Delete and create directory")){
await vscode.workspace.fs.delete(downloadPath);

if (chosenAction.type === `file` && chosenAction.postDownload?.length) {
if (fromWorkspace) {
const client = connection.client;
const remoteDir = config.homeDirectory;
const localDir = fromWorkspace.uri.path;

// First, we need to create or clear the relative directories in the workspace
// in case they don't exist. For example, if the path is `.logs/joblog.json`
// then we would need to create `.logs`.
const downloadDirectories = chosenAction.postDownload.map(path.parse)
.map(pathInfo => pathInfo.dir || pathInfo.base) //Get directories or files' parent directory
.filter(Tools.distinct) //Remove duplicates
.map(downloadDirectory => vscode.Uri.parse((path.posix.join(localDir, downloadDirectory)))); //Create local Uri path

for (const downloadPath of downloadDirectories) {
try {
const stat = await vscode.workspace.fs.stat(downloadPath); //Check if target exists
if (stat.type !== vscode.FileType.Directory) {
if (await vscode.window.showWarningMessage(`${downloadPath} exists but is a file.`, "Delete and create directory")) {
await vscode.workspace.fs.delete(downloadPath);
throw new Error("Create directory");
}
}
else if (stat.type !== vscode.FileType.Directory) {
await vscode.workspace.fs.delete(downloadPath, { recursive: true });
throw new Error("Create directory");
}
}
else if(stat.type !== vscode.FileType.Directory){
await vscode.workspace.fs.delete(downloadPath, {recursive: true});
throw new Error("Create directory");
catch (e) {
//Either fs.stat did not find the folder or it wasn't a folder and it's been deleted above
try {
await vscode.workspace.fs.createDirectory(downloadPath)
}
catch (error) {
vscode.window.showWarningMessage(`Failed to create download path ${downloadPath}: ${error}`);
console.log(error);
closeEmitter.fire(1);
}
}
}
catch(e){
//Either fs.stat did not find the folder or it wasn't a folder and it's been deleted above
try {
await vscode.workspace.fs.createDirectory(downloadPath)

// Then we download the files that is specified.
const downloads = chosenAction.postDownload.map(
async (downloadPath) => {
const localPath = vscode.Uri.parse(path.posix.join(localDir, downloadPath)).fsPath;
const remotePath = path.posix.join(remoteDir, downloadPath);

if (await content.isDirectory(remotePath)) {
return client.getDirectory(localPath, remotePath);
} else {
return client.getFile(localPath, remotePath);
}
}
catch(error){
vscode.window.showWarningMessage(`Failed to create download path ${downloadPath}: ${error}`);
console.log(error);
);

Promise.all(downloads)
.then(async result => {
// Done!
writeEmitter.fire(`Downloaded files as part of Action: ${chosenAction.postDownload!.join(`, `)}\n`);

// Process locally downloaded evfevent files:
if (useLocalEvfevent) {
refreshDiagnosticsFromLocal(instance, evfeventInfo);
}
})
.catch(error => {
vscode.window.showErrorMessage(`Failed to download files as part of Action.`);
writeEmitter.fire(`Failed to download a file after Action: ${error.message}\n`);
closeEmitter.fire(1);
}
}
});
}

// Then we download the files that is specified.
const downloads = chosenAction.postDownload.map(
async (downloadPath) => {
const localPath = vscode.Uri.parse(path.posix.join(localDir, downloadPath)).fsPath;
const remotePath = path.posix.join(remoteDir, downloadPath);

if (await content.isDirectory(remotePath)) {
return client.getDirectory(localPath, remotePath);
} else {
return client.getFile(localPath, remotePath);
}
}
);

Promise.all(downloads)
.then(async result => {
// Done!
writeEmitter.fire(`Downloaded files as part of Action: ${chosenAction.postDownload!.join(`, `)}\n`);

// Process locally downloaded evfevent files:
if (useLocalEvfevent) {
refreshDiagnosticsFromLocal(instance, evfeventInfo);
}
})
.catch(error => {
vscode.window.showErrorMessage(`Failed to download files as part of Action.`);
writeEmitter.fire(`Failed to download a file after Action: ${error.message}\n`);
closeEmitter.fire(1);
});
}

} catch (e) {
writeEmitter.fire(`${e}\n`);
vscode.window.showErrorMessage(`Action ${chosenAction} for ${evfeventInfo.library}/${evfeventInfo.object} failed. (internal error).`);
closeEmitter.fire(1);
}

} catch (e) {
writeEmitter.fire(`${e}\n`);
vscode.window.showErrorMessage(`Action ${chosenAction} for ${evfeventInfo.library}/${evfeventInfo.object} failed. (internal error).`);
closeEmitter.fire(1);
}

closeEmitter.fire(0);
},
close: function (): void { }
};
closeEmitter.fire(successful ? 0 : 1);
},
close: function (): void { }
};

return term;
return term;
})
})
});
}
);

return exitCode === 0;
}
else{
return false;
}
} else {
//No compile commands
vscode.window.showErrorMessage(`No compile commands found for ${uri.scheme}-${extension}.`);
return false;
}
}
else {
Expand Down
Loading