diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a4ac5c..55e9a9d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,5 +26,6 @@ Thanks so much to everyone [who has contributed](https://github.com/SanjulaGanep * [@SanjulaGanepola](https://github.com/SanjulaGanepola) * [@ChristopherHX](https://github.com/ChristopherHX) * [@a11rew](https://github.com/a11rew) +* [@atoko](https://github.com/atoko) Want to see your name on this list? Join us and contribute! \ No newline at end of file diff --git a/src/act.ts b/src/act.ts index dee0a2c..6f531e0 100644 --- a/src/act.ts +++ b/src/act.ts @@ -664,17 +664,18 @@ export class Act { } // Initialize history for workspace - if (!this.historyManager.workspaceHistory[commandArgs.path]) { - this.historyManager.workspaceHistory[commandArgs.path] = []; - await this.storageManager.update(StorageKey.WorkspaceHistory, this.historyManager.workspaceHistory); + const workspaceHistory = await this.historyManager.getWorkspaceHistory(); + if (workspaceHistory[commandArgs.path] === undefined) { + workspaceHistory[commandArgs.path] = []; + await this.storageManager.update(StorageKey.WorkspaceHistory, workspaceHistory); } // Process task count suffix - const historyIndex = this.historyManager.workspaceHistory[commandArgs.path].length; - const matchingTasks = this.historyManager.workspaceHistory[commandArgs.path] + const historyIndex = (workspaceHistory[commandArgs.path] ?? []).length; + const matchingTasks = (workspaceHistory[commandArgs.path] ?? []) .filter(history => history.name === commandArgs.name) .sort((a, b) => b.count - a.count); - const count = matchingTasks && matchingTasks.length > 0 ? matchingTasks[0].count + 1 : 1; + const count = matchingTasks.length > 0 ? matchingTasks[0].count + 1 : 1; // Process log file and path const start = new Date(); @@ -753,6 +754,7 @@ export class Act { lastline = lines.pop() || ""; } + const workspaceHistory = (await this.historyManager.getWorkspaceHistory()); for await (const line of lines) { const dateString = new Date().toString(); @@ -796,11 +798,13 @@ export class Act { jobName = `${jobName} (${matrixValues})`; } - let jobIndex = this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs! + const jobHistory = workspaceHistory[commandArgs.path][historyIndex]; + const jobs = jobHistory.jobs ?? []; + let jobIndex = jobs .findIndex(job => job.name === jobName); if (jobIndex < 0) { // Add new job with setup step - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs!.push({ + jobs.push({ name: jobName, status: HistoryStatus.Running, date: { @@ -808,13 +812,17 @@ export class Act { }, steps: [] }); - jobIndex = this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs!.length - 1; + jobIndex = jobs.length - 1; } // Update step status in workspace history + const job = jobs[jobIndex]; if (parsedMessage.stepID) { let stepName: string; const stepId: string = parsedMessage.stepID[0]; + + const steps = job.steps ?? []; + if (parsedMessage.stage !== 'Main') { stepName = `${parsedMessage.stage} ${parsedMessage.step}`; } else { @@ -822,20 +830,20 @@ export class Act { // TODO: This forcefully sets any pre step to success. To be fixed with https://github.com/nektos/act/issues/2551 const preStepName = `Pre ${parsedMessage.step}`; - let preStepIndex = this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps! + let preStepIndex = steps .findIndex(step => step.id === stepId && step.name === preStepName); - if (preStepIndex > -1 && this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![preStepIndex].status === HistoryStatus.Running) { - - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![preStepIndex].status = HistoryStatus.Success; - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![preStepIndex].date.end = dateString; + const prestep = (job.steps! ?? [])[preStepIndex]; + if (preStepIndex > -1 && prestep?.status === HistoryStatus.Running) { + prestep.status = HistoryStatus.Success; + prestep.date.end = dateString; } } - let stepIndex = this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps! + let stepIndex = steps .findIndex(step => step.id === stepId && step.name === stepName); if (stepIndex < 0) { // Add new step - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps!.push({ + steps.push({ id: stepId, name: stepName, status: HistoryStatus.Running, @@ -843,21 +851,24 @@ export class Act { start: dateString } }); - stepIndex = this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps!.length - 1; + stepIndex = steps.length - 1; } if (parsedMessage.stepResult) { - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![stepIndex].status = - HistoryManager.stepResultToHistoryStatus(parsedMessage.stepResult); - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![stepIndex].date.end = dateString; + const step = steps[stepIndex]; + if (step) { + step.status = HistoryManager.stepResultToHistoryStatus(parsedMessage.stepResult); + step.date.end = dateString; + } + } } if (parsedMessage.jobResult) { - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].status = - HistoryManager.stepResultToHistoryStatus(parsedMessage.jobResult); - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].date.end = - dateString; + if (job) { + job.status = HistoryManager.stepResultToHistoryStatus(parsedMessage.jobResult); + job.date.end = dateString; + } } } } @@ -872,7 +883,7 @@ export class Act { writeEmitter.fire(`${message.trimEnd()}\r\n`); historyTreeDataProvider.refresh(); } - await this.storageManager.update(StorageKey.WorkspaceHistory, this.historyManager.workspaceHistory); + await this.storageManager.update(StorageKey.WorkspaceHistory, workspaceHistory); }; }; @@ -920,36 +931,47 @@ export class Act { const dateString = new Date().toString(); // Set execution status and end time in workspace history - if (this.historyManager.workspaceHistory[commandArgs.path][historyIndex].status === HistoryStatus.Running) { + const workspaceHistory = (await this.historyManager.getWorkspaceHistory()); + if (workspaceHistory[commandArgs.path][historyIndex].status === HistoryStatus.Running) { const jobAndStepStatus = (!code && code !== 0) ? HistoryStatus.Cancelled : HistoryStatus.Unknown; - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs?.forEach((job, jobIndex) => { - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps?.forEach((step, stepIndex) => { + workspaceHistory[commandArgs.path][historyIndex].jobs?.forEach((job, jobIndex) => { + workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps?.forEach((step, stepIndex) => { if (step.status === HistoryStatus.Running) { // Update status of all running steps - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![stepIndex].status = jobAndStepStatus; - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![stepIndex].date.end = dateString; + const step = workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].steps![stepIndex]; + if (step) { + step.status = jobAndStepStatus; + step.date.end = dateString; + } } }); if (job.status === HistoryStatus.Running) { // Update status of all running jobs - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].status = jobAndStepStatus; - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex].date.end = dateString; + const step = workspaceHistory[commandArgs.path][historyIndex].jobs![jobIndex]; + if (step) { + step.status = jobAndStepStatus; + step.date.end = dateString; + } } }); // Update history status if (code === 0) { - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].status = HistoryStatus.Success; + workspaceHistory[commandArgs.path][historyIndex].status = HistoryStatus.Success; } else if (!code) { - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].status = HistoryStatus.Cancelled; + workspaceHistory[commandArgs.path][historyIndex].status = HistoryStatus.Cancelled; } else { - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].status = HistoryStatus.Failed; + workspaceHistory[commandArgs.path][historyIndex].status = HistoryStatus.Failed; } } - this.historyManager.workspaceHistory[commandArgs.path][historyIndex].date.end = dateString; + const step = workspaceHistory[commandArgs.path][historyIndex]; + + if (step) { + step.date.end = dateString; + } historyTreeDataProvider.refresh(); - await this.storageManager.update(StorageKey.WorkspaceHistory, this.historyManager.workspaceHistory); + await this.storageManager.update(StorageKey.WorkspaceHistory, workspaceHistory); if (signal === 'SIGINT') { writeEmitter.fire(`\r\n${commandArgs.name} #${count} was interrupted.\r\n`); @@ -987,7 +1009,7 @@ export class Act { }); // Add new entry to workspace history - this.historyManager.workspaceHistory[commandArgs.path].push({ + workspaceHistory[commandArgs.path].push({ index: historyIndex, count: count, name: `${commandArgs.name}`, @@ -1001,7 +1023,7 @@ export class Act { jobs: [] }); historyTreeDataProvider.refresh(); - await this.storageManager.update(StorageKey.WorkspaceHistory, this.historyManager.workspaceHistory); + await this.storageManager.update(StorageKey.WorkspaceHistory, workspaceHistory); } async install(packageManager: string) { diff --git a/src/historyManager.ts b/src/historyManager.ts index 47b52d6..314e1b9 100644 --- a/src/historyManager.ts +++ b/src/historyManager.ts @@ -49,11 +49,26 @@ export enum HistoryStatus { export class HistoryManager { storageManager: StorageManager; - workspaceHistory: { [path: string]: History[] }; + private workspaceHistory: { [path: string]: History[] }; + constructor(storageManager: StorageManager) { this.storageManager = storageManager; - const workspaceHistory = this.storageManager.get<{ [path: string]: History[] }>(StorageKey.WorkspaceHistory) || {}; + this.workspaceHistory = {}; + this.syncHistory(); + } + + + async getWorkspaceHistory() { + if (!this.workspaceHistory) { + await this.syncHistory(); + } + + return this.workspaceHistory; + } + + async syncHistory() { + const workspaceHistory = await this.storageManager.get<{ [path: string]: History[] }>(StorageKey.WorkspaceHistory) || {}; for (const [path, historyLogs] of Object.entries(workspaceHistory)) { workspaceHistory[path] = historyLogs.map(history => { history.jobs?.forEach((job, jobIndex) => { @@ -79,22 +94,27 @@ export class HistoryManager { }); } this.workspaceHistory = workspaceHistory; - } + }; async clearAll(workspaceFolder: WorkspaceFolder) { - const existingHistory = this.workspaceHistory[workspaceFolder.uri.fsPath]; + await this.syncHistory(); + const existingHistory = this.workspaceHistory?.[workspaceFolder.uri.fsPath] ?? []; for (const history of existingHistory) { try { await workspace.fs.delete(Uri.file(history.logPath)); } catch (error: any) { } } - this.workspaceHistory[workspaceFolder.uri.fsPath] = []; + if (this.workspaceHistory) { + this.workspaceHistory[workspaceFolder.uri.fsPath] = []; + } historyTreeDataProvider.refresh(); await this.storageManager.update(StorageKey.WorkspaceHistory, this.workspaceHistory); } async viewOutput(history: History) { + await this.syncHistory(); + try { const document = await workspace.openTextDocument(history.logPath); await window.showTextDocument(document); @@ -112,9 +132,10 @@ export class HistoryManager { } async remove(history: History) { - const historyIndex = this.workspaceHistory[history.commandArgs.path].findIndex(workspaceHistory => workspaceHistory.index === history.index); + await this.syncHistory(); + const historyIndex = (this.workspaceHistory?.[history.commandArgs.path] ?? []).findIndex(workspaceHistory => workspaceHistory.index === history.index); if (historyIndex > -1) { - this.workspaceHistory[history.commandArgs.path].splice(historyIndex, 1); + (this.workspaceHistory?.[history.commandArgs.path] ?? []).splice(historyIndex, 1); await this.storageManager.update(StorageKey.WorkspaceHistory, this.workspaceHistory); try { diff --git a/src/issueHandler.ts b/src/issueHandler.ts index 9f3a84d..9f085ac 100644 --- a/src/issueHandler.ts +++ b/src/issueHandler.ts @@ -118,8 +118,8 @@ export namespace IssueHandler { }); } - const workflowHistory = act.historyManager.workspaceHistory[workspaceFolder.uri.fsPath]?.filter(history => history.commandArgs.workflow?.uri.fsPath === workflow.uri.fsPath); - if (workflowHistory && workflowHistory.length > 0) { + const workflowHistory = ((await act.historyManager.getWorkspaceHistory())[workspaceFolder.uri.fsPath] ?? []).filter(history => history.commandArgs.workflow?.uri.fsPath === workflow.uri.fsPath); + if (workflowHistory.length > 0) { // Get last act command const settings = await act.settingsManager.getSettings(workspaceFolder, true); const history = workflowHistory[workflowHistory.length - 1]; diff --git a/src/settingsManager.ts b/src/settingsManager.ts index c84cd0f..e4a2857 100644 --- a/src/settingsManager.ts +++ b/src/settingsManager.ts @@ -124,7 +124,7 @@ export class SettingsManager { } } - const existingSettings = this.storageManager.get<{ [path: string]: Setting[] }>(storageKey) || {}; + const existingSettings = await this.storageManager.get<{ [path: string]: Setting[] }>(storageKey) || {}; if (existingSettings[workspaceFolder.uri.fsPath]) { for (const [index, setting] of settings.entries()) { const existingSetting = existingSettings[workspaceFolder.uri.fsPath].find(existingSetting => existingSetting.key === setting.key); @@ -154,7 +154,7 @@ export class SettingsManager { } async getCustomSettings(workspaceFolder: WorkspaceFolder, storageKey: StorageKey): Promise { - const existingCustomSettings = this.storageManager.get<{ [path: string]: CustomSetting[] }>(storageKey) || {}; + const existingCustomSettings = await this.storageManager.get<{ [path: string]: CustomSetting[] }>(storageKey) || {}; return existingCustomSettings[workspaceFolder.uri.fsPath] || []; } @@ -233,7 +233,7 @@ export class SettingsManager { } async editCustomSetting(workspaceFolder: WorkspaceFolder, newCustomSetting: CustomSetting, storageKey: StorageKey, forceAppend: boolean = false) { - const existingCustomSettings = this.storageManager.get<{ [path: string]: CustomSetting[] }>(storageKey) || {}; + const existingCustomSettings = await this.storageManager.get<{ [path: string]: CustomSetting[] }>(storageKey) || {}; if (existingCustomSettings[workspaceFolder.uri.fsPath]) { const index = existingCustomSettings[workspaceFolder.uri.fsPath] .findIndex(customSetting => @@ -254,7 +254,7 @@ export class SettingsManager { } async removeCustomSetting(workspaceFolder: WorkspaceFolder, existingCustomSetting: CustomSetting, storageKey: StorageKey) { - const existingCustomSettings = this.storageManager.get<{ [path: string]: CustomSetting[] }>(storageKey) || {}; + const existingCustomSettings = await this.storageManager.get<{ [path: string]: CustomSetting[] }>(storageKey) || {}; if (existingCustomSettings[workspaceFolder.uri.fsPath]) { const index = existingCustomSettings[workspaceFolder.uri.fsPath].findIndex(customSetting => storageKey === StorageKey.Options ? @@ -289,7 +289,7 @@ export class SettingsManager { newSetting.value = ''; } - const existingSettings = this.storageManager.get<{ [path: string]: Setting[] }>(storageKey) || {}; + const existingSettings = await this.storageManager.get<{ [path: string]: Setting[] }>(storageKey) || {}; if (existingSettings[workspaceFolder.uri.fsPath]) { const index = existingSettings[workspaceFolder.uri.fsPath].findIndex(setting => setting.key === newSetting.key); if (index > -1) { diff --git a/src/storageManager.ts b/src/storageManager.ts index 95f33fe..018c0d3 100644 --- a/src/storageManager.ts +++ b/src/storageManager.ts @@ -1,4 +1,4 @@ -import { ExtensionContext } from "vscode"; +import { ExtensionContext, Uri, workspace } from "vscode"; export enum StorageKey { WorkspaceHistory = 'workspaceHistory', @@ -9,7 +9,7 @@ export enum StorageKey { Inputs = 'inputs', InputFiles = 'inputFiles', Runners = 'runners', - PayloadFiles = 'PayloadFiles', + PayloadFiles = 'payloadFiles', Options = 'options' } @@ -21,15 +21,43 @@ export class StorageManager { this.context = context; } - keys(): readonly string[] { - return this.context.globalState.keys(); + private async getStorageDirectory(): Promise { + const storageDirectory = Uri.joinPath(this.context.storageUri ?? this.context.globalStorageUri, "storageManager"); + await workspace.fs.createDirectory(storageDirectory).then(undefined, () => void 0); + return storageDirectory; } - get(storageKey: StorageKey): T | undefined { - return this.context.globalState.get(`${this.extensionKey}.${storageKey}`); + private async getStorageFile(storageKey: StorageKey): Promise { + const storageDirectory = await this.getStorageDirectory(); + return Uri.joinPath(storageDirectory, `${storageKey}.json`); + } + + async get(storageKey: StorageKey): Promise { + if ([StorageKey.Secrets, StorageKey.SecretFiles].includes(storageKey)) { + return this.context.globalState.get(`${this.extensionKey}.${storageKey}`); + } + + const storageFile = await this.getStorageFile(storageKey); + return workspace.fs.readFile(storageFile).then(data => { + if (data) { + return JSON.parse(data.toString()) as T; + } + return undefined; + }, (error) => { + if (error.code === 'FileNotFound') { + return undefined; + } + }); } async update(storageKey: StorageKey, value: any): Promise { - await this.context.globalState.update(`${this.extensionKey}.${storageKey}`, value); + if ([StorageKey.Secrets, StorageKey.SecretFiles].includes(storageKey)) { + await this.context.globalState.update(`${this.extensionKey}.${storageKey}`, value); + return; + } + + const data = JSON.stringify(value, null, 2); + const storageFile = await this.getStorageFile(storageKey); + await workspace.fs.writeFile(storageFile, Buffer.from(data)); } } \ No newline at end of file diff --git a/src/views/history/historyTreeDataProvider.ts b/src/views/history/historyTreeDataProvider.ts index a10dcc1..e74cf56 100644 --- a/src/views/history/historyTreeDataProvider.ts +++ b/src/views/history/historyTreeDataProvider.ts @@ -87,18 +87,18 @@ export default class HistoryTreeDataProvider implements TreeDataProvider 0) { - isRunning = act.historyManager.workspaceHistory[workspaceFolders[0].uri.fsPath].find(workspaceHistory => workspaceHistory.status === HistoryStatus.Running) !== undefined; + const workspaceHistory = (await act.historyManager.getWorkspaceHistory())[workspaceFolders[0].uri.fsPath] ?? []; + if (workspaceHistory.length > 0) { + isRunning = workspaceHistory.find(workspaceHistory => workspaceHistory.status === HistoryStatus.Running) !== undefined; noHistory = false; } } else if (workspaceFolders.length > 1) { for (const workspaceFolder of workspaceFolders) { items.push(new WorkspaceFolderHistoryTreeItem(workspaceFolder)); - const workspaceHistory = act.historyManager.workspaceHistory[workspaceFolder.uri.fsPath]; - if (workspaceHistory && workspaceHistory.length > 0) { - isRunning = act.historyManager.workspaceHistory[workspaceFolder.uri.fsPath].find(workspaceHistory => workspaceHistory.status === HistoryStatus.Running) !== undefined; + const workspaceHistory = (await act.historyManager.getWorkspaceHistory())[workspaceFolder.uri.fsPath] ?? []; + if (workspaceHistory.length > 0) { + isRunning = workspaceHistory.find(workspaceHistory => workspaceHistory.status === HistoryStatus.Running) !== undefined; noHistory = false; } } diff --git a/src/views/history/workspaceFolderHistory.ts b/src/views/history/workspaceFolderHistory.ts index df61031..28dc17f 100644 --- a/src/views/history/workspaceFolderHistory.ts +++ b/src/views/history/workspaceFolderHistory.ts @@ -15,7 +15,7 @@ export default class WorkspaceFolderHistoryTreeItem extends TreeItem implements async getChildren(): Promise { const items: GithubLocalActionsTreeItem[] = []; - const workspaceHistory = act.historyManager.workspaceHistory[this.workspaceFolder.uri.fsPath]; + const workspaceHistory = (await act.historyManager.getWorkspaceHistory())[this.workspaceFolder.uri.fsPath]; if (workspaceHistory) { for (const history of workspaceHistory.slice().reverse()) { items.push(new HistoryTreeItem(this.workspaceFolder, history));