Add history view, rework to use history status, remove old classes

Signed-off-by: Sanjula Ganepola <sanjulagane@gmail.com>
This commit is contained in:
Sanjula Ganepola
2024-10-17 19:38:47 -04:00
parent 05caeafbb2
commit 0e6baaaad1
13 changed files with 368 additions and 218 deletions

View File

@@ -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"
}
]
},

View File

@@ -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<any>(workflow.yaml.jobs).map(([key, value]) => {
return {
name: value.name ? value.name : key,
status: JobStatus.Queued,
stepLogs: Object.entries<any>(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<string>();
const closeEmitter = new EventEmitter<number>();
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<void> => {
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(`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();
}
};
})
});

View File

@@ -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

View File

@@ -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<GithubLocalActionsTreeItem[]> {
return [];
}
}

View File

@@ -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<GithubLocalActionsTreeItem> {
private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>();
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<GithubLocalActionsTreeItem> {
return element;
}
async resolveTreeItem(item: TreeItem, element: GithubLocalActionsTreeItem, token: CancellationToken): Promise<GithubLocalActionsTreeItem> {
if (element.getToolTip) {
element.tooltip = await element.getToolTip();
}
return element;
}
async getChildren(element?: GithubLocalActionsTreeItem): Promise<GithubLocalActionsTreeItem[]> {
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;
}
}
}

View File

@@ -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<Github
if (element) {
return element.getChildren();
} else {
return [
const items: GithubLocalActionsTreeItem[] = [];
const workflows = await act.workflowsManager.getWorkflows();
if (workflows.length > 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;
}
}
}

View File

@@ -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<GithubLocalActionsTreeItem[]> {
return [];
}
}

View File

@@ -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<GithubLocalActionsTreeItem[]> {
const stepLogs = this.jobLog.stepLogs;
if (stepLogs) {
return this.jobLog.stepLogs.map(step => new StepTreeItem(step));
} else {
return [];
}
}
}

View File

@@ -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<GithubLocalActionsTreeItem[]> {
return [];
}
}

View File

@@ -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<GithubLocalActionsTreeItem[]> {
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<any>(jobs)) {
items.push(new JobTreeItem(this.workflow, { name: value.name ? value.name : key, id: key }));
}
}
return items;
}
}

View File

@@ -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<GithubLocalActionsTreeItem[]> {
return this.workflowLog.jobLogs.map(jobLog => new JobLogTreeItem(jobLog));
}
}

View File

@@ -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<Githu
await act.runAllWorkflows();
}),
commands.registerCommand('githubLocalActions.runEvent', async () => {
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);
// 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<Githu
}),
commands.registerCommand('githubLocalActions.runWorkflow', async (workflowTreeItem: WorkflowTreeItem) => {
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<Githu
if (element) {
return element.getChildren();
} else {
const items: GithubLocalActionsTreeItem[] = [];
const workflows = await act.workflowsManager.getWorkflows();
return workflows.map(workflow => new WorkflowTreeItem(workflow));
for (const workflow of workflows) {
items.push(new WorkflowTreeItem(workflow));
}
await commands.executeCommand('setContext', 'githubLocalActions:noWorkflows', items.length == 0);
return items;
}
}
}

View File

@@ -11,6 +11,11 @@ export interface Workflow {
error?: string
}
export interface Job {
name: string
id: string
}
export class WorkflowsManager {
async getWorkflows(): Promise<Workflow[]> {
const workflows: Workflow[] = [];