From 524a7242792bde709ba0f0b5da9378e14e2f0b59 Mon Sep 17 00:00:00 2001 From: Sanjula Ganepola Date: Sat, 19 Oct 2024 21:44:36 -0400 Subject: [PATCH] Add multi-workspace support Signed-off-by: Sanjula Ganepola --- package.json | 21 +++++- src/act.ts | 67 +++++++++++-------- src/historyManager.ts | 16 ++--- src/{dateUtils.ts => utils.ts} | 21 +++++- src/views/history/history.ts | 8 +-- src/views/history/historyTreeDataProvider.ts | 24 ++++--- .../settings/settingsTreeDataProvider.ts | 14 ++-- .../workflows/workflowsTreeDataProvider.ts | 49 ++++++++------ 8 files changed, 141 insertions(+), 79 deletions(-) rename src/{dateUtils.ts => utils.ts} (61%) diff --git a/package.json b/package.json index b4fbd51..12d5a0d 100644 --- a/package.json +++ b/package.json @@ -366,12 +366,12 @@ }, { "command": "githubLocalActions.runAllWorkflows", - "when": "view == workflows", + "when": "view == workflows && workspaceFolderCount <= 1", "group": "navigation@0" }, { "command": "githubLocalActions.runEvent", - "when": "view == workflows", + "when": "view == workflows && workspaceFolderCount <= 1", "group": "navigation@1" }, { @@ -381,7 +381,7 @@ }, { "command": "githubLocalActions.clearAll", - "when": "view == history", + "when": "view == history && workspaceFolderCount <= 1", "group": "navigation@0" }, { @@ -411,6 +411,16 @@ "when": "view == components && viewItem =~ /^githubLocalActions.component_Not Running.*/", "group": "inline@1" }, + { + "command": "githubLocalActions.runAllWorkflows", + "when": "view == workflows && viewItem =~ /^githubLocalActions.workspaceFolderWorkflows.*/ && workspaceFolderCount > 1", + "group": "inline@0" + }, + { + "command": "githubLocalActions.runEvent", + "when": "view == workflows && viewItem =~ /^githubLocalActions.workspaceFolderWorkflows.*/ && workspaceFolderCount > 1", + "group": "inline@1" + }, { "command": "githubLocalActions.openWorkflow", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", @@ -426,6 +436,11 @@ "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", "group": "inline@0" }, + { + "command": "githubLocalActions.clearAll", + "when": "view == history && viewItem =~ /^githubLocalActions.workspaceFolderHistory.*/ && workspaceFolderCount > 1", + "group": "inline@0" + }, { "command": "githubLocalActions.viewOutput", "when": "view == history && viewItem =~ /^githubLocalActions.history.*/", diff --git a/src/act.ts b/src/act.ts index 99fbdd9..d9a1bcc 100644 --- a/src/act.ts +++ b/src/act.ts @@ -1,12 +1,12 @@ import * as child_process from 'child_process'; import * as path from "path"; -import { commands, CustomExecution, env, EventEmitter, ExtensionContext, Pseudoterminal, ShellExecution, TaskDefinition, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, TaskScope, TerminalDimensions, window, workspace, WorkspaceFolder } from "vscode"; +import { commands, CustomExecution, env, EventEmitter, ExtensionContext, Pseudoterminal, ShellExecution, TaskDefinition, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, TaskScope, TerminalDimensions, window, WorkspaceFolder } from "vscode"; import { ComponentsManager } from "./componentsManager"; import { historyTreeDataProvider } from './extension'; import { HistoryManager, HistoryStatus } from './historyManager'; import { SettingsManager } from './settingsManager'; import { StorageKey, StorageManager } from './storageManager'; -import { Workflow, WorkflowsManager } from "./workflowsManager"; +import { Job, Workflow, WorkflowsManager } from "./workflowsManager"; export enum Event { BranchProtectionRule = 'branch_protection_rule', @@ -127,35 +127,48 @@ export class Act { } } - async runAllWorkflows() { - // TODO: Implement + async runAllWorkflows(workspaceFolder: WorkspaceFolder) { + return await this.runCommand({ + workspaceFolder: workspaceFolder, + options: ``, + name: workspaceFolder.name, + typeText: [] + }); } - async runWorkflow(workflow: Workflow) { - const workspaceFolder = workspace.getWorkspaceFolder(workflow.uri); - if (workspaceFolder) { - return await this.runCommand({ - workspaceFolder: workspaceFolder, - options: `${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}"`, - name: workflow.name, - typeText: [ - `Workflow: ${workflow.name}` - ] - }); - } else { - window.showErrorMessage(`Failed to locate workspace folder for ${workflow.uri.fsPath}`); - } + async runWorkflow(workspaceFolder: WorkspaceFolder, workflow: Workflow) { + return await this.runCommand({ + workspaceFolder: workspaceFolder, + options: `${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}"`, + name: workflow.name, + typeText: [ + `Workflow: ${workflow.name}` + ] + }); } - // TODO: Implement - // async runJob(workspaceFolder: WorkspaceFolder, workflow: Workflow, job: Job) { - // return await this.runCommand(workspaceFolder, `${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}" ${Option.Job} "${job.id}"`, `${workflow.name}/${job.name}`, [`Workflow: ${workflow.name}`, `Job: ${job.name}`]); - // } + async runJob(workspaceFolder: WorkspaceFolder, workflow: Workflow, job: Job) { + return await this.runCommand({ + workspaceFolder: workspaceFolder, + options: `${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}" ${Option.Job} "${job.id}"`, + name: `${workflow.name}/${job.name}`, + typeText: [ + `Workflow: ${workflow.name}`, + `Job: ${job.name}` + ] + }); + } - // TODO: Implement - // async runEvent(workspaceFolder: WorkspaceFolder, event: Event) { - // return await this.runCommand(workspaceFolder, `${Option.Workflows} ${event}`, event, [`Event: ${event}`]); - // } + async runEvent(workspaceFolder: WorkspaceFolder, event: Event) { + return await this.runCommand({ + workspaceFolder: workspaceFolder, + options: event, + name: event, + typeText: [ + `Event: ${event}` + ] + }); + } async runCommand(commandArgs: CommandArgs) { const command = `${Act.base} ${Option.Json} ${commandArgs.options}`; @@ -289,7 +302,7 @@ export class Act { writeEmitter.fire(`Environments: OSSBUILD\r\n`); writeEmitter.fire(`Variables: VARIABLE1=ABC, VARIABLE2=DEF\r\n`); writeEmitter.fire(`Secrets: SECRET1=ABC, SECRET2=DEF\r\n`); - writeEmitter.fire(`Command: ${command}\r\n`); + writeEmitter.fire(`Command: ${command.replace(` ${Option.Json}`, ``)}\r\n`); writeEmitter.fire(`\r\n`); }, diff --git a/src/historyManager.ts b/src/historyManager.ts index 5437b61..a29b8d5 100644 --- a/src/historyManager.ts +++ b/src/historyManager.ts @@ -1,4 +1,4 @@ -import { TaskExecution, window, workspace } from "vscode"; +import { TaskExecution, window, workspace, WorkspaceFolder } from "vscode"; import { CommandArgs } from "./act"; import { act, historyTreeDataProvider } from "./extension"; import { StorageKey, StorageManager } from "./storageManager"; @@ -42,16 +42,10 @@ export class HistoryManager { this.workspaceHistory = workspaceHistory; } - async clearAll() { - //TODO: Fix for multi workspace support - const workspaceFolders = workspace.workspaceFolders; - if (workspaceFolders && workspaceFolders.length > 0) { - for (const workspaceFolder of workspaceFolders) { - this.workspaceHistory[workspaceFolder.uri.fsPath] = []; - historyTreeDataProvider.refresh(); - this.storageManager.update(StorageKey.WorkspaceHistory, this.workspaceHistory); - } - } + async clearAll(workspaceFolder: WorkspaceFolder) { + this.workspaceHistory[workspaceFolder.uri.fsPath] = []; + historyTreeDataProvider.refresh(); + this.storageManager.update(StorageKey.WorkspaceHistory, this.workspaceHistory); } async viewOutput(history: History) { diff --git a/src/dateUtils.ts b/src/utils.ts similarity index 61% rename from src/dateUtils.ts rename to src/utils.ts index b3a9ee6..3e1c3a2 100644 --- a/src/dateUtils.ts +++ b/src/utils.ts @@ -1,4 +1,6 @@ -export namespace DateUtils { +import { window, workspace, WorkspaceFolder } from "vscode"; + +export namespace Utils { /** * Get date time string. * @@ -37,4 +39,21 @@ export namespace DateUtils { const seconds = totalSeconds % 60; return `${minutes}m ${seconds}s`; } + + /** + * Get the provided workspace folder or the first one if there are multiple. + */ + export async function getWorkspaceFolder(workspaceFolder?: WorkspaceFolder) { + if (workspaceFolder) { + return workspaceFolder; + } else { + const workspaceFolders = workspace.workspaceFolders; + if (workspaceFolders && workspaceFolders.length > 0) { + return workspaceFolders[0]; + } else { + await window.showErrorMessage('Failed to find a workspace folder'); + return; + } + } + } } \ No newline at end of file diff --git a/src/views/history/history.ts b/src/views/history/history.ts index fa514b1..6f88333 100644 --- a/src/views/history/history.ts +++ b/src/views/history/history.ts @@ -1,6 +1,6 @@ import { ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState, WorkspaceFolder } from "vscode"; -import { DateUtils } from "../../dateUtils"; import { History, HistoryStatus } from "../../historyManager"; +import { Utils } from "../../utils"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; export default class HistoryTreeItem extends TreeItem implements GithubLocalActionsTreeItem { @@ -13,7 +13,7 @@ export default class HistoryTreeItem extends TreeItem implements GithubLocalActi let totalDuration: string | undefined; if (history.date) { - totalDuration = DateUtils.getTimeDuration(history.date.start, history.date.end); + totalDuration = Utils.getTimeDuration(history.date.start, history.date.end); } this.description = totalDuration; @@ -34,8 +34,8 @@ export default class HistoryTreeItem extends TreeItem implements GithubLocalActi } this.tooltip = `Name: ${history.name}\n` + `Status: ${history.status}\n` + - `Started: ${history.date ? DateUtils.getDateString(history.date.start) : 'N/A'}\n` + - `Ended: ${history.date ? DateUtils.getDateString(history.date.end) : 'N/A'}\n` + + `Started: ${history.date ? Utils.getDateString(history.date.start) : 'N/A'}\n` + + `Ended: ${history.date ? Utils.getDateString(history.date.end) : 'N/A'}\n` + (totalDuration ? `Total Duration: ${totalDuration}\n` : ``); } diff --git a/src/views/history/historyTreeDataProvider.ts b/src/views/history/historyTreeDataProvider.ts index 3193fab..dfdca9c 100644 --- a/src/views/history/historyTreeDataProvider.ts +++ b/src/views/history/historyTreeDataProvider.ts @@ -1,6 +1,7 @@ import { CancellationToken, commands, EventEmitter, ExtensionContext, extensions, TreeDataProvider, TreeItem, workspace } from "vscode"; import { act } from "../../extension"; import { HistoryStatus } from "../../historyManager"; +import { Utils } from "../../utils"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import HistoryTreeItem from "./history"; import WorkspaceFolderHistoryTreeItem from "./workspaceFolderHistory"; @@ -16,8 +17,11 @@ export default class HistoryTreeDataProvider implements TreeDataProvider { - await act.historyManager.clearAll(); + commands.registerCommand('githubLocalActions.clearAll', async (workspaceFolderHistoryTreeItem?: WorkspaceFolderHistoryTreeItem) => { + const workspaceFolder = await Utils.getWorkspaceFolder(workspaceFolderHistoryTreeItem?.workspaceFolder); + if (workspaceFolder) { + await act.historyManager.clearAll(workspaceFolder); + } }), commands.registerCommand('githubLocalActions.refreshHistory', async () => { this.refresh(); @@ -65,13 +69,17 @@ export default class HistoryTreeDataProvider implements TreeDataProvider 1) { + for (const workspaceFolder of workspaceFolders) { + items.push(new WorkspaceFolderHistoryTreeItem(workspaceFolder)); - const workspaceHistory = act.historyManager.workspaceHistory[workspaceFolders[0].uri.fsPath]; - if (workspaceHistory.length > 0) { - isRunning = act.historyManager.workspaceHistory[workspaceFolders[0].uri.fsPath].find(workspaceHistory => workspaceHistory.status === HistoryStatus.Running) !== undefined; - noHistory = false; + const workspaceHistory = act.historyManager.workspaceHistory[workspaceFolders[0].uri.fsPath]; + if (workspaceHistory.length > 0) { + isRunning = act.historyManager.workspaceHistory[workspaceFolders[0].uri.fsPath].find(workspaceHistory => workspaceHistory.status === HistoryStatus.Running) !== undefined; + noHistory = false; + } } } } diff --git a/src/views/settings/settingsTreeDataProvider.ts b/src/views/settings/settingsTreeDataProvider.ts index ef6ba04..f45aacc 100644 --- a/src/views/settings/settingsTreeDataProvider.ts +++ b/src/views/settings/settingsTreeDataProvider.ts @@ -61,12 +61,16 @@ export default class SettingsTreeDataProvider implements TreeDataProvider 1) { + for (const workspaceFolder of workspaceFolders) { + items.push(new WorkspaceFolderSettingsTreeItem(workspaceFolder)); - const workflows = await act.workflowsManager.getWorkflows(workspaceFolder); - if (workflows.length > 0) { - noSettings = false; + const workflows = await act.workflowsManager.getWorkflows(workspaceFolder); + if (workflows.length > 0) { + noSettings = false; + } } } } diff --git a/src/views/workflows/workflowsTreeDataProvider.ts b/src/views/workflows/workflowsTreeDataProvider.ts index 8cc6e1f..f552535 100644 --- a/src/views/workflows/workflowsTreeDataProvider.ts +++ b/src/views/workflows/workflowsTreeDataProvider.ts @@ -1,7 +1,9 @@ import { CancellationToken, commands, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, window, workspace } from "vscode"; import { Event } from "../../act"; import { act } from "../../extension"; +import { Utils } from "../../utils"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; +import JobTreeItem from "./job"; import WorkflowTreeItem from "./workflow"; import WorkspaceFolderWorkflowsTreeItem from "./workspaceFolderWorkflows"; @@ -12,18 +14,23 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider { - await act.runAllWorkflows(); + commands.registerCommand('githubLocalActions.runAllWorkflows', async (workspaceFolderWorkflowsTreeItem?: WorkspaceFolderWorkflowsTreeItem) => { + const workspaceFolder = await Utils.getWorkspaceFolder(workspaceFolderWorkflowsTreeItem?.workspaceFolder); + if (workspaceFolder) { + await act.runAllWorkflows(workspaceFolder); + } }), - commands.registerCommand('githubLocalActions.runEvent', async () => { - const event = await window.showQuickPick(Object.values(Event), { - title: 'Select the event to run', - placeHolder: 'Event' - }); + commands.registerCommand('githubLocalActions.runEvent', async (workspaceFolderWorkflowsTreeItem?: WorkspaceFolderWorkflowsTreeItem) => { + const workspaceFolder = await Utils.getWorkspaceFolder(workspaceFolderWorkflowsTreeItem?.workspaceFolder); + if (workspaceFolder) { + const event = await window.showQuickPick(Object.values(Event), { + title: 'Select the event to run', + placeHolder: 'Event' + }); - if (event) { - // TODO: Implement running event - // await act.runEvent(event as Event); + if (event) { + await act.runEvent(workspaceFolder, event as Event); + } } }), commands.registerCommand('githubLocalActions.refreshWorkflows', async () => { @@ -34,11 +41,10 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider { - await act.runWorkflow(workflowTreeItem.workflow); + await act.runWorkflow(workflowTreeItem.workspaceFolder, workflowTreeItem.workflow); }), - commands.registerCommand('githubLocalActions.runJob', async (workflowTreeItem: WorkflowTreeItem) => { - // TODO: Implement running job - // await act.runJob() + commands.registerCommand('githubLocalActions.runJob', async (jobTreeItem: JobTreeItem) => { + await act.runJob(jobTreeItem.workspaceFolder, jobTreeItem.workflow, jobTreeItem.job); }) ); } @@ -68,15 +74,18 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider 1) { + for (const workspaceFolder of workspaceFolders) { + items.push(new WorkspaceFolderWorkflowsTreeItem(workspaceFolder)); - const workflows = await act.workflowsManager.getWorkflows(workspaceFolder); - if (workflows.length > 0) { - noWorkflows = false; + const workflows = await act.workflowsManager.getWorkflows(workspaceFolder); + if (workflows.length > 0) { + noWorkflows = false; + } } } - } await commands.executeCommand('setContext', 'githubLocalActions:noWorkflows', noWorkflows);