Create your own GitHub Action with .NET

When using GitHub Actions as your CI / CD solution, you might come to the point where your own custom Action would become handy. Here is how to create one with your .NET skills.

Create your own GitHub Action with .NET

When using GitHub Actions as your CI / CD solution, you might come to the point where your own custom Action would become handy. Here is how to create one with your .NET skills.

In this tutorial, we build a simple Hello World Action with a few input and output parameters.

Create the code

As GitHub Actions don't have any form of UI, we will create a .NET Console App for this project.

dotnet new console -n MyGitHubAction

A nice tool that helps you to easily create and document command line parameters for .NET Console Apps is CommandLineParser. Let's install it via NuGet!

dotnet add package CommandLineParser

The command line parameters form the Input values of our GitHub Action. Create a new Options class and add some parameters like the following ones.

public class Options
{
	[Option('f', "firstname", Required = false, HelpText = "Your First Name")]
	public string FirstName { get; set; }

	[Option('l', "lastname", Required = false, HelpText = "Your Last Name")]
	public string LastName { get; set; }
}
Options.cs

Once the options are set, we can focus on the logic. In this example we just check, if the name options were set. If so, we greet the person, if not we just greet the whole world.

class Program
{
	static  async Task Main(string[] args)
	{
		var result = Parser.Default.ParseArguments<Options>(args);
		await result.MapResult(
			async options => await RunWithOptionsAsync(options),
			errors => HandleParseError(errors)
		);
	}

	static async Task RunWithOptionsAsync(Options options)
	{
        if (string.IsNullOrEmpty(options.FirstName) && string.IsNullOrEmpty(options.LastName)
        {
            Console.WriteLine("Hello World.");
        }
        else
        {
            Console.WriteLine($"Hello {options.FirstName} {options.LastName}.");
        }
    
				          
		Console.WriteLine($"::set-output name=date::{DateTime.Now}");
	}

	static Task HandleParseError(IEnumerable<Error> errors)
	{
		Environment.Exit(1);
		return Task.CompletedTask;
	}
}
Program.cs

Note the following line, which follows is the syntax to create output variables in GitHubActions.

Console.WriteLine($"::set-output name=date::{DateTime.Now}");

We can now test our code right on our machines.

dotnet run -- -f "Robin-Manuel" -l "Thiel"

If you are satisfied with the results, we can proceed to the next step.

Create a Dockerfile

There are multiple ways to create a GitHub Action. We will use a Docker container to pack our Action. For this, let's create the following Dockerfile.

#######################################################
# Step 1: Build the application in a container        #
#######################################################

FROM mcr.microsoft.com/dotnet/sdk:5.0 as build

# Copy .csproj files for NuGet restore
COPY ["src/MyGitHubAction/MyGitHubAction.csproj", "src/WMyGitHubAction/"]

RUN dotnet restore src/MyGitHubAction/MyGitHubAction.csproj

# Copy the rest of the files over
COPY ["src/MyGitHubAction/", "src/MyGitHubAction/"]

# Build the application
WORKDIR /src/MyGitHubAction/
RUN dotnet publish --output /out/ --configuration Release --no-self-contained

#######################################################
# Step 2: Run the build outcome in a container        #
#######################################################

FROM mcr.microsoft.com/dotnet/runtime:5.0

COPY --from=build /out .

# Start the application
ENTRYPOINT ["dotnet", "/MyGitHubAction.dll"] # Use / as GitHub sets the working directory when launching the container
Dockerfile

Let's test the container locally!

docker build -t MyGitHubAction:1.0 .

docker run MyGitHubAction:1.0 -f "Robin-Manuel" -l "Thiel"

GitHub Action Metadata

The last thing we need is a manifest, which describes the Action to other users. For this, we create the action.yaml file in the root folder. In this file, we describe the action metadata.

name: 'My GitHub Action'
description: 'Greet someone or the whole world'
branding:
  icon: 'arrow-up-circle'
  color: 'blue'
inputs:
  firstname:
    description: 'Your Fist Name'
    required: false
  lastname:
    description: 'Your Last Name'
    required: false
outputs:
  date:
    description: 'The date when someone was greeteed.'  
runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
    - --firstname
    - ${{ inputs.firstname }}
    - --lastname
    - ${{ inputs.lastname }}   
action.yaml

Release the GitHub Action

Good job, we are done. To Release the Action, just push everything to a GitHub Repository and create a new Release in there. The UI will guide you through the process of publishing the Action to the Marketplace.