feat: Add Gitea workflow support (.gitea/workflows) - Support for both .github/workflows and .gitea/workflows directories - Fixed workflow execution path resolution - Cleaned up debugging code - Updated to version 1.2.5
Some checks failed
Test Gitea Workflow / test (push) Has been cancelled

This commit is contained in:
2025-08-03 23:00:22 +07:00
parent bc25f97d70
commit adf88e431c
8 changed files with 4699 additions and 1318 deletions

View File

@@ -0,0 +1,21 @@
name: Test Gitea Workflow
run-name: Test Gitea Workflow
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run tests
run: echo "Testing Gitea workflow"
- name: Build
run: echo "Building project"

2935
src/Extension Host.log Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,21 @@
import { ConfigurationTarget, workspace } from 'vscode'; import { ConfigurationTarget, workspace } from "vscode";
import { Act } from './act'; import { Act } from "./act";
import { WorkflowsManager } from './workflowsManager';
export enum Platform { export enum Platform {
windows = 'win32', windows = "win32",
mac = 'darwin', mac = "darwin",
linux = 'linux' linux = "linux",
} }
export enum Section { export enum Section {
actCommand = 'actCommand', actCommand = "actCommand",
workflowsDirectory = 'workflowsDirectory', dockerDesktopPath = "dockerDesktopPath",
dockerDesktopPath = 'dockerDesktopPath'
} }
export namespace ConfigurationManager { export namespace ConfigurationManager {
export const group: string = 'githubLocalActions'; export const group: string = "githubLocalActions";
export const searchPrefix: string = '@ext:sanjulaganepola.github-local-actions'; export const searchPrefix: string =
"@ext:sanjulaganepola.github-local-actions";
export async function initialize(): Promise<void> { export async function initialize(): Promise<void> {
let actCommand = ConfigurationManager.get<string>(Section.actCommand); let actCommand = ConfigurationManager.get<string>(Section.actCommand);
@@ -24,25 +23,32 @@ export namespace ConfigurationManager {
await ConfigurationManager.set(Section.actCommand, Act.defaultActCommand); await ConfigurationManager.set(Section.actCommand, Act.defaultActCommand);
} }
let workflowsDirectory = ConfigurationManager.get<string>(Section.workflowsDirectory); // Don't set a default workflows directory to allow multi-directory support
if (!workflowsDirectory) { // let workflowsDirectory = ConfigurationManager.get<string>(Section.workflowsDirectory);
await ConfigurationManager.set(Section.workflowsDirectory, WorkflowsManager.defaultWorkflowsDirectory); // if (!workflowsDirectory) {
} // await ConfigurationManager.set(Section.workflowsDirectory, WorkflowsManager.defaultWorkflowsDirectory);
// }
let dockerDesktopPath = ConfigurationManager.get<string>(Section.dockerDesktopPath); let dockerDesktopPath = ConfigurationManager.get<string>(
Section.dockerDesktopPath
);
if (!dockerDesktopPath) { if (!dockerDesktopPath) {
switch (process.platform) { switch (process.platform) {
case Platform.windows: case Platform.windows:
dockerDesktopPath = 'C:/Program Files/Docker/Docker/Docker Desktop.exe'; dockerDesktopPath =
"C:/Program Files/Docker/Docker/Docker Desktop.exe";
break; break;
case Platform.mac: case Platform.mac:
dockerDesktopPath = '/Applications/Docker.app'; dockerDesktopPath = "/Applications/Docker.app";
break; break;
default: default:
return; return;
} }
await ConfigurationManager.set(Section.dockerDesktopPath, dockerDesktopPath); await ConfigurationManager.set(
Section.dockerDesktopPath,
dockerDesktopPath
);
} }
} }
@@ -51,10 +57,14 @@ export namespace ConfigurationManager {
} }
export function get<T>(section: Section): T | undefined { export function get<T>(section: Section): T | undefined {
return workspace.getConfiguration(ConfigurationManager.group).get(section) as T; return workspace
.getConfiguration(ConfigurationManager.group)
.get(section) as T;
} }
export async function set(section: Section, value: any): Promise<void> { export async function set(section: Section, value: any): Promise<void> {
return await workspace.getConfiguration(ConfigurationManager.group).update(section, value, ConfigurationTarget.Global); return await workspace
.getConfiguration(ConfigurationManager.group)
.update(section, value, ConfigurationTarget.Global);
} }
} }

