Add run workflow support

Signed-off-by: Sanjula Ganepola <sanjulagane@gmail.com>
This commit is contained in:
Sanjula Ganepola
2024-09-26 22:22:56 -04:00
parent e96159f211
commit 7bd3c448c2
14 changed files with 142 additions and 51 deletions

View File

@@ -50,12 +50,12 @@
{ {
"id": "workflows", "id": "workflows",
"name": "Workflows", "name": "Workflows",
"icon": "$(remote-explorer)" "icon": "$(layers)"
}, },
{ {
"id": "settings", "id": "settings",
"name": "Settings", "name": "Settings",
"icon": "$(server-environment)" "icon": "$(gear)"
} }
] ]
}, },
@@ -66,6 +66,12 @@
"title": "Refresh", "title": "Refresh",
"icon": "$(refresh)" "icon": "$(refresh)"
}, },
{
"category": "GitHub Local Actions",
"command": "githubLocalActions.runAllWorkflows",
"title": "Run All Workflows",
"icon": "$(run-all)"
},
{ {
"category": "GitHub Local Actions", "category": "GitHub Local Actions",
"command": "githubLocalActions.refreshWorkflows", "command": "githubLocalActions.refreshWorkflows",
@@ -78,6 +84,12 @@
"title": "Open Workflow", "title": "Open Workflow",
"icon": "$(go-to-file)" "icon": "$(go-to-file)"
}, },
{
"category": "GitHub Local Actions",
"command": "githubLocalActions.runWorkflow",
"title": "Run Workflow",
"icon": "$(debug-start)"
},
{ {
"category": "GitHub Local Actions", "category": "GitHub Local Actions",
"command": "githubLocalActions.refreshSettings", "command": "githubLocalActions.refreshSettings",
@@ -91,6 +103,10 @@
"command": "githubLocalActions.refreshComponents", "command": "githubLocalActions.refreshComponents",
"when": "never" "when": "never"
}, },
{
"command": "githubLocalActions.runAllWorkflows",
"when": "never"
},
{ {
"command": "githubLocalActions.refreshWorkflows", "command": "githubLocalActions.refreshWorkflows",
"when": "never" "when": "never"
@@ -99,6 +115,10 @@
"command": "githubLocalActions.openWorkflow", "command": "githubLocalActions.openWorkflow",
"when": "never" "when": "never"
}, },
{
"command": "githubLocalActions.runWorkflow",
"when": "never"
},
{ {
"command": "githubLocalActions.refreshSettings", "command": "githubLocalActions.refreshSettings",
"when": "never" "when": "never"
@@ -111,10 +131,15 @@
"group": "navigation@0" "group": "navigation@0"
}, },
{ {
"command": "githubLocalActions.refreshWorkflows", "command": "githubLocalActions.runAllWorkflows",
"when": "view == workflows", "when": "view == workflows",
"group": "navigation@0" "group": "navigation@0"
}, },
{
"command": "githubLocalActions.refreshWorkflows",
"when": "view == workflows",
"group": "navigation@1"
},
{ {
"command": "githubLocalActions.refreshSettings", "command": "githubLocalActions.refreshSettings",
"when": "view == settings", "when": "view == settings",
@@ -124,8 +149,13 @@
"view/item/context": [ "view/item/context": [
{ {
"command": "githubLocalActions.openWorkflow", "command": "githubLocalActions.openWorkflow",
"when": "view == workflows && viewItem =~ /^workflow.*/", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/",
"group": "inline@0" "group": "inline@0"
},
{
"command": "githubLocalActions.runWorkflow",
"when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/",
"group": "inline@1"
} }
] ]
}, },

55
src/act.ts Normal file
View File

@@ -0,0 +1,55 @@
import * as path from "path";
import { commands, ShellExecution, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, window, workspace } from "vscode";
import { ComponentManager } from "./componentManager";
import { Workflow } from "./workflowManager";
export enum Options {
Workflows = '-W'
}
export class Act {
private static base: string = 'act';
static async runAllWorkflows() {
// TODO: Implement
}
static async runWorkflow(workflow: Workflow) {
return await Act.runCommand(workflow, `${Act.base} ${Options.Workflows} '.github/workflows/${path.parse(workflow.uri.fsPath).base}'`);
}
static async runCommand(workflow: Workflow, command: string) {
const unreadyComponents = await ComponentManager.getUnreadyComponents();
if (unreadyComponents.length > 0) {
window.showErrorMessage(`The following required components are not ready: ${unreadyComponents.map(component => component.name).join(', ')}`, 'Fix...').then(async value => {
if (value === 'Fix...') {
await commands.executeCommand('components.focus');
}
});
return;
}
await tasks.executeTask({
name: workflow.name,
detail: 'Run workflow',
definition: { type: 'GitHub Local Actions' },
source: 'GitHub Local Actions',
scope: workspace.getWorkspaceFolder(workflow.uri),
isBackground: true,
presentationOptions: {
reveal: TaskRevealKind.Always,
focus: false,
clear: false,
close: false,
echo: true,
panel: TaskPanelKind.Dedicated,
showReuseMessage: false
},
problemMatchers: [],
runOptions: {},
group: TaskGroup.Build,
execution: new ShellExecution(command)
});
}
}

