From eebee47f40593d8bb72de37e69f4280e51ebb1c4 Mon Sep 17 00:00:00 2001 From: Andrew Glago Date: Thu, 3 Apr 2025 16:29:45 +0000 Subject: [PATCH] Add support for running specific events on workflows and jobs (#190) * feat: add support for running specific workflows * feat: extend registered commands * docs: add changelog entry, update readme * chore: remove 'access commands via' note, moved to documentation * docs: add @a11rew to contributors * fix: remove debug change * Update change log to link to release notes Signed-off-by: Sanjula Ganepola * Reorder actions for consistency Signed-off-by: Sanjula Ganepola * Improve type safety with optional options param and mandatory workflow param Signed-off-by: Sanjula Ganepola --------- Signed-off-by: Sanjula Ganepola Co-authored-by: Sanjula Ganepola --- CHANGELOG.md | 8 +- CONTRIBUTING.md | 1 + README.md | 2 + package.json | 77 ++++++++++++++++--- src/act.ts | 46 ++++++++++- .../workflows/workflowsTreeDataProvider.ts | 36 +++++++++ 6 files changed, 150 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02878ef..05af3e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,3 @@ # Change Log -All notable changes to the "github-local-actions" extension will be documented in this file. - -Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. - -## [1.0.0] - -- Initial release \ No newline at end of file +All notable changes to the "github-local-actions" extension will be documented in the [GitHub release notes](https://github.com/SanjulaGanepola/github-local-actions/releases). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d9d463..2a4ac5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,5 +25,6 @@ Thanks so much to everyone [who has contributed](https://github.com/SanjulaGanep * [@SanjulaGanepola](https://github.com/SanjulaGanepola) * [@ChristopherHX](https://github.com/ChristopherHX) +* [@a11rew](https://github.com/a11rew) Want to see your name on this list? Join us and contribute! \ No newline at end of file diff --git a/README.md b/README.md index ee71d8b..c343164 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ The `Workflows` view is where you can manage and run workflows locally. You have 2. **Run Single Workflow**: Run a single workflow in the workspace. 3. **Run Job**: Run a specific job in a workflow. 4. **Run Event**: Run multiple workflows using a [GitHub event](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows). +5. **Run Workflow Event**: Run a specific event on a workflow. +6. **Run Job Event**: Run a specific event on a job. ![Workflows View](https://raw.githubusercontent.com/SanjulaGanepola/github-local-actions/main/images/workflows-view.png) diff --git a/package.json b/package.json index 079399f..b50d6cf 100644 --- a/package.json +++ b/package.json @@ -182,24 +182,36 @@ "title": "Refresh", "icon": "$(refresh)" }, - { - "category": "GitHub Local Actions", - "command": "githubLocalActions.openWorkflow", - "title": "Open Workflow", - "icon": "$(go-to-file)" - }, { "category": "GitHub Local Actions", "command": "githubLocalActions.runWorkflow", "title": "Run Workflow", "icon": "$(debug-start)" }, + { + "category": "GitHub Local Actions", + "command": "githubLocalActions.runWorkflowEvent", + "title": "Run Workflow with Event", + "icon": "$(symbol-event)" + }, + { + "category": "GitHub Local Actions", + "command": "githubLocalActions.openWorkflow", + "title": "Open Workflow", + "icon": "$(go-to-file)" + }, { "category": "GitHub Local Actions", "command": "githubLocalActions.runJob", "title": "Run Job", "icon": "$(debug-start)" }, + { + "category": "GitHub Local Actions", + "command": "githubLocalActions.runJobEvent", + "title": "Run Job with Event", + "icon": "$(symbol-event)" + }, { "category": "GitHub Local Actions", "command": "githubLocalActions.clearAll", @@ -417,18 +429,26 @@ "command": "githubLocalActions.refreshWorkflows", "when": "never" }, - { - "command": "githubLocalActions.openWorkflow", - "when": "never" - }, { "command": "githubLocalActions.runWorkflow", "when": "never" }, + { + "command": "githubLocalActions.runWorkflowEvent", + "when": "never" + }, + { + "command": "githubLocalActions.openWorkflow", + "when": "never" + }, { "command": "githubLocalActions.runJob", "when": "never" }, + { + "command": "githubLocalActions.runJobEvent", + "when": "never" + }, { "command": "githubLocalActions.clearAll", "when": "never" @@ -630,20 +650,55 @@ "group": "inline@1" }, { - "command": "githubLocalActions.openWorkflow", + "command": "githubLocalActions.runWorkflow", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", "group": "inline@0" }, { "command": "githubLocalActions.runWorkflow", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", + "group": "workflows@0" + }, + { + "command": "githubLocalActions.runWorkflowEvent", + "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", "group": "inline@1" }, + { + "command": "githubLocalActions.runWorkflowEvent", + "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", + "group": "workflows@1" + }, + { + "command": "githubLocalActions.openWorkflow", + "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", + "group": "inline@2" + }, + { + "command": "githubLocalActions.openWorkflow", + "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", + "group": "workflows@2" + }, { "command": "githubLocalActions.runJob", "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", "group": "inline@0" }, + { + "command": "githubLocalActions.runJob", + "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", + "group": "jobs@0" + }, + { + "command": "githubLocalActions.runJobEvent", + "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", + "group": "inline@1" + }, + { + "command": "githubLocalActions.runJobEvent", + "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", + "group": "jobs@1" + }, { "command": "githubLocalActions.clearAll", "when": "view == history && viewItem =~ /^githubLocalActions.workspaceFolderHistory.*/ && workspaceFolderCount > 1", diff --git a/src/act.ts b/src/act.ts index d5dc5fb..dee0a2c 100644 --- a/src/act.ts +++ b/src/act.ts @@ -304,9 +304,51 @@ export class Act { }); } - async runEvent(workspaceFolder: WorkspaceFolder, event: Event) { + async runEvent(workspaceFolder: WorkspaceFolder, event: Event, options?: { workflow: Workflow, job?: Job }) { let eventExists: boolean = false; const workflowsDirectory = WorkflowsManager.getWorkflowsDirectory(); + + // If a specific workflow is provided, run the event on that workflow + if (options) { + if (event in options.workflow.yaml.on) { + // If a job is also provided, run the event on that specific job + if (options.job) { + return await this.runCommand({ + path: workspaceFolder.uri.fsPath, + workflow: options.workflow, + options: [ + `${event} ${Option.Workflows} "${workflowsDirectory}/${path.parse(options.workflow.uri.fsPath).base}"`, + `${Option.Job} "${options.job.id}"` + ], + name: `${options.workflow.name}/${options.job.name} (${event})`, + extraHeader: [ + { key: 'Workflow', value: options.workflow.name }, + { key: 'Job', value: options.job.name }, + { key: 'Event', value: event } + ] + }); + } else { + // Run the event on the entire workflow + return await this.runCommand({ + path: workspaceFolder.uri.fsPath, + workflow: options.workflow, + options: [ + `${event} ${Option.Workflows} "${workflowsDirectory}/${path.parse(options.workflow.uri.fsPath).base}"` + ], + name: `${options.workflow.name} (${event})`, + extraHeader: [ + { key: 'Workflow', value: options.workflow.name }, + { key: 'Event', value: event } + ] + }); + } + } else { + window.showErrorMessage(`Event "${event}" is not registered on the workflow "${options.workflow.name}"`); + return; + } + } + + // Otherwise, run the event on all matching workflows const workflows = await this.workflowsManager.getWorkflows(workspaceFolder); if (workflows.length > 0) { for (const workflow of workflows) { @@ -328,7 +370,7 @@ export class Act { } if (!eventExists) { - window.showErrorMessage(`No workflows triggered by the ${event} event.`); + window.showErrorMessage(`No workflows triggered by the "${event}" event.`); } } else { window.showErrorMessage('No workflows found.'); diff --git a/src/views/workflows/workflowsTreeDataProvider.ts b/src/views/workflows/workflowsTreeDataProvider.ts index d87fbfa..570d4e3 100644 --- a/src/views/workflows/workflowsTreeDataProvider.ts +++ b/src/views/workflows/workflowsTreeDataProvider.ts @@ -93,6 +93,42 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider { await act.runJob(jobTreeItem.workspaceFolder, jobTreeItem.workflow, jobTreeItem.job); + }), + commands.registerCommand('githubLocalActions.runWorkflowEvent', async (workflowTreeItem: WorkflowTreeItem) => { + // Filter to only events that are registered on the workflow + const registeredEventsOnWorkflow = Object.keys(workflowTreeItem.workflow.yaml.on); + + if (registeredEventsOnWorkflow.length === 0) { + window.showErrorMessage(`No events registered on the workflow (${workflowTreeItem.workflow.name}). Add an event to the \`on\` section of the workflow to trigger it.`); + return; + } + + const event = await window.showQuickPick(registeredEventsOnWorkflow, { + title: 'Select the event to run', + placeHolder: 'Event', + }); + + if (event) { + await act.runEvent(workflowTreeItem.workspaceFolder, event as Event, { workflow: workflowTreeItem.workflow }); + } + }), + commands.registerCommand('githubLocalActions.runJobEvent', async (jobTreeItem: JobTreeItem) => { + // Filter to only events that are registered on the job's parent workflow + const registeredEventsOnJobParentWorkflow = Object.keys(jobTreeItem.workflow.yaml.on); + + if (registeredEventsOnJobParentWorkflow.length === 0) { + window.showErrorMessage(`No events registered on the workflow (${jobTreeItem.workflow.name}). Add an event to the \`on\` section of the workflow to trigger it.`); + return; + } + + const event = await window.showQuickPick(registeredEventsOnJobParentWorkflow, { + title: 'Select the event to run', + placeHolder: 'Event' + }); + + if (event) { + await act.runEvent(jobTreeItem.workspaceFolder, event as Event, { workflow: jobTreeItem.workflow, job: jobTreeItem.job }); + } }) ); }