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",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"child_process": "^1.0.2",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1197,6 +1198,11 @@
|
||||
"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": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
|
||||
@@ -213,6 +213,7 @@
|
||||
"test": "vscode-test"
|
||||
},
|
||||
"dependencies": {
|
||||
"child_process": "^1.0.2",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -229,4 +230,4 @@
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 { 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 { Workflow } from "./workflowManager";
|
||||
|
||||
@@ -51,15 +52,14 @@ export class Act {
|
||||
}
|
||||
|
||||
static async runEvent(eventTrigger: EventTrigger) {
|
||||
return await Act.runCommand(`${Act.base} ${eventTrigger}`);
|
||||
// return await Act.runCommand(`${Act.base} ${eventTrigger}`);
|
||||
}
|
||||
|
||||
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();
|
||||
if (unreadyComponents.length > 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
const workspaceFolder = workspace.getWorkspaceFolder(workflow.uri);
|
||||
if (!workspaceFolder) {
|
||||
window.showErrorMessage('Failed to detect workspace folder');
|
||||
return;
|
||||
}
|
||||
|
||||
await tasks.executeTask({
|
||||
name: workflow?.name || 'act',
|
||||
name: workflow.name,
|
||||
detail: 'Run workflow',
|
||||
definition: { type: 'GitHub Local Actions' },
|
||||
source: 'GitHub Local Actions',
|
||||
scope: TaskScope.Workspace,
|
||||
scope: workspaceFolder || TaskScope.Workspace,
|
||||
isBackground: true,
|
||||
presentationOptions: {
|
||||
reveal: TaskRevealKind.Always,
|
||||
@@ -89,7 +95,42 @@ export class Act {
|
||||
problemMatchers: [],
|
||||
runOptions: {},
|
||||
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 {
|
||||
name: string,
|
||||
icon: string,
|
||||
status: Status,
|
||||
status: ComponentStatus,
|
||||
required: boolean
|
||||
message?: string
|
||||
}
|
||||
|
||||
export enum Status {
|
||||
export enum ComponentStatus {
|
||||
Enabled = 'Enabled',
|
||||
Warning = 'Warning',
|
||||
Disabled = 'Disabled'
|
||||
@@ -18,26 +18,26 @@ export class ComponentManager {
|
||||
{
|
||||
name: 'nektos/act',
|
||||
icon: 'package',
|
||||
status: Status.Enabled,
|
||||
status: ComponentStatus.Enabled,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'Docker Engine',
|
||||
icon: 'dashboard',
|
||||
status: Status.Enabled,
|
||||
status: ComponentStatus.Enabled,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'GitHub Actions Extension',
|
||||
icon: 'extensions',
|
||||
status: Status.Warning,
|
||||
status: ComponentStatus.Warning,
|
||||
required: false,
|
||||
message: 'GitHub Actions extension is not required, but is recommended to take advantage of workflow editor features.'
|
||||
},
|
||||
{
|
||||
name: 'GitHub CLI',
|
||||
icon: 'terminal',
|
||||
status: Status.Warning,
|
||||
status: ComponentStatus.Warning,
|
||||
required: false,
|
||||
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[]> {
|
||||
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 { Status } from "../componentManager";
|
||||
import { ComponentStatus } from "../componentManager";
|
||||
import ComponentTreeItem from "./components/component";
|
||||
import WorkflowTreeItem from "./workflows/workflow";
|
||||
|
||||
@@ -9,17 +9,17 @@ export class DecorationProvider implements FileDecorationProvider {
|
||||
const params = new URLSearchParams(uri.query);
|
||||
|
||||
if (uri.scheme === ComponentTreeItem.contextValue) {
|
||||
if (params.get('status') === Status.Enabled) {
|
||||
if (params.get('status') === ComponentStatus.Enabled) {
|
||||
return {
|
||||
badge: '✅',
|
||||
color: new ThemeColor('GitHubLocalActions.green')
|
||||
};
|
||||
} else if (params.get('status') === Status.Warning) {
|
||||
} else if (params.get('status') === ComponentStatus.Warning) {
|
||||
return {
|
||||
badge: '⚠️',
|
||||
color: new ThemeColor('GitHubLocalActions.yellow')
|
||||
};
|
||||
} else if (params.get('status') === Status.Disabled) {
|
||||
} else if (params.get('status') === ComponentStatus.Disabled) {
|
||||
return {
|
||||
badge: '❌',
|
||||
color: new ThemeColor('GitHubLocalActions.red')
|
||||
|
||||
@@ -7,7 +7,7 @@ export default class WorkflowTreeItem extends TreeItem implements GithubLocalAct
|
||||
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.contextValue = WorkflowTreeItem.contextValue;
|
||||
this.iconPath = new ThemeIcon('layers');
|
||||
|
||||
@@ -10,7 +10,44 @@ export interface Workflow {
|
||||
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 {
|
||||
private workflowLogs: WorkflowLog[] = [];
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
static async getWorkflows(): Promise<Workflow[]> {
|
||||
const workflows: Workflow[] = [];
|
||||
|
||||
@@ -19,17 +56,20 @@ export class WorkflowManager {
|
||||
const workflowFileUris = await workspace.findFiles(`.github/workflows/*.{yml,yaml}`);
|
||||
|
||||
for await (const workflowFileUri of workflowFileUris) {
|
||||
let yamlContent: any | undefined;
|
||||
|
||||
try {
|
||||
const fileContent = await fs.readFile(workflowFileUri.fsPath, 'utf8');
|
||||
yamlContent = yaml.parse(fileContent);
|
||||
|
||||
workflows.push({
|
||||
name: path.parse(workflowFileUri.fsPath).name,
|
||||
name: yamlContent.name || path.parse(workflowFileUri.fsPath).name,
|
||||
uri: workflowFileUri,
|
||||
content: yaml.parse(fileContent)
|
||||
});
|
||||
} catch (error) {
|
||||
workflows.push({
|
||||
name: path.parse(workflowFileUri.fsPath).name,
|
||||
name: (yamlContent ? yamlContent.name : undefined) || path.parse(workflowFileUri.fsPath).name,
|
||||
uri: workflowFileUri,
|
||||
error: 'Failed to parse workflow file'
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user