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 <sanjulagane@gmail.com>

* Reorder actions for consistency

Signed-off-by: Sanjula Ganepola <sanjulagane@gmail.com>

* Improve type safety with optional options param and mandatory workflow param

Signed-off-by: Sanjula Ganepola <sanjulagane@gmail.com>

---------

Signed-off-by: Sanjula Ganepola <sanjulagane@gmail.com>
Co-authored-by: Sanjula Ganepola <sanjulagane@gmail.com>
This commit is contained in:
Andrew Glago
2025-04-03 16:29:45 +00:00
committed by GitHub
parent c505e8af9b
commit eebee47f40
6 changed files with 150 additions and 20 deletions

View File

@@ -1,9 +1,3 @@
# Change Log # Change Log
All notable changes to the "github-local-actions" extension will be documented in this 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).
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [1.0.0]
- Initial release

View File

@@ -25,5 +25,6 @@ Thanks so much to everyone [who has contributed](https://github.com/SanjulaGanep
* [@SanjulaGanepola](https://github.com/SanjulaGanepola) * [@SanjulaGanepola](https://github.com/SanjulaGanepola)
* [@ChristopherHX](https://github.com/ChristopherHX) * [@ChristopherHX](https://github.com/ChristopherHX)
* [@a11rew](https://github.com/a11rew)
Want to see your name on this list? Join us and contribute! Want to see your name on this list? Join us and contribute!

View File

@@ -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. 2. **Run Single Workflow**: Run a single workflow in the workspace.
3. **Run Job**: Run a specific job in a workflow. 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). 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) ![Workflows View](https://raw.githubusercontent.com/SanjulaGanepola/github-local-actions/main/images/workflows-view.png)

View File

@@ -182,24 +182,36 @@
"title": "Refresh", "title": "Refresh",
"icon": "$(refresh)" "icon": "$(refresh)"
}, },
{
"category": "GitHub Local Actions",
"command": "githubLocalActions.openWorkflow",
"title": "Open Workflow",
"icon": "$(go-to-file)"
},
{ {
"category": "GitHub Local Actions", "category": "GitHub Local Actions",
"command": "githubLocalActions.runWorkflow", "command": "githubLocalActions.runWorkflow",
"title": "Run Workflow", "title": "Run Workflow",
"icon": "$(debug-start)" "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", "category": "GitHub Local Actions",
"command": "githubLocalActions.runJob", "command": "githubLocalActions.runJob",
"title": "Run Job", "title": "Run Job",
"icon": "$(debug-start)" "icon": "$(debug-start)"
}, },
{
"category": "GitHub Local Actions",
"command": "githubLocalActions.runJobEvent",
"title": "Run Job with Event",
"icon": "$(symbol-event)"
},
{ {
"category": "GitHub Local Actions", "category": "GitHub Local Actions",
"command": "githubLocalActions.clearAll", "command": "githubLocalActions.clearAll",
@@ -417,18 +429,26 @@
"command": "githubLocalActions.refreshWorkflows", "command": "githubLocalActions.refreshWorkflows",
"when": "never" "when": "never"
}, },
{
"command": "githubLocalActions.openWorkflow",
"when": "never"
},
{ {
"command": "githubLocalActions.runWorkflow", "command": "githubLocalActions.runWorkflow",
"when": "never" "when": "never"
}, },
{
"command": "githubLocalActions.runWorkflowEvent",
"when": "never"
},
{
"command": "githubLocalActions.openWorkflow",
"when": "never"
},
{ {
"command": "githubLocalActions.runJob", "command": "githubLocalActions.runJob",
"when": "never" "when": "never"
}, },
{
"command": "githubLocalActions.runJobEvent",
"when": "never"
},
{ {
"command": "githubLocalActions.clearAll", "command": "githubLocalActions.clearAll",
"when": "never" "when": "never"
@@ -630,20 +650,55 @@
"group": "inline@1" "group": "inline@1"
}, },
{ {
"command": "githubLocalActions.openWorkflow", "command": "githubLocalActions.runWorkflow",
"when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/",
"group": "inline@0" "group": "inline@0"
}, },
{ {
"command": "githubLocalActions.runWorkflow", "command": "githubLocalActions.runWorkflow",
"when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/", "when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/",
"group": "workflows@0"
},
{
"command": "githubLocalActions.runWorkflowEvent",
"when": "view == workflows && viewItem =~ /^githubLocalActions.workflow.*/",
"group": "inline@1" "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", "command": "githubLocalActions.runJob",
"when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/", "when": "view == workflows && viewItem =~ /^githubLocalActions.job.*/",
"group": "inline@0" "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", "command": "githubLocalActions.clearAll",
"when": "view == history && viewItem =~ /^githubLocalActions.workspaceFolderHistory.*/ && workspaceFolderCount > 1", "when": "view == history && viewItem =~ /^githubLocalActions.workspaceFolderHistory.*/ && workspaceFolderCount > 1",

View File

@@ -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; let eventExists: boolean = false;
const workflowsDirectory = WorkflowsManager.getWorkflowsDirectory(); 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); const workflows = await this.workflowsManager.getWorkflows(workspaceFolder);
if (workflows.length > 0) { if (workflows.length > 0) {
for (const workflow of workflows) { for (const workflow of workflows) {
@@ -328,7 +370,7 @@ export class Act {
} }
if (!eventExists) { if (!eventExists) {
window.showErrorMessage(`No workflows triggered by the ${event} event.`); window.showErrorMessage(`No workflows triggered by the "${event}" event.`);
} }
} else { } else {
window.showErrorMessage('No workflows found.'); window.showErrorMessage('No workflows found.');

View File

@@ -93,6 +93,42 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
}), }),
commands.registerCommand('githubLocalActions.runJob', async (jobTreeItem: JobTreeItem) => { commands.registerCommand('githubLocalActions.runJob', async (jobTreeItem: JobTreeItem) => {
await act.runJob(jobTreeItem.workspaceFolder, jobTreeItem.workflow, jobTreeItem.job); 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 });
}
}) })
); );
} }