Rework to use CustomExecution
Signed-off-by: Sanjula Ganepola <sanjulagane@gmail.com>
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"child_process": "^1.0.2",
|
||||||
"yaml": "^2.5.1"
|
"yaml": "^2.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -1197,6 +1198,11 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/child_process": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g=="
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
|
|||||||
@@ -213,6 +213,7 @@
|
|||||||
"test": "vscode-test"
|
"test": "vscode-test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"child_process": "^1.0.2",
|
||||||
"yaml": "^2.5.1"
|
"yaml": "^2.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
57
src/act.ts
57
src/act.ts
@@ -1,5 +1,6 @@
|
|||||||
|
import * as child_process from 'child_process';
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { commands, ShellExecution, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, TaskScope, window } from "vscode";
|
import { commands, CustomExecution, EventEmitter, Pseudoterminal, TaskDefinition, TaskGroup, TaskPanelKind, TaskRevealKind, tasks, TaskScope, TerminalDimensions, window, workspace } from "vscode";
|
||||||
import { ComponentManager } from "./componentManager";
|
import { ComponentManager } from "./componentManager";
|
||||||
import { Workflow } from "./workflowManager";
|
import { Workflow } from "./workflowManager";
|
||||||
|
|
||||||
@@ -51,15 +52,14 @@ export class Act {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async runEvent(eventTrigger: EventTrigger) {
|
static async runEvent(eventTrigger: EventTrigger) {
|
||||||
return await Act.runCommand(`${Act.base} ${eventTrigger}`);
|
// return await Act.runCommand(`${Act.base} ${eventTrigger}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async runWorkflow(workflow: Workflow) {
|
static async runWorkflow(workflow: Workflow) {
|
||||||
return await Act.runCommand(`${Act.base} ${Option.Workflows} '.github/workflows/${path.parse(workflow.uri.fsPath).base}'`, workflow);
|
return await Act.runCommand(workflow, `${Act.base} ${Option.Workflows} ".github/workflows/${path.parse(workflow.uri.fsPath).base}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async runCommand(command: string, workflow?: Workflow) {
|
static async runCommand(workflow: Workflow, command: string) {
|
||||||
|
|
||||||
const unreadyComponents = await ComponentManager.getUnreadyComponents();
|
const unreadyComponents = await ComponentManager.getUnreadyComponents();
|
||||||
if (unreadyComponents.length > 0) {
|
if (unreadyComponents.length > 0) {
|
||||||
window.showErrorMessage(`The following required components are not ready: ${unreadyComponents.map(component => component.name).join(', ')}`, 'Fix...').then(async value => {
|
window.showErrorMessage(`The following required components are not ready: ${unreadyComponents.map(component => component.name).join(', ')}`, 'Fix...').then(async value => {
|
||||||
@@ -70,12 +70,18 @@ export class Act {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workspaceFolder = workspace.getWorkspaceFolder(workflow.uri);
|
||||||
|
if (!workspaceFolder) {
|
||||||
|
window.showErrorMessage('Failed to detect workspace folder');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await tasks.executeTask({
|
await tasks.executeTask({
|
||||||
name: workflow?.name || 'act',
|
name: workflow.name,
|
||||||
detail: 'Run workflow',
|
detail: 'Run workflow',
|
||||||
definition: { type: 'GitHub Local Actions' },
|
definition: { type: 'GitHub Local Actions' },
|
||||||
source: 'GitHub Local Actions',
|
source: 'GitHub Local Actions',
|
||||||
scope: TaskScope.Workspace,
|
scope: workspaceFolder || TaskScope.Workspace,
|
||||||
isBackground: true,
|
isBackground: true,
|
||||||
presentationOptions: {
|
presentationOptions: {
|
||||||
reveal: TaskRevealKind.Always,
|
reveal: TaskRevealKind.Always,
|
||||||
@@ -89,7 +95,42 @@ export class Act {
|
|||||||
problemMatchers: [],
|
problemMatchers: [],
|
||||||
runOptions: {},
|
runOptions: {},
|
||||||
group: TaskGroup.Build,
|
group: TaskGroup.Build,
|
||||||
execution: new ShellExecution(command)
|
execution: new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> => {
|
||||||
|
const writeEmitter = new EventEmitter<string>();
|
||||||
|
const closeEmitter = new EventEmitter<number>();
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDidWrite: writeEmitter.event,
|
||||||
|
onDidClose: closeEmitter.event,
|
||||||
|
open: async (initialDimensions: TerminalDimensions | undefined): Promise<void> => {
|
||||||
|
writeEmitter.fire(`Workflow: ${workflow.name}\r\nPath: ${workflow.uri.fsPath}\r\nCommand: ${command}\r\nTimestamp: ${new Date().toLocaleTimeString()}\r\n\r\n`);
|
||||||
|
|
||||||
|
const exec = child_process.spawn(command, { cwd: workspaceFolder.uri.fsPath, shell: '/usr/bin/bash' });
|
||||||
|
exec.stdout.on('data', (data) => {
|
||||||
|
const output = data.toString();
|
||||||
|
writeEmitter.fire(output);
|
||||||
|
|
||||||
|
if (output.includes('success')) {
|
||||||
|
window.showInformationMessage('Command succeeded!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec.stderr.on('data', (data) => {
|
||||||
|
const error = data.toString();
|
||||||
|
writeEmitter.fire(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
exec.on('close', (code) => {
|
||||||
|
closeEmitter.fire(code || 0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
close: () => {
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
export interface Component {
|
export interface Component {
|
||||||
name: string,
|
name: string,
|
||||||
icon: string,
|
icon: string,
|
||||||
status: Status,
|
status: ComponentStatus,
|
||||||
required: boolean
|
required: boolean
|
||||||
message?: string
|
message?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Status {
|
export enum ComponentStatus {
|
||||||
Enabled = 'Enabled',
|
Enabled = 'Enabled',
|
||||||
Warning = 'Warning',
|
Warning = 'Warning',
|
||||||
Disabled = 'Disabled'
|
Disabled = 'Disabled'
|
||||||
@@ -18,26 +18,26 @@ export class ComponentManager {
|
|||||||
{
|
{
|
||||||
name: 'nektos/act',
|
name: 'nektos/act',
|
||||||
icon: 'package',
|
icon: 'package',
|
||||||
status: Status.Enabled,
|
status: ComponentStatus.Enabled,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Docker Engine',
|
name: 'Docker Engine',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
status: Status.Enabled,
|
status: ComponentStatus.Enabled,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'GitHub Actions Extension',
|
name: 'GitHub Actions Extension',
|
||||||
icon: 'extensions',
|
icon: 'extensions',
|
||||||
status: Status.Warning,
|
status: ComponentStatus.Warning,
|
||||||
required: false,
|
required: false,
|
||||||
message: 'GitHub Actions extension is not required, but is recommended to take advantage of workflow editor features.'
|
message: 'GitHub Actions extension is not required, but is recommended to take advantage of workflow editor features.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'GitHub CLI',
|
name: 'GitHub CLI',
|
||||||
icon: 'terminal',
|
icon: 'terminal',
|
||||||
status: Status.Warning,
|
status: ComponentStatus.Warning,
|
||||||
required: false,
|
required: false,
|
||||||
message: 'GitHub CLI is not required, but is recommended if you plan to use it to retrieve GitHub tokens.'
|
message: 'GitHub CLI is not required, but is recommended if you plan to use it to retrieve GitHub tokens.'
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,6 @@ export class ComponentManager {
|
|||||||
|
|
||||||
static async getUnreadyComponents(): Promise<Component[]> {
|
static async getUnreadyComponents(): Promise<Component[]> {
|
||||||
const components = await ComponentManager.getComponents();
|
const components = await ComponentManager.getComponents();
|
||||||
return components.filter(component => component.required && component.status !== Status.Enabled);
|
return components.filter(component => component.required && component.status !== ComponentStatus.Enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 "../componentManager";
|
import { ComponentStatus } from "../componentManager";
|
||||||
import ComponentTreeItem from "./components/component";
|
import ComponentTreeItem from "./components/component";
|
||||||
import WorkflowTreeItem from "./workflows/workflow";
|
import WorkflowTreeItem from "./workflows/workflow";
|
||||||
|
|
||||||
@@ -9,17 +9,17 @@ export class DecorationProvider implements FileDecorationProvider {
|
|||||||
const params = new URLSearchParams(uri.query);
|
const params = new URLSearchParams(uri.query);
|
||||||
|
|
||||||
if (uri.scheme === ComponentTreeItem.contextValue) {
|
if (uri.scheme === ComponentTreeItem.contextValue) {
|
||||||
if (params.get('status') === Status.Enabled) {
|
if (params.get('status') === ComponentStatus.Enabled) {
|
||||||
return {
|
return {
|
||||||
badge: '✅',
|
badge: '✅',
|
||||||
color: new ThemeColor('GitHubLocalActions.green')
|
color: new ThemeColor('GitHubLocalActions.green')
|
||||||
};
|
};
|
||||||
} else if (params.get('status') === Status.Warning) {
|
} else if (params.get('status') === ComponentStatus.Warning) {
|
||||||
return {
|
return {
|
||||||
badge: '⚠️',
|
badge: '⚠️',
|
||||||
color: new ThemeColor('GitHubLocalActions.yellow')
|
color: new ThemeColor('GitHubLocalActions.yellow')
|
||||||
};
|
};
|
||||||
} else if (params.get('status') === Status.Disabled) {
|
} else if (params.get('status') === ComponentStatus.Disabled) {
|
||||||
return {
|
return {
|
||||||
badge: '❌',
|
badge: '❌',
|
||||||
color: new ThemeColor('GitHubLocalActions.red')
|
color: new ThemeColor('GitHubLocalActions.red')
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default class WorkflowTreeItem extends TreeItem implements GithubLocalAct
|
|||||||
workflow: Workflow;
|
workflow: Workflow;
|
||||||
|
|
||||||
constructor(workflow: Workflow) {
|
constructor(workflow: Workflow) {
|
||||||
super(workflow.content.name || workflow.name, workflow.error ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed);
|
super(workflow.name, workflow.error ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed);
|
||||||
this.workflow = workflow;
|
this.workflow = workflow;
|
||||||
this.contextValue = WorkflowTreeItem.contextValue;
|
this.contextValue = WorkflowTreeItem.contextValue;
|
||||||
this.iconPath = new ThemeIcon('layers');
|
this.iconPath = new ThemeIcon('layers');
|
||||||
|
|||||||
@@ -10,7 +10,44 @@ export interface Workflow {
|
|||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorkflowLog {
|
||||||
|
workflow: Workflow,
|
||||||
|
status: WorkflowStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WorkflowStatus {
|
||||||
|
Queued = 'queued',
|
||||||
|
InProgress = 'inProgress',
|
||||||
|
success = 'success',
|
||||||
|
failed = 'failed',
|
||||||
|
Cancelled = 'cancelled'
|
||||||
|
}
|
||||||
|
|
||||||
|
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 class WorkflowManager {
|
export class WorkflowManager {
|
||||||
|
private workflowLogs: WorkflowLog[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static async getWorkflows(): Promise<Workflow[]> {
|
static async getWorkflows(): Promise<Workflow[]> {
|
||||||
const workflows: Workflow[] = [];
|
const workflows: Workflow[] = [];
|
||||||
|
|
||||||
@@ -19,17 +56,20 @@ export class WorkflowManager {
|
|||||||
const workflowFileUris = await workspace.findFiles(`.github/workflows/*.{yml,yaml}`);
|
const workflowFileUris = await workspace.findFiles(`.github/workflows/*.{yml,yaml}`);
|
||||||
|
|
||||||
for await (const workflowFileUri of workflowFileUris) {
|
for await (const workflowFileUri of workflowFileUris) {
|
||||||
|
let yamlContent: any | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileContent = await fs.readFile(workflowFileUri.fsPath, 'utf8');
|
const fileContent = await fs.readFile(workflowFileUri.fsPath, 'utf8');
|
||||||
|
yamlContent = yaml.parse(fileContent);
|
||||||
|
|
||||||
workflows.push({
|
workflows.push({
|
||||||
name: path.parse(workflowFileUri.fsPath).name,
|
name: yamlContent.name || path.parse(workflowFileUri.fsPath).name,
|
||||||
uri: workflowFileUri,
|
uri: workflowFileUri,
|
||||||
content: yaml.parse(fileContent)
|
content: yaml.parse(fileContent)
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
workflows.push({
|
workflows.push({
|
||||||
name: path.parse(workflowFileUri.fsPath).name,
|
name: (yamlContent ? yamlContent.name : undefined) || path.parse(workflowFileUri.fsPath).name,
|
||||||
uri: workflowFileUri,
|
uri: workflowFileUri,
|
||||||
error: 'Failed to parse workflow file'
|
error: 'Failed to parse workflow file'
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user