View File

@@ -1,7 +1,8 @@
export interface Component { export interface Component {
name: string, name: string,
status: Status,
icon: string, icon: string,
status: Status,
required: boolean
message?: string message?: string
} }
@@ -12,32 +13,39 @@ export enum Status {
} }
export class ComponentManager { export class ComponentManager {
components: Component[] = [ static async getComponents(): Promise<Component[]> {
{ return [
name: 'nektos/act', {
status: Status.Enabled, name: 'nektos/act',
icon: 'package' icon: 'package',
}, status: Status.Enabled,
{ required: true
name: 'Docker Engine', },
status: Status.Disabled, {
icon: 'dashboard' name: 'Docker Engine',
}, icon: 'dashboard',
{ status: Status.Enabled,
name: 'GitHub Actions Extension', required: true
status: Status.Warning, },
icon: 'extensions', {
message: 'GitHub Actions extension is not required but is recommended to take advantage of workflow editor features' name: 'GitHub Actions Extension',
}, icon: 'extensions',
{ status: Status.Warning,
name: 'GitHub CLI', required: false,
status: Status.Warning, message: 'GitHub Actions extension is not required, but is recommended to take advantage of workflow editor features.'
icon: 'terminal', },
message: 'GitHub CLI is not required but is recommended if you plan to use it to retrieve GitHub tokens' {
} name: 'GitHub CLI',
]; icon: 'terminal',
status: Status.Warning,
required: false,
message: 'GitHub CLI is not required, but is recommended if you plan to use it to retrieve GitHub tokens.'
}
];
}
async getComponents(): Promise<Component[]> { static async getUnreadyComponents(): Promise<Component[]> {
return this.components; const components = await ComponentManager.getComponents();
return components.filter(component => component.required && component.status !== Status.Enabled);
} }
} }

View File

View File

