├── .vscode └── settings.json ├── .github ├── FUNDING.yml ├── workflows │ ├── pr.yaml │ ├── publish.yaml │ └── _build.yaml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.yaml ├── src ├── Resources │ ├── Run.png │ ├── Delete.png │ ├── Edit.png │ ├── GitHub.png │ ├── Icon.png │ ├── AddItem.png │ ├── codicon.ttf │ ├── CancelBuild.png │ ├── OpenWebSite.png │ ├── UIStrings.resx │ └── UIStrings.Designer.cs ├── lib │ └── win32 │ │ ├── x64 │ │ └── git2-e632535.dll │ │ └── arm64 │ │ └── git2-e632535.dll ├── runtimes │ ├── win-x64 │ │ └── native │ │ │ └── libsodium.dll │ └── win-arm64 │ │ └── native │ │ └── libsodium.dll ├── Models │ ├── SimpleJob.cs │ ├── SimpleEnvironment.cs │ ├── SimpleRun.cs │ └── BaseWorkflowType.cs ├── ToolWindows │ ├── MessagePayload.cs │ ├── MessageCommand.cs │ ├── ToolWindowMessenger.cs │ ├── ActionsToolWindow.cs │ ├── GHActionsToolWindow.xaml │ └── GHActionsToolWindow.xaml.cs ├── Commands │ ├── ActionsToolWindowCommand.cs │ ├── GotoRepoCommand.cs │ ├── RefreshRepoCommand.cs │ ├── OpenSettingsCommand.cs │ └── ReportFeedbackCommand.cs ├── Helpers │ ├── ConclusionFilter.cs │ ├── CredentialManager.cs │ └── RepoInfo.cs ├── Properties │ └── AssemblyInfo.cs ├── GitHubActionsVS.slnx ├── source.extension.cs ├── LICENSE ├── Converters │ ├── ConclusionColorConverter.cs │ ├── ConclusionIconConverter.cs │ └── NullToVisibilityConverter.cs ├── VSCommandTable.cs ├── Options │ └── ExtensionOptions.cs ├── source.extension.vsixmanifest ├── UserControls │ ├── AddEditSecret.xaml.cs │ └── AddEditSecret.xaml ├── .gitattributes ├── VSCommandTable.vsct ├── GitHubActionsVSPackage.cs └── GitHubActionsVS.csproj ├── version.json ├── vs-publish.json ├── LICENSE ├── .all-contributorsrc ├── .gitattributes ├── README.md └── .gitignore /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.defaultSolution": "src/GitHubActionsVS.sln" 3 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [timheuer] 4 | -------------------------------------------------------------------------------- /src/Resources/Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/Run.png -------------------------------------------------------------------------------- /src/Resources/Delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/Delete.png -------------------------------------------------------------------------------- /src/Resources/Edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/Edit.png -------------------------------------------------------------------------------- /src/Resources/GitHub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/GitHub.png -------------------------------------------------------------------------------- /src/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/Icon.png -------------------------------------------------------------------------------- /src/Resources/AddItem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/AddItem.png -------------------------------------------------------------------------------- /src/Resources/codicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/codicon.ttf -------------------------------------------------------------------------------- /src/Resources/CancelBuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/CancelBuild.png -------------------------------------------------------------------------------- /src/Resources/OpenWebSite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/Resources/OpenWebSite.png -------------------------------------------------------------------------------- /src/lib/win32/x64/git2-e632535.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/lib/win32/x64/git2-e632535.dll -------------------------------------------------------------------------------- /src/lib/win32/arm64/git2-e632535.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/lib/win32/arm64/git2-e632535.dll -------------------------------------------------------------------------------- /src/runtimes/win-x64/native/libsodium.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/runtimes/win-x64/native/libsodium.dll -------------------------------------------------------------------------------- /src/runtimes/win-arm64/native/libsodium.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timheuer/GitHubActionsVS/HEAD/src/runtimes/win-arm64/native/libsodium.dll -------------------------------------------------------------------------------- /src/Models/SimpleJob.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubActionsVS.Models; 2 | 3 | public class SimpleJob : SimpleRun 4 | { 5 | public override string DisplayName => Name; 6 | } 7 | -------------------------------------------------------------------------------- /src/ToolWindows/MessagePayload.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubActionsVS.ToolWindows; 2 | 3 | public readonly record struct MessagePayload( 4 | MessageCommand Command, 5 | string Text = default); 6 | -------------------------------------------------------------------------------- /src/ToolWindows/MessageCommand.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubActionsVS.ToolWindows; 2 | 3 | public enum MessageCommand 4 | { 5 | GotoRepo, 6 | Refresh, 7 | OpenSettings, 8 | ReportFeedback 9 | } 10 | -------------------------------------------------------------------------------- /src/Models/SimpleEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GitHubActionsVS.Models; 4 | public class SimpleEnvironment 5 | { 6 | public string Name { get; set; } 7 | public string Url { get; set; } 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/ToolWindows/ToolWindowMessenger.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubActionsVS.ToolWindows; 2 | public class ToolWindowMessenger 3 | { 4 | public void Send(MessagePayload payload) 5 | { 6 | MessageReceived?.Invoke(this, payload); 7 | } 8 | 9 | public event EventHandler MessageReceived; 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/pr.yaml: -------------------------------------------------------------------------------- 1 | name: "Build PR" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '**/*.md' 9 | - '**/*.gitignore' 10 | - '**/*.gitattributes' 11 | 12 | jobs: 13 | build: 14 | name: Build and Test 15 | uses: ./.github/workflows/_build.yaml -------------------------------------------------------------------------------- /src/Commands/ActionsToolWindowCommand.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubActionsVS; 2 | 3 | [Command(PackageIds.ActionsCommand)] 4 | internal sealed class ActionsToolWindowCommand : BaseCommand 5 | { 6 | protected override Task ExecuteAsync(OleMenuCmdEventArgs e) 7 | { 8 | return ActionsToolWindow.ShowAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Models/SimpleRun.cs: -------------------------------------------------------------------------------- 1 | using Humanizer; 2 | using System.Collections.Generic; 3 | 4 | namespace GitHubActionsVS.Models; 5 | public class SimpleRun : BaseWorkflowType 6 | { 7 | public List Jobs { get; set; } 8 | public string RunNumber { get; set; } 9 | 10 | public override string DisplayName => $"{Name} #{RunNumber} ({LogDate.Humanize()})"; 11 | } 12 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "0.1", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/main", // we release out of main 6 | "^refs/tags/v\\d+\\.\\d+" // we also release tags starting with vN.N 7 | ], 8 | "cloudBuild": { 9 | "enabled": true 10 | } 11 | } -------------------------------------------------------------------------------- /vs-publish.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/vsix-publish", 3 | "categories": [ "build", "source control" ], 4 | "identity": { 5 | "internalName": "GitHubActionsVS", 6 | "tags": [ "github", "actions", "devops", "workflow" ] 7 | }, 8 | "overview": "README.md", 9 | "publisher": "TimHeuer", 10 | "repo": "https://github.com/timheuer/GitHubActionsVS", 11 | "qna": false 12 | } -------------------------------------------------------------------------------- /src/Helpers/ConclusionFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GitHubActionsVS.Helpers; 8 | public class ConclusionFilter 9 | { 10 | public static bool IsFinished(string conclusion) 11 | { 12 | return conclusion.ToLower() switch 13 | { 14 | "pending" or "waiting" or "queued" or "in_progress" or "inprogress" or "requested" or null => false, 15 | _ => true, 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Helpers/CredentialManager.cs: -------------------------------------------------------------------------------- 1 | using CredentialManagement; 2 | 3 | namespace GitHubActionsVS.Helpers; 4 | internal class CredentialManager 5 | { 6 | public static UserPass GetCredentials(string target) 7 | { 8 | using var cm = new Credential { Target = target }; 9 | if (!cm.Exists()) 10 | { 11 | return null; 12 | } 13 | 14 | cm.Load(); 15 | return new UserPass { Username = cm.Username, Password = cm.Password }; 16 | } 17 | 18 | public class UserPass 19 | { 20 | public string Username { get; set; } 21 | public string Password { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle(Vsix.Name)] 6 | [assembly: AssemblyDescription(Vsix.Description)] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany(Vsix.Author)] 9 | [assembly: AssemblyProduct(Vsix.Name)] 10 | [assembly: AssemblyCopyright(Vsix.Author)] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\LibGit2Sharp.dll")] 17 | [assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\Sodium.Core.dll")] 18 | 19 | namespace System.Runtime.CompilerServices; 20 | public class IsExternalInit { } -------------------------------------------------------------------------------- /src/Commands/GotoRepoCommand.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS.ToolWindows; 2 | 3 | namespace GitHubActionsVS; 4 | 5 | [Command(PackageIds.GotoRepoCommand)] 6 | internal sealed class GotoRepoCommand : BaseCommand 7 | { 8 | protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) 9 | { 10 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 11 | ThreadHelper.JoinableTaskFactory.RunAsync(async () => 12 | { 13 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 14 | ToolWindowMessenger messenger = await Package.GetServiceAsync(); 15 | messenger.Send(new(MessageCommand.GotoRepo)); 16 | }).FireAndForget(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Commands/RefreshRepoCommand.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS.ToolWindows; 2 | 3 | namespace GitHubActionsVS; 4 | 5 | [Command(PackageIds.RefreshRepoCommand)] 6 | internal sealed class RefreshRepoCommand : BaseCommand 7 | { 8 | protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) 9 | { 10 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 11 | ThreadHelper.JoinableTaskFactory.RunAsync(async () => 12 | { 13 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 14 | ToolWindowMessenger messenger = await Package.GetServiceAsync(); 15 | messenger.Send(new(MessageCommand.Refresh)); 16 | }).FireAndForget(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Commands/OpenSettingsCommand.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS.ToolWindows; 2 | 3 | namespace GitHubActionsVS; 4 | 5 | [Command(PackageIds.OpenSettingsCommand)] 6 | internal sealed class OpenSettingsCommand : BaseCommand 7 | { 8 | protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) 9 | { 10 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 11 | ThreadHelper.JoinableTaskFactory.RunAsync(async () => 12 | { 13 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 14 | ToolWindowMessenger messenger = await Package.GetServiceAsync(); 15 | messenger.Send(new(MessageCommand.OpenSettings)); 16 | }).FireAndForget(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/GitHubActionsVS.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by VSIX Synchronizer 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace GitHubActionsVS 7 | { 8 | internal sealed partial class Vsix 9 | { 10 | public const string Id = "TimHeuer.GitHubActions"; 11 | public const string Name = "GitHub Actions"; 12 | public const string Description = @"A window that provides a view of GitHub Actions for the current repo of the opened solution in Visual Studio. Provided by @timheuer"; 13 | public const string Language = "en-US"; 14 | public const string Version = "0.0.999"; 15 | public const string Author = "Tim Heuer"; 16 | public const string Tags = "github,actions,workflow,devops,dotnet"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Models/BaseWorkflowType.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubActionsVS.Models; 2 | 3 | public abstract class BaseWorkflowType 4 | { 5 | public string Name { get; set; } 6 | 7 | public abstract string DisplayName { get; } 8 | public string Conclusion { get; set; } 9 | public DateTimeOffset? LogDate { get; set; } 10 | public string DisplayDate => $"{LogDate:g}"; 11 | public string? Url { get; set; } 12 | public string Id { get; set; } 13 | public string TriggerEvent { get; set; } 14 | public string TriggerLogin { get; set; } 15 | public string RunDuration { get; set; } 16 | 17 | public bool HasActions 18 | { 19 | get 20 | { 21 | return TriggerEvent is not null || Url is not null; 22 | } 23 | } 24 | 25 | public bool Cancellable 26 | { 27 | get 28 | { 29 | return TriggerEvent is not null && !Helpers.ConclusionFilter.IsFinished(Conclusion); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[BUG]: " 4 | labels: ["bug"] 5 | assignees: 6 | - timheuer 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this bug report! 12 | - type: textarea 13 | id: what-happened 14 | attributes: 15 | label: What happened? 16 | description: Also tell us, what did you expect to happen? 17 | placeholder: Tell us what you see! 18 | value: "A bug happened!" 19 | validations: 20 | required: true 21 | - type: input 22 | id: vsversion 23 | attributes: 24 | label: Visual Studio Version 25 | description: Copy the Visual Studio version from your Help...About Visual Studio menu 26 | placeholder: Version 17.8.5 27 | - type: textarea 28 | id: logs 29 | attributes: 30 | label: Relevant log output 31 | description: Please copy and paste any relevant log output. From the Output window for "GitHub Actions for VS" 32 | -------------------------------------------------------------------------------- /src/Commands/ReportFeedbackCommand.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS.ToolWindows; 2 | using Microsoft.VisualStudio.Shell; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace GitHubActionsVS; 10 | 11 | [Command(PackageIds.ReportFeedbackCommand)] 12 | internal sealed class ReportFeedbackCommand : BaseCommand 13 | { 14 | protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) 15 | { 16 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 17 | ThreadHelper.JoinableTaskFactory.RunAsync(async () => 18 | { 19 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 20 | ToolWindowMessenger messenger = await Package.GetServiceAsync(); 21 | 22 | var vsVersion = await VS.Shell.GetVsVersionAsync(); 23 | 24 | messenger.Send(new(MessageCommand.ReportFeedback, vsVersion.ToString())); 25 | }).FireAndForget(); 26 | } 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tim Heuer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tim Heuer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Converters/ConclusionColorConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | using System.Windows.Media; 4 | 5 | namespace GitHubActionsVS.Converters; 6 | 7 | public class ConclusionColorConverter : IMultiValueConverter 8 | { 9 | 10 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | string status = values[0] as string; 13 | Brush defaultBrush = values[1] as Brush; 14 | 15 | return GetConclusionColor(status, defaultBrush); 16 | } 17 | 18 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | private Brush GetConclusionColor(string status, Brush inheritedBrush) => status.ToLowerInvariant() switch 24 | { 25 | 26 | "success" => new SolidColorBrush(Colors.Green), 27 | "failure" => new SolidColorBrush(Colors.Red), 28 | "startup_failure" => new SolidColorBrush(Colors.Red), 29 | "waiting" => new SolidColorBrush(Color.FromRgb(154, 103, 0)), 30 | _ => inheritedBrush, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/Converters/ConclusionIconConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | 4 | namespace GitHubActionsVS.Converters; 5 | public class ConclusionIconConverter : IValueConverter 6 | { 7 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 8 | { 9 | string status = value as string; 10 | return GetConclusionIndicator(status); 11 | } 12 | 13 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | return value; 16 | } 17 | 18 | private string GetConclusionIndicator(string status) => status.ToLowerInvariant() switch 19 | { 20 | "success" => "\uEBB3 ", 21 | "completed" => "\uEBB3 ", 22 | "failure" => "\uEC13 ", 23 | "startup_failure" => "\uEC13 ", 24 | "cancelled" => "\uEC19 ", 25 | "skipped" => "\uEABD ", 26 | "pending" => "\uEC15 ", 27 | "queued" => "\uEBA7 ", 28 | "requested" => "\uEBA7 ", 29 | "waiting" => "\uEA82 ", 30 | "inprogress" => "\uEA82 ", 31 | "in_progress" => "\uEA82 ", 32 | "warning" => "\uEC1F ", 33 | null => "\uEA82 ", 34 | _ => "\uEA74 ", 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitType": "docs", 8 | "commitConvention": "angular", 9 | "contributors": [ 10 | { 11 | "login": "IEvangelist", 12 | "name": "David Pine", 13 | "avatar_url": "https://avatars.githubusercontent.com/u/7679720?v=4", 14 | "profile": "https://davidpine.net", 15 | "contributions": [ 16 | "code", 17 | "doc" 18 | ] 19 | }, 20 | { 21 | "login": "timheuer", 22 | "name": "Tim Heuer", 23 | "avatar_url": "https://avatars.githubusercontent.com/u/4821?v=4", 24 | "profile": "https://timheuer.com/blog/", 25 | "contributions": [ 26 | "code", 27 | "doc" 28 | ] 29 | }, 30 | { 31 | "login": "zlatanov", 32 | "name": "Ivan Zlatanov", 33 | "avatar_url": "https://avatars.githubusercontent.com/u/2470527?v=4", 34 | "profile": "https://github.com/zlatanov", 35 | "contributions": [ 36 | "code" 37 | ] 38 | } 39 | ], 40 | "contributorsPerLine": 7, 41 | "skipCi": true, 42 | "repoType": "github", 43 | "repoHost": "https://github.com", 44 | "projectName": "GitHubActionsVS", 45 | "projectOwner": "timheuer" 46 | } 47 | -------------------------------------------------------------------------------- /src/VSCommandTable.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by VSIX Synchronizer 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace GitHubActionsVS 7 | { 8 | using System; 9 | 10 | /// 11 | /// Helper class that exposes all GUIDs used across VS Package. 12 | /// 13 | internal sealed partial class PackageGuids 14 | { 15 | public const string GitHubActionsVSString = "39792190-7ea1-477e-8f95-28a1132e1fc9"; 16 | public static Guid GitHubActionsVS = new Guid(GitHubActionsVSString); 17 | } 18 | /// 19 | /// Helper class that encapsulates all CommandIDs uses across VS Package. 20 | /// 21 | internal sealed partial class PackageIds 22 | { 23 | public const int ContextMenuGroup = 0x0001; 24 | public const int ActionsCommand = 0x0100; 25 | public const int TWindowToolbar = 0x1000; 26 | public const int TWindowToolbarGroup = 0x1050; 27 | public const int RefreshRepoCommand = 0x0111; 28 | public const int GotoRepoCommand = 0x0112; 29 | public const int OpenSettingsCommand = 0x0113; 30 | public const int ReportFeedbackCommand = 0x0114; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Options/ExtensionOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace GitHubActionsVS; 5 | internal partial class OptionsProvider 6 | { 7 | // Register the options with this attribute on your package class: 8 | // [ProvideOptionPage(typeof(OptionsProvider.ExtensionOptionsOptions), "GitHubActionsVS", "ExtensionOptions", 0, 0, true, SupportsProfiles = true)] 9 | [ComVisible(true)] 10 | public class ExtensionOptionsOptions : BaseOptionPage { } 11 | } 12 | 13 | public class ExtensionOptions : BaseOptionModel, IRatingConfig 14 | { 15 | [Category("Query Settings")] 16 | [DisplayName("Max Runs")] 17 | [Description("The maximum number of runs to retrieve")] 18 | [DefaultValue(10)] 19 | public int MaxRuns { get; set; } = 10; 20 | 21 | [Category("Query Settings")] 22 | [DisplayName("Refresh Active Jobs")] 23 | [Description("Whether to poll/refresh when pending/active jobs are going")] 24 | [DefaultValue(false)] 25 | public bool RefreshActiveJobs { get; set; } = false; 26 | 27 | [Category("Query Settings")] 28 | [DisplayName("Refresh Interval (in seconds)")] 29 | [Description("The interval (in seconds) to poll/refresh when pending/active jobs are going")] 30 | [DefaultValue(5)] 31 | public int RefreshInterval { get; set; } = 5; 32 | 33 | [Browsable(false)] 34 | public int RatingRequests { get; set; } 35 | } 36 | -------------------------------------------------------------------------------- /src/ToolWindows/ActionsToolWindow.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS.ToolWindows; 2 | using Microsoft.VisualStudio.Imaging; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace GitHubActionsVS; 9 | public class ActionsToolWindow : BaseToolWindow 10 | { 11 | public override string GetTitle(int toolWindowId) => "GitHub Actions"; 12 | 13 | public override Type PaneType => typeof(Pane); 14 | 15 | public GHActionsToolWindow ActionsWindow { get; private set; } 16 | private static ActionsToolWindow _instance; 17 | public static ActionsToolWindow Instance => _instance ??= new ActionsToolWindow(); 18 | 19 | public override async Task CreateAsync(int toolWindowId, CancellationToken cancellationToken) 20 | { 21 | ToolWindowMessenger toolWindowMessenger = await Package.GetServiceAsync(); 22 | 23 | ActionsWindow = new GHActionsToolWindow(toolWindowMessenger); 24 | _instance = this; 25 | return ActionsWindow; 26 | } 27 | 28 | [Guid("4a4ad204-3623-4e03-b2d1-6fef94652174")] 29 | internal class Pane : ToolkitToolWindowPane 30 | { 31 | public Pane() 32 | { 33 | BitmapImageMoniker = KnownMonikers.ToolWindow; 34 | ToolBar = new System.ComponentModel.Design.CommandID(PackageGuids.GitHubActionsVS, PackageIds.TWindowToolbar); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Converters/NullToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using GitHubActionsVS.Models; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace GitHubActionsVS.Converters; 7 | public class NullToVisibilityConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | return value == null ? Visibility.Hidden: Visibility.Visible; 12 | } 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | 20 | public class NullToBooleanConverter : IValueConverter 21 | { 22 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | return value == null ? false : true; 25 | } 26 | 27 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | } 32 | 33 | public class BoolToVisibilityConverter : IValueConverter 34 | { 35 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 36 | { 37 | return (bool)value ? Visibility.Visible : Visibility.Collapsed; 38 | } 39 | 40 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 41 | { 42 | throw new NotImplementedException(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Helpers/RepoInfo.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | 4 | namespace GitHubActionsVS.Helpers; 5 | internal class RepoInfo 6 | { 7 | public bool IsGitHub { get; set; } 8 | public string RepoName { get; set; } 9 | public string RepoOwner { get; set; } 10 | public string RepoUrl { get; set; } 11 | public string CurrentBranch { get; set; } 12 | 13 | 14 | internal void FindGitFolder(string path, out string foundPath) 15 | { 16 | foundPath = null; 17 | // Check if the current directory contains a .git folder 18 | if (Directory.Exists(Path.Combine(path, ".git"))) 19 | { 20 | foundPath = path; 21 | var repo = new LibGit2Sharp.Repository(foundPath); 22 | var remote = repo.Network.Remotes.FirstOrDefault(); 23 | if (remote is not null) 24 | { 25 | var url = remote.Url; 26 | if (url.Contains("github.com")) 27 | { 28 | IsGitHub = true; 29 | var parts = url.Split('/'); 30 | RepoOwner = parts[parts.Length - 2]; 31 | RepoName = parts[parts.Length - 1].Replace(".git", ""); 32 | RepoUrl = url.Replace(".git", ""); 33 | CurrentBranch = repo.Head.FriendlyName; 34 | } 35 | } 36 | return; 37 | } 38 | else 39 | { 40 | string parentPath = Directory.GetParent(path)?.FullName; 41 | if (!string.IsNullOrEmpty(parentPath)) 42 | { 43 | FindGitFolder(parentPath, out foundPath); // Recursively search the parent directory 44 | } 45 | } 46 | return; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: "Publish" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | inputs: 9 | Command: 10 | description: 'Publish command' 11 | jobs: 12 | build: 13 | name: Build and Test 14 | uses: ./.github/workflows/_build.yaml 15 | 16 | publish: 17 | needs: build 18 | environment: 19 | name: production 20 | url: https://marketplace.visualstudio.com/items?itemName=TimHeuer.GitHubActionsVS 21 | name: Publish 22 | runs-on: windows-2022 23 | permissions: 24 | contents: write 25 | 26 | env: 27 | VERSION: ${{ needs.build.outputs.version }} 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Download Package artifact 35 | uses: actions/download-artifact@v4 36 | with: 37 | name: ${{ github.event.repository.name }}.vsix 38 | 39 | - name: Tag and Release 40 | id: tag_release 41 | uses: softprops/action-gh-release@v1 42 | with: 43 | body: Release ${{ env.VERSION }} 44 | tag_name: ${{ env.VERSION }} 45 | generate_release_notes: true 46 | files: | 47 | **/*.vsix 48 | 49 | - name: Upload to VsixGallery 50 | uses: timheuer/openvsixpublish@v1 51 | with: 52 | vsix-file: ${{ github.event.repository.name }}.vsix 53 | 54 | - name: Publish extension to Marketplace 55 | id: publish_vsix 56 | if: ${{ (github.event_name == 'push' && contains(github.event.head_commit.message, '[release]')) || (github.event_name == 'workflow_dispatch' && contains(github.event.inputs.Command, '[release]')) }} 57 | continue-on-error: true # remove after VS bug fix 58 | uses: cezarypiatek/VsixPublisherAction@1.1 59 | with: 60 | extension-file: '${{ github.event.repository.name }}.vsix' 61 | publish-manifest-file: 'vs-publish.json' 62 | personal-access-code: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }} 63 | 64 | -------------------------------------------------------------------------------- /.github/workflows/_build.yaml: -------------------------------------------------------------------------------- 1 | name: "Base build" 2 | 3 | on: 4 | workflow_call: 5 | outputs: 6 | version: 7 | description: 'Version of the build' 8 | value: ${{ jobs.build.outputs.version }} 9 | workflow_dispatch: 10 | inputs: 11 | Reason: 12 | description: 'Reason for the build' 13 | 14 | jobs: 15 | build: 16 | outputs: 17 | version: ${{ steps.vsix_version.outputs.SimpleVersion }} 18 | name: Build 19 | runs-on: windows-2022 20 | env: 21 | PROJECT_PATH: "src/GitHubActionsVS.slnx" 22 | VsixManifestPath: src\source.extension.vsixmanifest 23 | VsixManifestSourcePath: src\source.extension.cs 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Version stamping 31 | id: vsix_version 32 | uses: dotnet/nbgv@v0.4 33 | with: 34 | setAllVars: true 35 | 36 | - name: 🧰 Setup .NET build dependencies 37 | uses: timheuer/bootstrap-dotnet@v1 38 | with: 39 | nuget: 'false' 40 | sdk: 'false' 41 | msbuild: 'true' 42 | 43 | - name: Increment VSIX version 44 | id: vsix_version_stamp 45 | uses: timheuer/vsix-version-stamp@v2 46 | with: 47 | manifest-file: ${{ env.VsixManifestPath }} 48 | vsix-token-source-file: ${{ env.VsixManifestSourcePath }} 49 | version-number: ${{ steps.vsix_version.outputs.SimpleVersion }} 50 | 51 | - name: 🏗️ Build 52 | run: msbuild ${{ env.PROJECT_PATH }} /p:Configuration=Release /v:m -restore /p:OutDir=\_built -bl 53 | 54 | - name: ⬆️ Upload artifact 55 | uses: actions/upload-artifact@v4 56 | with: 57 | name: msbuild.binlog 58 | path: msbuild.binlog 59 | 60 | - name: ⬆️ Upload artifact 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: ${{ github.event.repository.name }}.vsix 64 | path: /_built/**/*.vsix 65 | 66 | - name: Echo version 67 | run: | 68 | Write-Output ${{ steps.vsix_version.outputs.SimpleVersion }} -------------------------------------------------------------------------------- /src/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GitHub Actions 6 | A window that provides a view of GitHub Actions for the current repo of the opened solution in Visual Studio. Provided by @timheuer 7 | https://github.com/timheuer/GitHubActionsVS 8 | LICENSE 9 | https://github.com/timheuer/GitHubActionsVS 10 | https://github.com/timheuer/GitHubActionsVS 11 | Resources\Icon.png 12 | Resources\Icon.png 13 | github,actions,workflow,devops,dotnet 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | amd64 22 | 23 | 24 | arm64 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/UserControls/AddEditSecret.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows; 3 | using resx = GitHubActionsVS.Resources.UIStrings; 4 | 5 | namespace GitHubActionsVS.UserControls; 6 | /// 7 | /// Interaction logic for AddEditSecret.xaml 8 | /// 9 | public partial class AddEditSecret : Window 10 | { 11 | public AddEditSecret(string secretName) 12 | { 13 | InitializeComponent(); 14 | txtName.Text = secretName; 15 | txtName.IsEnabled = string.IsNullOrWhiteSpace(secretName); 16 | Title = string.IsNullOrWhiteSpace(secretName) ? resx.ADD_SECRET : resx.EDIT_SECRET; 17 | btnCreate.Content = string.IsNullOrWhiteSpace(secretName) ? resx.BUTTON_SAVE : resx.BUTTON_UPDATE; 18 | } 19 | 20 | public string SecretName => txtName.Text.Trim(); 21 | public string SecretValue => txtSecret.Text.Trim(); 22 | 23 | private async void Save_Click(object sender, RoutedEventArgs e) 24 | { 25 | // Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. Must start with a letter ([a-z], [A-Z]) or underscores (_). 26 | Regex rx = new Regex("^[a-zA-Z_][a-zA-Z0-9_]*$"); 27 | if (rx.IsMatch(txtName.Text)) 28 | { 29 | if (string.IsNullOrWhiteSpace(txtSecret.Text.Trim())) 30 | { 31 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 32 | Community.VisualStudio.Toolkit.MessageBox mb = new(); 33 | mb.ShowError("Secret value cannot be empty.", "Invalid Secret Value"); 34 | } 35 | else 36 | { 37 | DialogResult = true; 38 | Close(); 39 | } 40 | } 41 | else 42 | { 43 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 44 | Community.VisualStudio.Toolkit.MessageBox mb = new(); 45 | mb.ShowError("Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. Must start with a letter ([a-z], [A-Z]) or underscores (_).", "Invalid Secret Name"); 46 | } 47 | } 48 | 49 | private void Cancel_Click(object sender, RoutedEventArgs e) 50 | { 51 | DialogResult = false; 52 | Close(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/UserControls/AddEditSecret.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |