diff --git a/package.json b/package.json index 3b46151..d3849a4 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,11 @@ "name": "Workflows", "icon": "$(layers)" }, + { + "id": "history", + "name": "History", + "icon": "$(book)" + }, { "id": "settings", "name": "Settings", @@ -59,6 +64,42 @@ } ] }, + "viewsWelcome": [ + { + "view": "components", + "contents": "Loading components..." + }, + { + "view": "workflows", + "contents": "Loading components...", + "when": "!githubLocalActions:noWorkflows" + }, + { + "view": "workflows", + "contents": "No workflows found.", + "when": "githubLocalActions:noWorkflows" + }, + { + "view": "history", + "contents": "Loading history...", + "when": "!githubLocalActions:noHistory" + }, + { + "view": "history", + "contents": "No history found.", + "when": "githubLocalActions:noHistory" + }, + { + "view": "settings", + "contents": "Loading settings...", + "when": "!githubLocalActions:noSettings" + }, + { + "view": "settings", + "contents": "No workflows found.", + "when": "githubLocalActions:noSettings" + } + ], "commands": [ { "category": "GitHub Local Actions", @@ -114,6 +155,18 @@ "title": "Run Workflow", "icon": "$(debug-start)" }, + { + "category": "GitHub Local Actions", + "command": "githubLocalActions.runJob", + "title": "Run Job", + "icon": "$(debug-start)" + }, + { + "category": "GitHub Local Actions", + "command": "githubLocalActions.refreshHistory", + "title": "Refresh", + "icon": "$(refresh)" + }, { "category": "GitHub Local Actions", "command": "githubLocalActions.refreshSettings", @@ -159,6 +212,14 @@ "command": "githubLocalActions.runWorkflow", "when": "never" }, + { + "command": "githubLocalActions.runJob", + "when": "never" + }, + { + "command": "githubLocalActions.refreshHistory", + "when": "never" + }, { "command": "githubLocalActions.refreshSettings", "when": "never" @@ -185,6 +246,11 @@ "when": "view == workflows", "group": "navigation@2" }, + { + "command": "githubLocalActions.refreshHistory", + "when": "view == history", + "group": "navigation@0" + }, { "command": "githubLocalActions.refreshSettings", "when": "view == settings", @@ -216,6 +282,11 @@ "command": "githubLocalActions.runWorkflow", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", "group": "inline@1" + }, + { + "command": "githubLocalActions.runJob", + "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", + "group": "inline@0" } ] }, diff --git a/src/act.ts b/src/act.ts index 043ff66..04df970 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, Pseudoterminal, ShellExecution, TaskDefinition, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, TaskScope, TerminalDimensions, window, workspace } from "vscode"; +import { commands, CustomExecution, env, EventEmitter, Pseudoterminal, ShellExecution, TaskDefinition, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, TaskScope, TerminalDimensions, window, workspace, WorkspaceFolder } from "vscode"; import { ComponentsManager } from "./componentsManager"; -import { workflowsTreeDataProvider } from './extension'; +import { historyTreeDataProvider } from './extension'; import { SettingsManager } from './settingsManager'; import { Workflow, WorkflowsManager } from "./workflowsManager"; -export enum EventTrigger { +export enum Event { BranchProtectionRule = 'branch_protection_rule', CheckRun = 'check_run', CheckSuite = 'check_suite', @@ -44,27 +44,11 @@ export enum EventTrigger { export enum Option { Workflows = '--workflows', + Job = '--job', Variable = '--var', Json = "--json" } -export interface WorkflowLog { - name: string, - status: WorkflowStatus - jobLogs: JobLog[] -} - -export interface JobLog { - name: string, - status: JobStatus, - stepLogs: StepLog[] -} - -export interface StepLog { - name: string, - status: StepStatus -} - export interface RawLog { dryrun: boolean, job: string, @@ -74,42 +58,34 @@ export interface RawLog { msg: string, time: string, - stage: string, - step: string, - stepID: string[], + raw_output?: boolean, - jobResult: string, //TODO: Could be an enum? + stage?: string, + step?: string, + stepID?: string[], + stepResult?: string, //TODO: Could be an enum? + + jobResult?: string, //TODO: Could be an enum? } -export enum WorkflowStatus { - Queued = 'queued', - InProgress = 'inProgress', - Success = 'success', - Failed = 'failed', - Cancelled = 'cancelled' +export interface History { + name: string, + status: HistoryStatus, + start?: string, + end?: string, + output?: string } -export enum JobStatus { - Queued = 'queued', - InProgress = 'inProgress', - Skipped = 'skipped', - Success = 'success', - Failed = 'failed', - Cancelled = 'cancelled' -} - -export enum StepStatus { - Queued = 'queued', - InProgress = 'inProgress', - Skipped = 'skipped', - Success = 'success', - Failed = 'failed', - Cancelled = 'cancelled' +export enum HistoryStatus { + Running = 'Running', + Success = 'Success', + Failed = 'Failed', + Cancelled = 'Cancelled' } export class Act { private static base: string = 'act'; - workflowLogs: { [path: string]: WorkflowLog[] }; + workspaceHistory: { [path: string]: History[] }; componentsManager: ComponentsManager; workflowsManager: WorkflowsManager; settingsManager: SettingsManager; @@ -117,7 +93,7 @@ export class Act { prebuiltExecutables: { [architecture: string]: string }; constructor() { - this.workflowLogs = {}; + this.workspaceHistory = {}; this.componentsManager = new ComponentsManager(); this.workflowsManager = new WorkflowsManager(); this.settingsManager = new SettingsManager(); @@ -178,15 +154,28 @@ export class Act { // TODO: Implement } - async runEvent(eventTrigger: EventTrigger) { - // return await this.runCommand(`${Act.base} ${eventTrigger}`); - } - async runWorkflow(workflow: Workflow) { - return await this.runCommand(workflow, `${Act.base} ${Option.Json} ${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}"`); + const workspaceFolder = workspace.getWorkspaceFolder(workflow.uri); + if (workspaceFolder) { + return await this.runCommand(workspaceFolder, `${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}"`, workflow.name, [`Workflow: ${workflow.name}`]); + } else { + window.showErrorMessage(`Failed to locate workspace folder for ${workflow.uri.fsPath}`); + } } - async runCommand(workflow: Workflow, command: string) { + // 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}`]); + // } + + // TODO: Implement + // async runEvent(workspaceFolder: WorkspaceFolder, event: Event) { + // return await this.runCommand(workspaceFolder, `${Option.Workflows} ${event}`, event, [`Event: ${event}`]); + // } + + async runCommand(workspaceFolder: WorkspaceFolder, options: string, name: string, typeText: string[]) { + const command = `${Act.base} ${Option.Json} ${options}`; + const unreadyComponents = await this.componentsManager.getUnreadyComponents(); if (unreadyComponents.length > 0) { window.showErrorMessage(`The following required components are not ready: ${unreadyComponents.map(component => component.name).join(', ')}`, 'Fix...').then(async value => { @@ -197,37 +186,20 @@ export class Act { return; } - const workspaceFolder = workspace.getWorkspaceFolder(workflow.uri); - if (!workspaceFolder) { - window.showErrorMessage('Failed to detect workspace folder'); - return; + if (!this.workspaceHistory[workspaceFolder.uri.fsPath]) { + this.workspaceHistory[workspaceFolder.uri.fsPath] = []; } - if (!this.workflowLogs[workflow.uri.fsPath]) { - this.workflowLogs[workflow.uri.fsPath] = []; - } - - this.workflowLogs[workflow.uri.fsPath].push({ - name: `${workflow.name} #${this.workflowLogs[workflow.uri.fsPath].length + 1}`, - status: WorkflowStatus.Queued, - jobLogs: Object.entries(workflow.yaml.jobs).map(([key, value]) => { - return { - name: value.name ? value.name : key, - status: JobStatus.Queued, - stepLogs: Object.entries(workflow.yaml.jobs[key].steps).map(([key, value]) => { - return { - name: value.name ? value.name : key, - status: StepStatus.Queued, - stepLogs: [] - } - }) - } - }) + const historyIndex = this.workspaceHistory[workspaceFolder.uri.fsPath].length; + this.workspaceHistory[workspaceFolder.uri.fsPath].push({ + name: `${name} #${this.workspaceHistory[workspaceFolder.uri.fsPath].length + 1}`, + status: HistoryStatus.Running, + start: new Date().toISOString() }); - workflowsTreeDataProvider.refresh(); + historyTreeDataProvider.refresh(); await tasks.executeTask({ - name: workflow.name, + name: name, detail: 'Run workflow', definition: { type: 'GitHub Local Actions' }, source: 'GitHub Local Actions', @@ -249,53 +221,77 @@ export class Act { const writeEmitter = new EventEmitter(); const closeEmitter = new EventEmitter(); + const exec = child_process.spawn(command, { cwd: workspaceFolder.uri.fsPath, shell: env.shell }); + const handleIO = (data: any) => { + const lines: string[] = data.toString().split('\n').filter((line: string) => line != ''); + for (const line of lines) { + const jsonLine = JSON.parse(line); + + if (!this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].start) { + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].start = jsonLine.time; + } + + if (jsonLine.jobResult) { + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].end = jsonLine.time; + + switch (jsonLine.jobResult) { + case 'success': + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].status = HistoryStatus.Success; + break; + case 'failure': + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].status = HistoryStatus.Failed; + break; + // TODO: Handle cancelled + } + } + + historyTreeDataProvider.refresh(); + writeEmitter.fire(`${jsonLine.msg.trimEnd()}\r\n`); + } + } + exec.stdout.on('data', handleIO); + exec.stderr.on('data', handleIO); + exec.on('close', (code) => { + if (!this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].end) { + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].end = new Date().toISOString(); + } + + if (this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].status === HistoryStatus.Running) { + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].status = HistoryStatus.Failed; + } + + historyTreeDataProvider.refresh(); + closeEmitter.fire(code || 0); + }); + return { onDidWrite: writeEmitter.event, onDidClose: closeEmitter.event, open: async (initialDimensions: TerminalDimensions | undefined): Promise => { - writeEmitter.fire(`Workflow: ${workflow.name}\r\n`); - writeEmitter.fire(`Path: ${workflow.uri.fsPath}\r\n`); - writeEmitter.fire(`Command: ${command}\r\n`); + writeEmitter.fire(`Name: ${name}\r\n`); + writeEmitter.fire(`Path: ${workspaceFolder.uri.fsPath}\r\n`); + for (const text of typeText) { + writeEmitter.fire(`${text}\r\n`); + } 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(`Timestamp: ${new Date().toLocaleTimeString()}\r\n`); + writeEmitter.fire(`Variables: VARIABLE1=ABC, VARIABLE2=DEF\r\n`); + writeEmitter.fire(`Secrets: SECRET1=ABC, SECRET2=DEF\r\n`); + writeEmitter.fire(`Timestamp: ${new Date().toLocaleTimeString()}\r\n`); + writeEmitter.fire(`Command: ${command}\r\n`); writeEmitter.fire(`\r\n`); - - const exec = child_process.spawn(command, { cwd: workspaceFolder.uri.fsPath, shell: env.shell }); - exec.stdout.on('data', (data) => { - const lines = data.toString().split('\n'); - for (const line of lines) { - const rawLog: RawLog = JSON.parse(line); - - if (rawLog.stepID) { - - } else if (rawLog.jobID) { - // this.workflowLogs[workflow.uri.fsPath][Object.values(this.workflowLogs).length - 1].jobLogs.push({ - // name: '', - // status: '', - // stepLogs: [] - // }); - } else if (rawLog.jobResult) { - - } else { - - } - } - writeEmitter.fire(lines); - }); - - exec.stderr.on('data', (data) => { - const error = data.toString(); - writeEmitter.fire(error); - }); - - exec.on('close', (code) => { - closeEmitter.fire(code || 0); - }); }, - close: () => { } + close: () => { + if (this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].status === HistoryStatus.Running) { + this.workspaceHistory[workspaceFolder.uri.fsPath][historyIndex].status = HistoryStatus.Cancelled; + } + + historyTreeDataProvider.refresh(); + exec.stdout.destroy(); + exec.stdin.destroy(); + exec.stderr.destroy(); + exec.kill(); + } }; }) }); diff --git a/src/extension.ts b/src/extension.ts index f2c7ba8..9e2db3f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,12 +3,14 @@ import { window, workspace } from 'vscode'; import { Act } from './act'; import ComponentsTreeDataProvider from './views/components/componentsTreeDataProvider'; import { DecorationProvider } from './views/decorationProvider'; +import HistoryTreeDataProvider from './views/history/historyTreeDataProvider'; import SettingsTreeDataProvider from './views/settings/settingsTreeDataProvider'; import WorkflowsTreeDataProvider from './views/workflows/workflowsTreeDataProvider'; export let act: Act; export let componentsTreeDataProvider: ComponentsTreeDataProvider; export let workflowsTreeDataProvider: WorkflowsTreeDataProvider; +export let historyTreeDataProvider: HistoryTreeDataProvider; export let settingsTreeDataProvider: SettingsTreeDataProvider; export function activate(context: vscode.ExtensionContext) { @@ -22,6 +24,8 @@ export function activate(context: vscode.ExtensionContext) { const componentsTreeView = window.createTreeView(ComponentsTreeDataProvider.VIEW_ID, { treeDataProvider: componentsTreeDataProvider }); workflowsTreeDataProvider = new WorkflowsTreeDataProvider(context); const workflowsTreeView = window.createTreeView(WorkflowsTreeDataProvider.VIEW_ID, { treeDataProvider: workflowsTreeDataProvider }); + historyTreeDataProvider = new HistoryTreeDataProvider(context); + const historyTreeView = window.createTreeView(HistoryTreeDataProvider.VIEW_ID, { treeDataProvider: historyTreeDataProvider }); settingsTreeDataProvider = new SettingsTreeDataProvider(context); const settingsTreeView = window.createTreeView(SettingsTreeDataProvider.VIEW_ID, { treeDataProvider: settingsTreeDataProvider }); @@ -43,6 +47,7 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( componentsTreeView, workflowsTreeView, + historyTreeView, settingsTreeView, window.registerFileDecorationProvider(decorationProvider), workflowsFileWatcher diff --git a/src/views/history/history.ts b/src/views/history/history.ts new file mode 100644 index 0000000..dba52f5 --- /dev/null +++ b/src/views/history/history.ts @@ -0,0 +1,46 @@ +import { ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; +import { History, HistoryStatus } from "../../act"; +import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; + +export default class HistoryTreeItem extends TreeItem implements GithubLocalActionsTreeItem { + static contextValue = 'githubLocalActions.history'; + history: History; + + constructor(history: History) { + super(history.name, TreeItemCollapsibleState.None); + this.history = history; + + let totalDuration: string | undefined; + if (history.start) { + const start = new Date(history.start).getTime(); + const end = history.end ? new Date(history.end).getTime() : new Date().getTime(); + totalDuration = `${((end - start) / 1000).toFixed(0).toString()}s`; + this.description = totalDuration; + } + + this.contextValue = `${HistoryTreeItem.contextValue}_${history.status}`; + switch (history.status) { + case HistoryStatus.Running: + this.iconPath = new ThemeIcon('loading~spin'); + break; + case HistoryStatus.Success: + this.iconPath = new ThemeIcon('pass', new ThemeColor('GitHubLocalActions.green')); + break; + case HistoryStatus.Failed: + this.iconPath = new ThemeIcon('error', new ThemeColor('GitHubLocalActions.red')); + break; + case HistoryStatus.Cancelled: + this.iconPath = new ThemeIcon('circle-slash', new ThemeColor('GitHubLocalActions.yellow')); + break; + } + this.tooltip = `Name: ${history.name}\n` + + `Status: ${history.status}\n` + + `Started: ${history.start ? history.start : 'N/A'}\n` + + `Ended: ${history.end ? history.end : 'N/A'}\n` + + (totalDuration ? `Total Duration: ${totalDuration}\n` : ``); + } + + async getChildren(): Promise { + return []; + } +} \ No newline at end of file diff --git a/src/views/history/historyTreeDataProvider.ts b/src/views/history/historyTreeDataProvider.ts new file mode 100644 index 0000000..49d92b6 --- /dev/null +++ b/src/views/history/historyTreeDataProvider.ts @@ -0,0 +1,59 @@ +import { CancellationToken, commands, EventEmitter, ExtensionContext, extensions, TreeDataProvider, TreeItem, workspace } from "vscode"; +import { act } from "../../extension"; +import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; +import HistoryTreeItem from "./history"; + +export default class HistoryTreeDataProvider implements TreeDataProvider { + private _onDidChangeTreeData = new EventEmitter(); + readonly onDidChangeTreeData = this._onDidChangeTreeData.event; + static VIEW_ID = 'history'; + + constructor(context: ExtensionContext) { + extensions.onDidChange(e => { + this.refresh(); + }); + + context.subscriptions.push( + commands.registerCommand('githubLocalActions.refreshHistory', async () => { + this.refresh(); + }) + ); + } + + refresh(element?: GithubLocalActionsTreeItem) { + this._onDidChangeTreeData.fire(element); + } + + getTreeItem(element: GithubLocalActionsTreeItem): GithubLocalActionsTreeItem | Thenable { + return element; + } + + async resolveTreeItem(item: TreeItem, element: GithubLocalActionsTreeItem, token: CancellationToken): Promise { + if (element.getToolTip) { + element.tooltip = await element.getToolTip(); + } + + return element; + } + + async getChildren(element?: GithubLocalActionsTreeItem): Promise { + if (element) { + return element.getChildren(); + } else { + const items: GithubLocalActionsTreeItem[] = []; + + const workspaceFolders = workspace.workspaceFolders; + if (workspaceFolders && workspaceFolders.length > 0) { + const workspaceHistory = act.workspaceHistory[workspaceFolders[0].uri.fsPath]; //TODO: Fix for multi workspace support + if (workspaceHistory) { + for (const history of workspaceHistory) { + items.push(new HistoryTreeItem(history)); + } + } + } + + await commands.executeCommand('setContext', 'githubLocalActions:noHistory', items.length == 0); + return items; + } + } +} \ No newline at end of file diff --git a/src/views/settings/settingsTreeDataProvider.ts b/src/views/settings/settingsTreeDataProvider.ts index ca52d3c..3373c91 100644 --- a/src/views/settings/settingsTreeDataProvider.ts +++ b/src/views/settings/settingsTreeDataProvider.ts @@ -1,4 +1,5 @@ import { CancellationToken, commands, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem } from "vscode"; +import { act } from "../../extension"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import ContainerEnginesTreeItem from "./containerEngines"; import EnvironmentsTreeItem from "./environments"; @@ -40,14 +41,22 @@ export default class SettingsTreeDataProvider implements TreeDataProvider 0) { + items.push(...[ + new EnvironmentsTreeItem(), + new SecretsTreeItem(), + new VariablesTreeItem(), + new InputsTreeItem(), + new RunnersTreeItem(), + new ContainerEnginesTreeItem() + ]); + } + + await commands.executeCommand('setContext', 'githubLocalActions:noSettings', items.length == 0); + return items; } } } \ No newline at end of file diff --git a/src/views/workflows/job.ts b/src/views/workflows/job.ts new file mode 100644 index 0000000..6a71906 --- /dev/null +++ b/src/views/workflows/job.ts @@ -0,0 +1,23 @@ +import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; +import { Job, Workflow } from "../../workflowsManager"; +import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; + +export default class JobTreeItem extends TreeItem implements GithubLocalActionsTreeItem { + static contextValue = 'githubLocalActions.job'; + job: Job; + workflow: Workflow; + + constructor(workflow: Workflow, job: Job) { + super(job.name, TreeItemCollapsibleState.None); + this.workflow = workflow; + this.job = job; + this.contextValue = JobTreeItem.contextValue; + this.iconPath = new ThemeIcon('rocket'); + this.tooltip = `Name: ${job.name}` + + `ID: ${job.id}`; + } + + async getChildren(): Promise { + return []; + } +} \ No newline at end of file diff --git a/src/views/workflows/jobLog.ts b/src/views/workflows/jobLog.ts deleted file mode 100644 index 2c4cf0c..0000000 --- a/src/views/workflows/jobLog.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; -import { JobLog } from "../../act"; -import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; -import StepTreeItem from "./stepLog"; - -export default class JobLogTreeItem extends TreeItem implements GithubLocalActionsTreeItem { - static contextValue = 'githubLocalActions.jobLog'; - jobLog: JobLog; - - constructor(jobLog: JobLog) { - super(jobLog.name, TreeItemCollapsibleState.Collapsed); - this.jobLog = jobLog; - this.contextValue = JobLogTreeItem.contextValue; - this.iconPath = new ThemeIcon('pass-filled'); - // this.tooltip = `Name: ${workflow.name}\n` + - // `Path: ${workflow.uri.fsPath}\n` + - // (workflow.error ? `Error: ${workflow.error}` : ``); - - // TODO: Add tooltip and resourceUri - } - - async getChildren(): Promise { - const stepLogs = this.jobLog.stepLogs; - if (stepLogs) { - return this.jobLog.stepLogs.map(step => new StepTreeItem(step)); - } else { - return []; - } - } -} \ No newline at end of file diff --git a/src/views/workflows/stepLog.ts b/src/views/workflows/stepLog.ts deleted file mode 100644 index 63772a8..0000000 --- a/src/views/workflows/stepLog.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; -import { StepLog } from "../../act"; -import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; - -export default class StepLogTreeItem extends TreeItem implements GithubLocalActionsTreeItem { - static contextValue = 'githubLocalActions.stepLog'; - stepLog: StepLog; - - constructor(stepLog: StepLog) { - super(stepLog.name, TreeItemCollapsibleState.None); - this.stepLog = stepLog; - this.contextValue = StepLogTreeItem.contextValue; - this.iconPath = new ThemeIcon('pass-filled'); - // this.tooltip = `Name: ${workflow.name}\n` + - // `Path: ${workflow.uri.fsPath}\n` + - // (workflow.error ? `Error: ${workflow.error}` : ``); - - // TODO: Add tooltip and resourceUri - } - - async getChildren(): Promise { - return []; - } -} \ No newline at end of file diff --git a/src/views/workflows/workflow.ts b/src/views/workflows/workflow.ts index 81f6106..63d1875 100644 --- a/src/views/workflows/workflow.ts +++ b/src/views/workflows/workflow.ts @@ -1,8 +1,7 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; -import { act } from "../../extension"; import { Workflow } from "../../workflowsManager"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; -import WorkflowLogTreeItem from "./workflowLog"; +import JobTreeItem from "./job"; export default class WorkflowTreeItem extends TreeItem implements GithubLocalActionsTreeItem { static contextValue = 'githubLocalActions.workflow'; @@ -23,11 +22,15 @@ export default class WorkflowTreeItem extends TreeItem implements GithubLocalAct } async getChildren(): Promise { - const workflowLogs = act.workflowLogs[this.workflow.uri.fsPath]; - if (workflowLogs) { - return workflowLogs.map(workflowLog => new WorkflowLogTreeItem(workflowLog)); - } else { - return []; + const items: GithubLocalActionsTreeItem[] = []; + + const jobs = this.workflow.yaml.jobs; + if (jobs) { + for (const [key, value] of Object.entries(jobs)) { + items.push(new JobTreeItem(this.workflow, { name: value.name ? value.name : key, id: key })); + } } + + return items; } } \ No newline at end of file diff --git a/src/views/workflows/workflowLog.ts b/src/views/workflows/workflowLog.ts deleted file mode 100644 index 15d5367..0000000 --- a/src/views/workflows/workflowLog.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; -import { WorkflowLog } from "../../act"; -import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; -import JobLogTreeItem from "./jobLog"; - -export default class WorkflowLogTreeItem extends TreeItem implements GithubLocalActionsTreeItem { - static contextValue = 'githubLocalActions.workflowLog'; - workflowLog: WorkflowLog; - - constructor(workflowLog: WorkflowLog) { - super(workflowLog.name, TreeItemCollapsibleState.Collapsed); - this.workflowLog = workflowLog; - this.contextValue = WorkflowLogTreeItem.contextValue; - this.iconPath = new ThemeIcon('pass-filled'); - // this.tooltip = `Name: ${workflow.name}\n` + - // `Path: ${workflow.uri.fsPath}\n` + - // (workflow.error ? `Error: ${workflow.error}` : ``); - - // TODO: Add tooltip and resourceUri - } - - async getChildren(): Promise { - return this.workflowLog.jobLogs.map(jobLog => new JobLogTreeItem(jobLog)); - } -} \ No newline at end of file diff --git a/src/views/workflows/workflowsTreeDataProvider.ts b/src/views/workflows/workflowsTreeDataProvider.ts index 8634493..96bac6f 100644 --- a/src/views/workflows/workflowsTreeDataProvider.ts +++ b/src/views/workflows/workflowsTreeDataProvider.ts @@ -1,5 +1,5 @@ import { CancellationToken, commands, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, window, workspace } from "vscode"; -import { EventTrigger } from "../../act"; +import { Event } from "../../act"; import { act } from "../../extension"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import WorkflowTreeItem from "./workflow"; @@ -15,13 +15,14 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider { - const event = await window.showQuickPick(Object.values(EventTrigger), { + const event = await window.showQuickPick(Object.values(Event), { title: 'Select the event to run', placeHolder: 'Event' }); - if(event) { - await act.runEvent(event as EventTrigger); + if (event) { + // TODO: Implement running event + // await act.runEvent(event as Event); } }), commands.registerCommand('githubLocalActions.refreshWorkflows', async () => { @@ -33,6 +34,10 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider { await act.runWorkflow(workflowTreeItem.workflow); + }), + commands.registerCommand('githubLocalActions.runJob', async (workflowTreeItem: WorkflowTreeItem) => { + // TODO: Implement running job + // await act.runJob() }) ); } @@ -57,8 +62,15 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider new WorkflowTreeItem(workflow)); + for (const workflow of workflows) { + items.push(new WorkflowTreeItem(workflow)); + } + + await commands.executeCommand('setContext', 'githubLocalActions:noWorkflows', items.length == 0); + return items; } } } \ No newline at end of file diff --git a/src/workflowsManager.ts b/src/workflowsManager.ts index b40e7bc..55e1202 100644 --- a/src/workflowsManager.ts +++ b/src/workflowsManager.ts @@ -11,6 +11,11 @@ export interface Workflow { error?: string } +export interface Job { + name: string + id: string +} + export class WorkflowsManager { async getWorkflows(): Promise { const workflows: Workflow[] = [];