View File

@@ -1,15 +1,23 @@
import { commands, env, ExtensionContext, TreeCheckboxChangeEvent, Uri, window, workspace } from 'vscode'; import {
import { Act } from './act'; commands,
import { ConfigurationManager, Section } from './configurationManager'; env,
import { IssueHandler } from './issueHandler'; ExtensionContext,
import ComponentsTreeDataProvider from './views/components/componentsTreeDataProvider'; TreeCheckboxChangeEvent,
import { DecorationProvider } from './views/decorationProvider'; Uri,
import { GithubLocalActionsTreeItem } from './views/githubLocalActionsTreeItem'; window,
import HistoryTreeDataProvider from './views/history/historyTreeDataProvider'; workspace,
import SettingTreeItem from './views/settings/setting'; } from "vscode";
import SettingsTreeDataProvider from './views/settings/settingsTreeDataProvider'; import { Act } from "./act";
import WorkflowsTreeDataProvider from './views/workflows/workflowsTreeDataProvider'; import { ConfigurationManager, Section } from "./configurationManager";
import { WorkflowsManager } from './workflowsManager'; import { IssueHandler } from "./issueHandler";
import ComponentsTreeDataProvider from "./views/components/componentsTreeDataProvider";
import { DecorationProvider } from "./views/decorationProvider";
import { GithubLocalActionsTreeItem } from "./views/githubLocalActionsTreeItem";
import HistoryTreeDataProvider from "./views/history/historyTreeDataProvider";
import SettingTreeItem from "./views/settings/setting";
import SettingsTreeDataProvider from "./views/settings/settingsTreeDataProvider";
import WorkflowsTreeDataProvider from "./views/workflows/workflowsTreeDataProvider";
import { WorkflowsManager } from "./workflowsManager";
export let act: Act; export let act: Act;
export let componentsTreeDataProvider: ComponentsTreeDataProvider; export let componentsTreeDataProvider: ComponentsTreeDataProvider;
@@ -18,47 +26,63 @@ export let historyTreeDataProvider: HistoryTreeDataProvider;
export let settingsTreeDataProvider: SettingsTreeDataProvider; export let settingsTreeDataProvider: SettingsTreeDataProvider;
export function activate(context: ExtensionContext) { export function activate(context: ExtensionContext) {
console.log('Congratulations, your extension "github-local-actions" is now active!'); console.log(
'Congratulations, your extension "github-local-actions" is now active!'
);
act = new Act(context); act = new Act(context);
// Create tree views // Create tree views
const decorationProvider = new DecorationProvider(); const decorationProvider = new DecorationProvider();
componentsTreeDataProvider = new ComponentsTreeDataProvider(context); componentsTreeDataProvider = new ComponentsTreeDataProvider(context);
const componentsTreeView = window.createTreeView(ComponentsTreeDataProvider.VIEW_ID, { treeDataProvider: componentsTreeDataProvider, showCollapseAll: true }); const componentsTreeView = window.createTreeView(
ComponentsTreeDataProvider.VIEW_ID,
{ treeDataProvider: componentsTreeDataProvider, showCollapseAll: true }
);
workflowsTreeDataProvider = new WorkflowsTreeDataProvider(context); workflowsTreeDataProvider = new WorkflowsTreeDataProvider(context);
const workflowsTreeView = window.createTreeView(WorkflowsTreeDataProvider.VIEW_ID, { treeDataProvider: workflowsTreeDataProvider, showCollapseAll: true }); const workflowsTreeView = window.createTreeView(
WorkflowsTreeDataProvider.VIEW_ID,
{ treeDataProvider: workflowsTreeDataProvider, showCollapseAll: true }
);
historyTreeDataProvider = new HistoryTreeDataProvider(context); historyTreeDataProvider = new HistoryTreeDataProvider(context);
const historyTreeView = window.createTreeView(HistoryTreeDataProvider.VIEW_ID, { treeDataProvider: historyTreeDataProvider, showCollapseAll: true }); const historyTreeView = window.createTreeView(
HistoryTreeDataProvider.VIEW_ID,
{ treeDataProvider: historyTreeDataProvider, showCollapseAll: true }
);
settingsTreeDataProvider = new SettingsTreeDataProvider(context); settingsTreeDataProvider = new SettingsTreeDataProvider(context);
const settingsTreeView = window.createTreeView(SettingsTreeDataProvider.VIEW_ID, { treeDataProvider: settingsTreeDataProvider, showCollapseAll: true }); const settingsTreeView = window.createTreeView(
settingsTreeView.onDidChangeCheckboxState(async (event: TreeCheckboxChangeEvent<GithubLocalActionsTreeItem>) => { SettingsTreeDataProvider.VIEW_ID,
await settingsTreeDataProvider.onDidChangeCheckboxState(event as TreeCheckboxChangeEvent<SettingTreeItem>); { treeDataProvider: settingsTreeDataProvider, showCollapseAll: true }
}); );
settingsTreeView.onDidChangeCheckboxState(
async (event: TreeCheckboxChangeEvent<GithubLocalActionsTreeItem>) => {
await settingsTreeDataProvider.onDidChangeCheckboxState(
event as TreeCheckboxChangeEvent<SettingTreeItem>
);
}
);
// Create file watcher // Create file watcher
let workflowsFileWatcher = setupFileWatcher(context); let workflowsFileWatcher = setupFileWatcher(context);
// Initialize configurations // Initialize configurations
ConfigurationManager.initialize(); ConfigurationManager.initialize();
workspace.onDidChangeConfiguration(async event => { workspace.onDidChangeConfiguration(async (event) => {
if (event.affectsConfiguration(ConfigurationManager.group)) { if (event.affectsConfiguration(ConfigurationManager.group)) {
await ConfigurationManager.initialize(); await ConfigurationManager.initialize();
if (event.affectsConfiguration(`${ConfigurationManager.group}.${Section.actCommand}`) || if (
event.affectsConfiguration(`${ConfigurationManager.group}.${Section.dockerDesktopPath}`)) { event.affectsConfiguration(
`${ConfigurationManager.group}.${Section.actCommand}`
) ||
event.affectsConfiguration(
`${ConfigurationManager.group}.${Section.dockerDesktopPath}`
)
) {
componentsTreeDataProvider.refresh(); componentsTreeDataProvider.refresh();
} }
if (event.affectsConfiguration(`${ConfigurationManager.group}.${Section.workflowsDirectory}`)) {
workflowsTreeDataProvider.refresh();
settingsTreeDataProvider.refresh();
if (workflowsFileWatcher) {
workflowsFileWatcher.dispose();
workflowsFileWatcher = setupFileWatcher(context);
}
}
} }
}); });
@@ -69,18 +93,31 @@ export function activate(context: ExtensionContext) {
settingsTreeView, settingsTreeView,
window.registerFileDecorationProvider(decorationProvider), window.registerFileDecorationProvider(decorationProvider),
workflowsFileWatcher, workflowsFileWatcher,
commands.registerCommand('githubLocalActions.viewDocumentation', async () => { commands.registerCommand(
await env.openExternal(Uri.parse('https://sanjulaganepola.github.io/github-local-actions-docs')); "githubLocalActions.viewDocumentation",
}), async () => {
commands.registerCommand('githubLocalActions.reportAnIssue', async () => { await env.openExternal(
Uri.parse(
"https://sanjulaganepola.github.io/github-local-actions-docs"
)
);
}
),
commands.registerCommand("githubLocalActions.reportAnIssue", async () => {
await IssueHandler.openBugReport(context); await IssueHandler.openBugReport(context);
}), })
); );
} }
function setupFileWatcher(context: ExtensionContext) { function setupFileWatcher(context: ExtensionContext) {
const workflowsDirectory = WorkflowsManager.getWorkflowsDirectory(); const workflowsDirectories = WorkflowsManager.getWorkflowsDirectories();
const workflowsFileWatcher = workspace.createFileSystemWatcher(`**/${workflowsDirectory}/*.{${WorkflowsManager.ymlExtension},${WorkflowsManager.yamlExtension}}`); const fileWatchers: any[] = [];
for (const workflowsDirectory of workflowsDirectories) {
const workflowsFileWatcher = workspace.createFileSystemWatcher(
`**/${workflowsDirectory}/*.{${WorkflowsManager.ymlExtension},${WorkflowsManager.yamlExtension}}`
);
workflowsFileWatcher.onDidCreate(() => { workflowsFileWatcher.onDidCreate(() => {
workflowsTreeDataProvider.refresh(); workflowsTreeDataProvider.refresh();
settingsTreeDataProvider.refresh(); settingsTreeDataProvider.refresh();
@@ -94,7 +131,15 @@ function setupFileWatcher(context: ExtensionContext) {
settingsTreeDataProvider.refresh(); settingsTreeDataProvider.refresh();
}); });
return workflowsFileWatcher; fileWatchers.push(workflowsFileWatcher);
}
// Return a disposable that disposes all watchers
return {
dispose: () => {
fileWatchers.forEach((watcher) => watcher.dispose());
},
};
} }
export function deactivate() { } export function deactivate() {}

View File

@@ -1,5 +1,14 @@
import * as path from "path"; import * as path from "path";
import { CancellationToken, commands, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, window, workspace } from "vscode"; import {
CancellationToken,
commands,
EventEmitter,
ExtensionContext,
TreeDataProvider,
TreeItem,
window,
workspace,
} from "vscode";
import { Event } from "../../act"; import { Event } from "../../act";
import { act } from "../../extension"; import { act } from "../../extension";
import { Utils } from "../../utils"; import { Utils } from "../../utils";
@@ -9,51 +18,88 @@ import JobTreeItem from "./job";
import WorkflowTreeItem from "./workflow"; import WorkflowTreeItem from "./workflow";
import WorkspaceFolderWorkflowsTreeItem from "./workspaceFolderWorkflows"; import WorkspaceFolderWorkflowsTreeItem from "./workspaceFolderWorkflows";
export default class WorkflowsTreeDataProvider implements TreeDataProvider<GithubLocalActionsTreeItem> { export default class WorkflowsTreeDataProvider
private _onDidChangeTreeData = new EventEmitter<GithubLocalActionsTreeItem | undefined | null | void>(); implements TreeDataProvider<GithubLocalActionsTreeItem>
{
private _onDidChangeTreeData = new EventEmitter<
GithubLocalActionsTreeItem | undefined | null | void
>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event; readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
static VIEW_ID = 'workflows'; static VIEW_ID = "workflows";
constructor(context: ExtensionContext) { constructor(context: ExtensionContext) {
context.subscriptions.push( context.subscriptions.push(
commands.registerCommand('githubLocalActions.runAllWorkflows', async (workspaceFolderWorkflowsTreeItem?: WorkspaceFolderWorkflowsTreeItem) => { commands.registerCommand(
const workspaceFolder = await Utils.getWorkspaceFolder(workspaceFolderWorkflowsTreeItem?.workspaceFolder); "githubLocalActions.runAllWorkflows",
async (
workspaceFolderWorkflowsTreeItem?: WorkspaceFolderWorkflowsTreeItem
) => {
const workspaceFolder = await Utils.getWorkspaceFolder(
workspaceFolderWorkflowsTreeItem?.workspaceFolder
);
if (workspaceFolder) { if (workspaceFolder) {
await act.runAllWorkflows(workspaceFolder); await act.runAllWorkflows(workspaceFolder);
} }
}), }
commands.registerCommand('githubLocalActions.runEvent', async (workspaceFolderWorkflowsTreeItem?: WorkspaceFolderWorkflowsTreeItem) => { ),
const workspaceFolder = await Utils.getWorkspaceFolder(workspaceFolderWorkflowsTreeItem?.workspaceFolder); commands.registerCommand(
"githubLocalActions.runEvent",
async (
workspaceFolderWorkflowsTreeItem?: WorkspaceFolderWorkflowsTreeItem
) => {
const workspaceFolder = await Utils.getWorkspaceFolder(
workspaceFolderWorkflowsTreeItem?.workspaceFolder
);
if (workspaceFolder) { if (workspaceFolder) {
const event = await window.showQuickPick(Object.values(Event), { const event = await window.showQuickPick(Object.values(Event), {
title: 'Select the event to run', title: "Select the event to run",
placeHolder: 'Event' placeHolder: "Event",
}); });
if (event) { if (event) {
await act.runEvent(workspaceFolder, event as Event); await act.runEvent(workspaceFolder, event as Event);
} }
} }
}), }
commands.registerCommand('githubLocalActions.refreshWorkflows', async () => { ),
commands.registerCommand(
"githubLocalActions.refreshWorkflows",
async () => {
this.refresh(); this.refresh();
}), }
commands.registerCommand('githubLocalActions.openWorkflow', async (workflowTreeItem: WorkflowTreeItem) => { ),
commands.registerCommand(
"githubLocalActions.openWorkflow",
async (workflowTreeItem: WorkflowTreeItem) => {
try { try {
const document = await workspace.openTextDocument(workflowTreeItem.workflow.uri); const document = await workspace.openTextDocument(
workflowTreeItem.workflow.uri
);
await window.showTextDocument(document); await window.showTextDocument(document);
} catch (error: any) { } catch (error: any) {
try { try {
await workspace.fs.stat(workflowTreeItem.workflow.uri); await workspace.fs.stat(workflowTreeItem.workflow.uri);
window.showErrorMessage(`Failed to open workflow. Error: ${error}`); window.showErrorMessage(
`Failed to open workflow. Error: ${error}`
);
} catch (error: any) { } catch (error: any) {
window.showErrorMessage(`Workflow ${path.parse(workflowTreeItem.workflow.uri.fsPath).base} not found.`); window.showErrorMessage(
`Workflow ${
path.parse(workflowTreeItem.workflow.uri.fsPath).base
} not found.`
);
} }
} }
}), }
commands.registerCommand('githubLocalActions.runWorkflow', async (workflowTreeItem: WorkflowTreeItem) => { ),
commands.registerCommand(
"githubLocalActions.runWorkflow",
async (workflowTreeItem: WorkflowTreeItem) => {
if (workflowTreeItem) { if (workflowTreeItem) {
await act.runWorkflow(workflowTreeItem.workspaceFolder, workflowTreeItem.workflow); await act.runWorkflow(
workflowTreeItem.workspaceFolder,
workflowTreeItem.workflow
);
} else { } else {
let errorMessage: string | undefined; let errorMessage: string | undefined;
@@ -61,12 +107,21 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
if (activeTextEditor) { if (activeTextEditor) {
const uri = activeTextEditor.document.uri; const uri = activeTextEditor.document.uri;
const fileName = path.parse(uri.fsPath).base; const fileName = path.parse(uri.fsPath).base;
const workflowsDirectory = WorkflowsManager.getWorkflowsDirectory(); const workflowsDirectory =
if (uri.path.match(`.*/${workflowsDirectory}/.*\\.(${WorkflowsManager.yamlExtension}|${WorkflowsManager.ymlExtension})`)) { WorkflowsManager.getWorkflowsDirectory();
if (
uri.path.match(
`.*/${workflowsDirectory}/.*\\.(${WorkflowsManager.yamlExtension}|${WorkflowsManager.ymlExtension})`
)
) {
const workspaceFolder = workspace.getWorkspaceFolder(uri); const workspaceFolder = workspace.getWorkspaceFolder(uri);
if (workspaceFolder) { if (workspaceFolder) {
const workflows = await act.workflowsManager.getWorkflows(workspaceFolder); const workflows = await act.workflowsManager.getWorkflows(
const workflow = workflows.find(workflow => workflow.uri.fsPath === uri.fsPath); workspaceFolder
);
const workflow = workflows.find(
(workflow) => workflow.uri.fsPath === uri.fsPath
);
if (workflow) { if (workflow) {
await act.runWorkflow(workspaceFolder, workflow); await act.runWorkflow(workspaceFolder, workflow);
} else { } else {
@@ -79,57 +134,91 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
errorMessage = `${fileName} is not a workflow that can be executed locally.`; errorMessage = `${fileName} is not a workflow that can be executed locally.`;
} }
} else { } else {
errorMessage = 'No workflow opened to execute locally.'; errorMessage = "No workflow opened to execute locally.";
} }
if (errorMessage) { if (errorMessage) {
window.showErrorMessage(errorMessage, 'View Workflows').then(async value => { window
if (value === 'View Workflows') { .showErrorMessage(errorMessage, "View Workflows")
await commands.executeCommand('workflows.focus'); .then(async (value) => {
if (value === "View Workflows") {
await commands.executeCommand("workflows.focus");
} }
}); });
} }
} }
}), }
commands.registerCommand('githubLocalActions.runJob', async (jobTreeItem: JobTreeItem) => { ),
await act.runJob(jobTreeItem.workspaceFolder, jobTreeItem.workflow, jobTreeItem.job); commands.registerCommand(
}), "githubLocalActions.runJob",
commands.registerCommand('githubLocalActions.runWorkflowEvent', async (workflowTreeItem: WorkflowTreeItem) => { async (jobTreeItem: JobTreeItem) => {
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 // Filter to only events that are registered on the workflow
const registeredEventsOnWorkflow = Object.keys(workflowTreeItem.workflow.yaml.on); const registeredEventsOnWorkflow = Object.keys(
workflowTreeItem.workflow.yaml.on
);
if (registeredEventsOnWorkflow.length === 0) { 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.`); 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; return;
} }
const event = await window.showQuickPick(registeredEventsOnWorkflow, { const event = await window.showQuickPick(registeredEventsOnWorkflow, {
title: 'Select the event to run', title: "Select the event to run",
placeHolder: 'Event', placeHolder: "Event",
}); });
if (event) { if (event) {
await act.runEvent(workflowTreeItem.workspaceFolder, event as Event, { workflow: workflowTreeItem.workflow }); await act.runEvent(
workflowTreeItem.workspaceFolder,
event as Event,
{ workflow: workflowTreeItem.workflow }
);
} }
}), }
commands.registerCommand('githubLocalActions.runJobEvent', async (jobTreeItem: JobTreeItem) => { ),
commands.registerCommand(
"githubLocalActions.runJobEvent",
async (jobTreeItem: JobTreeItem) => {
// Filter to only events that are registered on the job's parent workflow // Filter to only events that are registered on the job's parent workflow
const registeredEventsOnJobParentWorkflow = Object.keys(jobTreeItem.workflow.yaml.on); const registeredEventsOnJobParentWorkflow = Object.keys(
jobTreeItem.workflow.yaml.on
);
if (registeredEventsOnJobParentWorkflow.length === 0) { 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.`); 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; return;
} }
const event = await window.showQuickPick(registeredEventsOnJobParentWorkflow, { const event = await window.showQuickPick(
title: 'Select the event to run', registeredEventsOnJobParentWorkflow,
placeHolder: 'Event' {
}); title: "Select the event to run",
placeHolder: "Event",
}
);
if (event) { if (event) {
await act.runEvent(jobTreeItem.workspaceFolder, event as Event, { workflow: jobTreeItem.workflow, job: jobTreeItem.job }); await act.runEvent(jobTreeItem.workspaceFolder, event as Event, {
workflow: jobTreeItem.workflow,
job: jobTreeItem.job,
});
} }
}) }
)
); );
} }
@@ -137,11 +226,17 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
this._onDidChangeTreeData.fire(element); this._onDidChangeTreeData.fire(element);
} }
getTreeItem(element: GithubLocalActionsTreeItem): GithubLocalActionsTreeItem | Thenable<GithubLocalActionsTreeItem> { getTreeItem(
element: GithubLocalActionsTreeItem
): GithubLocalActionsTreeItem | Thenable<GithubLocalActionsTreeItem> {
return element; return element;
} }
async resolveTreeItem(item: TreeItem, element: GithubLocalActionsTreeItem, token: CancellationToken): Promise<GithubLocalActionsTreeItem> { async resolveTreeItem(
item: TreeItem,
element: GithubLocalActionsTreeItem,
token: CancellationToken
): Promise<GithubLocalActionsTreeItem> {
if (element.getToolTip) { if (element.getToolTip) {
element.tooltip = await element.getToolTip(); element.tooltip = await element.getToolTip();
} }
@@ -149,7 +244,9 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
return element; return element;
} }
async getChildren(element?: GithubLocalActionsTreeItem): Promise<GithubLocalActionsTreeItem[]> { async getChildren(
element?: GithubLocalActionsTreeItem
): Promise<GithubLocalActionsTreeItem[]> {
if (element) { if (element) {
return element.getChildren(); return element.getChildren();
} else { } else {
@@ -159,9 +256,15 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
const workspaceFolders = workspace.workspaceFolders; const workspaceFolders = workspace.workspaceFolders;
if (workspaceFolders) { if (workspaceFolders) {
if (workspaceFolders.length === 1) { if (workspaceFolders.length === 1) {
items.push(...await new WorkspaceFolderWorkflowsTreeItem(workspaceFolders[0]).getChildren()); items.push(
...(await new WorkspaceFolderWorkflowsTreeItem(
workspaceFolders[0]
).getChildren())
);
const workflows = await act.workflowsManager.getWorkflows(workspaceFolders[0]); const workflows = await act.workflowsManager.getWorkflows(
workspaceFolders[0]
);
if (workflows && workflows.length > 0) { if (workflows && workflows.length > 0) {
noWorkflows = false; noWorkflows = false;
} }
@@ -169,7 +272,9 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
for (const workspaceFolder of workspaceFolders) { for (const workspaceFolder of workspaceFolders) {
items.push(new WorkspaceFolderWorkflowsTreeItem(workspaceFolder)); items.push(new WorkspaceFolderWorkflowsTreeItem(workspaceFolder));
const workflows = await act.workflowsManager.getWorkflows(workspaceFolder); const workflows = await act.workflowsManager.getWorkflows(
workspaceFolder
);
if (workflows && workflows.length > 0) { if (workflows && workflows.length > 0) {
noWorkflows = false; noWorkflows = false;
} }
@@ -177,7 +282,11 @@ export default class WorkflowsTreeDataProvider implements TreeDataProvider<Githu
} }
} }
await commands.executeCommand('setContext', 'githubLocalActions:noWorkflows', noWorkflows); await commands.executeCommand(
"setContext",
"githubLocalActions:noWorkflows",
noWorkflows
);
return items; return items;
} }
} }

View File

@@ -2,56 +2,83 @@ import * as fs from "fs/promises";
import * as path from "path"; import * as path from "path";
import { RelativePattern, Uri, workspace, WorkspaceFolder } from "vscode"; import { RelativePattern, Uri, workspace, WorkspaceFolder } from "vscode";
import * as yaml from "yaml"; import * as yaml from "yaml";
import { ConfigurationManager, Section } from "./configurationManager";
export interface Workflow { export interface Workflow {
name: string, name: string;
uri: Uri, uri: Uri;
fileContent?: string, fileContent?: string;
yaml?: any, yaml?: any;
error?: string error?: string;
} }
export interface Job { export interface Job {
name: string name: string;
id: string id: string;
} }
export class WorkflowsManager { export class WorkflowsManager {
static defaultWorkflowsDirectory: string = '.github/workflows'; static defaultWorkflowsDirectory: string = ".github/workflows";
static yamlExtension: string = 'yaml'; static giteaWorkflowsDirectory: string = ".gitea/workflows";
static ymlExtension: string = 'yml'; static yamlExtension: string = "yaml";
static ymlExtension: string = "yml";
static getWorkflowsDirectories(): string[] {
const directories = [
WorkflowsManager.defaultWorkflowsDirectory,
WorkflowsManager.giteaWorkflowsDirectory,
];
return directories;
}
static getWorkflowsDirectory(): string { static getWorkflowsDirectory(): string {
return ConfigurationManager.get<string>(Section.workflowsDirectory) || WorkflowsManager.defaultWorkflowsDirectory; return WorkflowsManager.defaultWorkflowsDirectory;
} }
async getWorkflows(workspaceFolder: WorkspaceFolder): Promise<Workflow[]> { async getWorkflows(workspaceFolder: WorkspaceFolder): Promise<Workflow[]> {
const workflows: Workflow[] = []; const workflows: Workflow[] = [];
const workflowsDirectory = WorkflowsManager.getWorkflowsDirectory(); const workflowsDirectories = WorkflowsManager.getWorkflowsDirectories();
const workflowFileUris = await workspace.findFiles(new RelativePattern(workspaceFolder, `${workflowsDirectory}/*.{${WorkflowsManager.yamlExtension},${WorkflowsManager.ymlExtension}}`));
for (const workflowsDirectory of workflowsDirectories) {
try {
const workflowFileUris = await workspace.findFiles(
new RelativePattern(
workspaceFolder,
`${workflowsDirectory}/*.{${WorkflowsManager.yamlExtension},${WorkflowsManager.ymlExtension}}`
)
);
for await (const workflowFileUri of workflowFileUris) { for await (const workflowFileUri of workflowFileUris) {
let yamlContent: any | undefined; 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); yamlContent = yaml.parse(fileContent);
workflows.push({ workflows.push({
name: yamlContent.name || path.parse(workflowFileUri.fsPath).name, name: yamlContent.name || path.parse(workflowFileUri.fsPath).name,
uri: workflowFileUri, uri: workflowFileUri,
fileContent: fileContent, fileContent: fileContent,
yaml: yaml.parse(fileContent) yaml: yaml.parse(fileContent),
}); });
} catch (error: any) { } catch (error: any) {
workflows.push({ workflows.push({
name: (yamlContent ? yamlContent.name : undefined) || path.parse(workflowFileUri.fsPath).name, name:
(yamlContent ? yamlContent.name : undefined) ||
path.parse(workflowFileUri.fsPath).name,
uri: workflowFileUri, uri: workflowFileUri,
error: 'Failed to parse workflow' error: "Failed to parse workflow",
}); });
} }
} }
} catch (error) {
// Directory doesn't exist, skip it
continue;
}
}
return workflows; return workflows;
} }