@@ -1,9 +1,9 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
import { Component } from "../../componentManager"; import { Component } from "../../componentManager";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
export default class ComponentTreeItem extends TreeItem implements GithubLocalActionsTreeItem { export default class ComponentTreeItem extends TreeItem implements GithubLocalActionsTreeItem {
static contextValue = 'component'; static contextValue = 'githubLocalActions.component';
component: Component; component: Component;
constructor(component: Component) { constructor(component: Component) {

View File

@@ -6,12 +6,9 @@ import ComponentTreeItem from "./component";
export default class ComponentsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> { export default class ComponentsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> {
private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>(); private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event; readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
public static VIEW_ID = 'components'; static VIEW_ID = 'components';
private componentManager: ComponentManager;
constructor(context: ExtensionContext) { constructor(context: ExtensionContext) {
this.componentManager = new ComponentManager();
context.subscriptions.push( context.subscriptions.push(
commands.registerCommand('githubLocalActions.refreshComponents', async () => { commands.registerCommand('githubLocalActions.refreshComponents', async () => {
this.refresh(); this.refresh();
@@ -39,7 +36,7 @@ export default class ComponentsTreeDataProvider implements TreeDataProvider<Gith
if (element) { if (element) {
return element.getChildren(); return element.getChildren();
} else { } else {
const components = await this.componentManager.getComponents(); const components = await ComponentManager.getComponents();
return components.map(component => new ComponentTreeItem(component)); return components.map(component => new ComponentTreeItem(component));
} }
} }

View File

@@ -1,5 +1,5 @@
import { CancellationToken, Event, FileDecoration, FileDecorationProvider, ProviderResult, ThemeColor, Uri } from "vscode"; import { CancellationToken, Event, FileDecoration, FileDecorationProvider, ProviderResult, ThemeColor, Uri } from "vscode";
import { Status } from "../types"; import { Status } from "../componentManager";
import ComponentTreeItem from "./components/component"; import ComponentTreeItem from "./components/component";
import WorkflowTreeItem from "./workflows/workflow"; import WorkflowTreeItem from "./workflows/workflow";

View File

@@ -2,7 +2,7 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
export default class EnvironmentsTreeItem extends TreeItem implements GithubLocalActionsTreeItem { export default class EnvironmentsTreeItem extends TreeItem implements GithubLocalActionsTreeItem {
static contextValue = 'environments'; static contextValue = 'githubLocalActions.environments';
constructor() { constructor() {
super('Environments', TreeItemCollapsibleState.Collapsed); super('Environments', TreeItemCollapsibleState.Collapsed);

View File

@@ -2,7 +2,7 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
export default class SecretsTreeItem extends TreeItem implements GithubLocalActionsTreeItem { export default class SecretsTreeItem extends TreeItem implements GithubLocalActionsTreeItem {
static contextValue = 'secrets'; static contextValue = 'githubLocalActions.secrets';
constructor() { constructor() {
super('Secrets', TreeItemCollapsibleState.Collapsed); super('Secrets', TreeItemCollapsibleState.Collapsed);

View File

@@ -7,7 +7,7 @@ import VariablesTreeItem from "./variables";
export default class SettingsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> { export default class SettingsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> {
private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>(); private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event; readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
public static VIEW_ID = 'settings'; static VIEW_ID = 'settings';
constructor(context: ExtensionContext) { constructor(context: ExtensionContext) {
context.subscriptions.push( context.subscriptions.push(

View File

@@ -2,7 +2,7 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
export default class VariablesTreeItem extends TreeItem implements GithubLocalActionsTreeItem { export default class VariablesTreeItem extends TreeItem implements GithubLocalActionsTreeItem {
static contextValue = 'variables'; static contextValue = 'githubLocalActions.variables';
constructor() { constructor() {
super('Variables', TreeItemCollapsibleState.Collapsed); super('Variables', TreeItemCollapsibleState.Collapsed);

View File

@@ -3,7 +3,7 @@ import { Workflow } from "../../workflowManager";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
export default class WorkflowTreeItem extends TreeItem implements GithubLocalActionsTreeItem { export default class WorkflowTreeItem extends TreeItem implements GithubLocalActionsTreeItem {
static contextValue = 'workflow'; static contextValue = 'githubLocalActions.workflow';
workflow: Workflow; workflow: Workflow;
constructor(workflow: Workflow) { constructor(workflow: Workflow) {

View File

@@ -1,4 +1,5 @@
import { CancellationToken, commands, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, window, workspace } from "vscode"; import { CancellationToken, commands, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, window, workspace } from "vscode";
import { Act } from "../../act";
import { WorkflowManager } from "../../workflowManager"; import { WorkflowManager } from "../../workflowManager";
import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem"; import { GithubLocalActionsTreeItem } from "../githubLocalActionsTreeItem";
import WorkflowTreeItem from "./workflow"; import WorkflowTreeItem from "./workflow";
@@ -6,13 +7,13 @@ import WorkflowTreeItem from "./workflow";
export default class WorkflowsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> { export default class WorkflowsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> {
private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>(); private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event; readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
public static VIEW_ID = 'workflows'; static VIEW_ID = 'workflows';
private workflowManager: WorkflowManager;
constructor(context: ExtensionContext) { constructor(context: ExtensionContext) {
this.workflowManager = new WorkflowManager();
context.subscriptions.push( context.subscriptions.push(
commands.registerCommand('githubLocalActions.runAllWorkflows', async () => {
await Act.runAllWorkflows();
}),
commands.registerCommand('githubLocalActions.refreshWorkflows', async () => { commands.registerCommand('githubLocalActions.refreshWorkflows', async () => {
this.refresh(); this.refresh();
}), }),
@@ -21,7 +22,7 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
await window.showTextDocument(document); await window.showTextDocument(document);
}), }),
commands.registerCommand('githubLocalActions.runWorkflow', async (workflowTreeItem: WorkflowTreeItem) => { commands.registerCommand('githubLocalActions.runWorkflow', async (workflowTreeItem: WorkflowTreeItem) => {
await Act.runWorkflow(workflowTreeItem.workflow);
}) })
); );
} }
@@ -46,7 +47,7 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
if (element) { if (element) {
return element.getChildren(); return element.getChildren();
} else { } else {
const workflows = await this.workflowManager.getWorkflows(); const workflows = await WorkflowManager.getWorkflows();
return workflows.map(workflow => new WorkflowTreeItem(workflow)); return workflows.map(workflow => new WorkflowTreeItem(workflow));
} }
} }

View File

@@ -11,7 +11,7 @@ export interface Workflow {
} }
export class WorkflowManager { export class WorkflowManager {
async getWorkflows(): Promise<Workflow[]> { static async getWorkflows(): Promise<Workflow[]> {
const workflows: Workflow[] = []; const workflows: Workflow[] = [];
const workspaceFolders = workspace.workspaceFolders; const workspaceFolders = workspace.workspaceFolders;