In my previous article on Extending Visual Studio for Mac 2022, I went through a step-by-step walkthrough to build a simple Visual Studio for Mac extension that does adds a menu item under the Edit menu, which when clicked, will write // Hello at the current cursor position of the currently active document.

Visual Studio for Mac extensions can be created using SDK Style .NET projects use .NET 7.0 as the target framework. The first requirement for setting up a build pipeline is being able to build the project from the command line. Using the example from the the previous article, let’s build the Sample project using a Github Actions workflow

In case you are new to Github Actions, you should create the folder /.github/workflows in your git repo, then create a workflow file under the folder we just created.

For this example, let’s create a file called build.yml.

Here’s the contents of our workflow:


name: Build

on:
  workflow_dispatch:
  push:

env:
  VERSION: 1.0.${{ github.run_number }}

jobs:

  build:

    runs-on: macos-latest
    timeout-minutes: 10

    steps:
    - uses: actions/checkout@v3

    - name: Update Extension Version Info
      run: |
        sed -i -e 's/1.0/${{ env.VERSION }}/g' ./AddinInfo.cs
        cat ./AddinInfo.cs
      working-directory: src

    - name: Restore
      run: dotnet restore
      working-directory: src

    - name: Build
      run: /Applications/Visual\ Studio.app/Contents/MacOS/vstool build --configuration:Release $PWD/Sample.csproj
      working-directory: src

    - name: Pack
      run: /Applications/Visual\ Studio.app/Contents/MacOS/vstool setup pack $PWD/src/bin/Release/net7.0/Sample.dll -d:$PWD

    - name: Archive binaries
      run: zip -r Binaries.zip src/bin/Release/net7.0/

    - name: Publish binaries
      uses: actions/upload-artifact@v2
      with:
        name: Binaries
        path: Binaries.zip

    - name: Rename build output
      run: mv *.mpack Sample-${{ env.VERSION }}.mpack

    - name: Publish artifacts
      uses: actions/upload-artifact@v2
      with:
        name: Extension
        path: Sample-${{ env.VERSION }}.mpack

Let’s break the workflow job steps down into detail…

Step 1) Checkout the branch.

Pretty a much a standard first for most Github Action workflows

Step 2) Update the version info of the extension.

For this naive example let’s just call it version 1.0.xxx where xxx is the workflow run number. We get the Github Action workflow run number from ${{ github.run_number }}. Let’s store this in an environment variable called VERSION. We then want to update the version number in the AddinInfo.cs file

To do this, we use the the sed (stream editor) command to replace all instances of 1.0 with 1.0.xxx.

We do this by running:

$ sed -i -e 's/1.0/$/g' ./AddinInfo.cs

The contents of AddinInfo.cs may look something like this:

using Mono.Addins;
using Mono.Addins.Description;

[assembly: Addin(Id = "Sample", Namespace = "Sample", Version = "1.0")]
[assembly: AddinName("My First Extension")]
[assembly: AddinCategory("IDE extensions")]
[assembly: AddinDescription("My first Visual Studio for Mac extension")]
[assembly: AddinAuthor("Christian Resma Helle")]

Step 3) Restore package reference

This simple step is where the workflow should basically just run dotnet restore from the folder that contains the solution file

Step 4) Build the extension

We want to build the project in Release configuration using the CLI. Normally we can do this using dotnet build -c Release which works fine if you have previously built a Visual Studio for mac extension on the machine you’re working on but if you are building it in a new machine that has previously never build a Visual Studio for Mac extension then you most likely will need to run the Visual Studio Tool Runner a.k.a. vstool but because we are probably running from a short lived virtual machine, we can’t assume that running dotnet build -c Release will work on the first try.

Instead of running dotnet build we should instead do something like run the Visual Studio Tool Runner build command

$ /Applications/Visual\ Studio.app/Contents/MacOS/vstool build --configuration:Release $PWD/Sample.csproj

You need to specify the absolute path to the project file, but you can simplify this by getting the present working directory from the PWD command

Step 5) Package the extension

Now that the extension is built, we now need to package it to be able to distribute it. To create a MonoDevelop package file .mpack you need to run the Visual Studio Extension Setup Utility pack command

$ /Applications/Visual\ Studio.app/Contents/MacOS/vstool setup pack [absolute path to main output DLL] -d:[absolute path to output folder]

A little tip for getting the absolute path is to use $PWD. So if you created your project under the ~/projects/my-extension folder and this is currently your working directory then you can do something like:

$ /Applications/Visual\ Studio.app/Contents/MacOS/vstool setup pack $PWD/Sample.dll -d:$PWD

Sample.dll is the build output of the project we just built

The command above will produce the output ~/projects/my-extension/Sample.mpack

Step 5) Package the binaries

Archive the binary files so we can use them as build artifiacts

$ zip -r Binaries.zip src/bin/Release/net7.0/

Step 6) Publish binaries as build artifacts

In this step we will publish the newly created Binaries.zip as a build artifact

Step 7) Rename built output

We do this so to help users who might have download multiple verssions of the extension be able to keep older versions of the extension. This is completely optional, but is something I find to be a good practice

Uploading artifacts uses the actions/upload-artifact@v2 task

Step 8) Publish .mpack file as build artifacts

This is pretty straight forward. You start off by using the actions/upload-artifact@v2 task. This workflow allows the developer to the filename of the artifact, and also which folder to publish as build artifacts

Build Output

If everything succeeds then we should be able to see the results of the build in Github Actions. At the bottom of the content section for every build should have 2 artifacts, the binary files and the .mpack file itself.

There are 2 artifacts in the build called Binaries and Extensions. They are packed as zip files when downloaded

I hope you found this useful and get inspired to start building extensions of your own. If you’re interested in the full source code then you can grab it here