Publishing image to GitHub Container Registry using GitHub Actions
Tags: githubactions, docker, githubpackages, csharp
Published: 2024-01-31 11:50:04

Hi everyone, in today's post we are going to talk about how to create images and publish to GitHub Container Registry using GitHub Actions.

### Base Knowledge
For a deeper understanding of the subject, it's advisable to understand the concepts of Docker and GitHub Container Registry and to help you with this, I have left two useful links below.

[What is Container Registry?](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)

[Docker Overview](https://docs.docker.com/get-started/overview/)

### Application Implementation (.NET)
The first step is basically to create a new project in .NET, feel free to use your favorite IDE to do this. We'll use a .NET API project, so we can containerize the application and publish it as an image on GitHub Container Registry.

When creating the project, remember to include support for docker on Linux if your IDE offers this option

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ww6rv9o7mwl862bs0if.png)

> Note: I'm using JetBrains Rider

In the project we will not implement anything new, we will only use what is already in the standard template, as the main objective is just to generate an image of the application. For more information about the project I created, you can see it here [.NET Core API](https://github.com/reniciuspagotto/docker-image-aspnet-application)

If you forgot to enable docker support through the IDE or if the IDE does not have this type of support, let's manually add the dockerfile to the root of the project. Remember to review and make the necessary changes to the dockerfile, especially the application name if it's different.

```
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83cihjqtpx8722tm137p.png)

.Api/ImageApplication.Api.csproj", "ImageApplication.Api/"]
RUN dotnet restore "ImageApplication.Api/ImageApplication.Api.csproj"
COPY . .
WORKDIR "/src/ImageApplication.Api"
RUN dotnet build "ImageApplication.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "ImageApplication.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ImageApplication.Api.dll"]
```

After creating the application and configuring the dockerfile, ensure that it works as expected. You can download Docker Desktop or Rancher Desktop and run it locally to ensure the image will build correctly.

Now, push the code to a new GitHub repository as for the next steps it's essential to have the repository created.

### Generating GitHub Token
The next step is to generate the token that will represent the secret to authenticate to GitHub Container Registry and allow publication. In the right menu, go to **_Settings_**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1hlwa4wi6rznk0uyf16.png)

In the left menu, go to **_Developer settings_**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lh6tp0bo6f4t0j6zs2cs.png)

Let's use a classic token

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3iyuxcgv3xd7rerzykf.png)

Click the **_Generate new token_** dropdown and select **_Generate new token (classic)_**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6e60fao8v3acsy7z8dm2.png)

Set a **_name_** for your token, the _**expiration date**_ and select the **_write:packages_** scope

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uk7eapi25evcnqbe6i1k.png)

At the bottom, click on the green Generate Token button to actually create the token and obtain the secret.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zmph3c6b2r0ez43im1du.png)

Go back to the repository you created for the .NET application. Now let's use the GitHub Secret to store the token to be used by the pipeline. To do this, in the repository. select _**Settings**_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6pt37q7iva1ooxkzxh5e.png)

In the left menu, select **_Secret and variables_** and click **_Actions_**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c8p6pdmc5x2ljkkeyw3q.png)

Select **_New repository secret_**, define a name for the secret and in values paste the token we generated earlier. Finally, click **_Add secret_**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsuu9nxiik76z2x07znq.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/isgurqj50z6iqh1hny97.png)

Now we have the token that will be used for authentication and publication authorization as a GitHub Secret where it will be used by the pipeline.

### Pipeline / Workflow
After creating our project, configuring the dockerfile and authentication token, it's time to prepare our CI/CD pipeline to generate the image and publish it to the Container Registry.

The pipeline itself is simple and easy to understand, as you can see below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ku9863nj427i67k7xgrt.png)

Let's understand part by part

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0a3j0p5a5uq5hs8tz3wv.png)

**Name:** Represents the name of the workflow

**on:** It's the trigger of workflows

**push:** It's the type of trigger, for example, the pipeline will be executed on every push

**tags:** As we defined the trigger of our pipeline as a push event, we also need to inform the push type, in our example it will be a tag push with a pre-defined format.

The pipeline/workflow will always be triggered every time a tag push occurs that follows the pattern

In the next part, we define the pipeline environment variable.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6s8ca8r8wwyh3wh0dmt3.png)

The _**registry**_ variable represents the Container Registry URL, provided by GitHub.

The _**image name**_ variable represents the name of my repository, which will also be used to name the image.

Next part, we have the definition of jobs where we need to define at least one job, the OS to be used by the Runner and the necessary steps.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k41xkdqn5ggfax5evpwh.png)

**Checkout Repository** -> It's basically a git clone of the repository inside the VM (Runner) that will execute the steps.

**Set up Docker Buildx** -> Command for installing Docker Buildx that will be responsible for executing docker commands.

**Log in to the GitHub Container Registry** -> It's the authentication step in the Container Registry. Note that we pass 3 variables for step, registry, username and password. Registry is the URL defined in the environment variable at the beginning of the pipeline, username is your username on GitHub and is accessible by the standard variable _github.actor_ provided by GitHub itself and the last is the password which is our token that we created previously and which is stored as secret where we reference it using the name REGISTRY_TOKEN.

In the last part we have...

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o712u2h831o60s29b9ux.png)

**Extract metadata...** -> It's the command to make the git tag be used as the image version.

**Build and Push Image** -> Finally, this is the step that runs the dockerfile, creates the image, and pushes it to the Container Registry.

### Executing the pipeline
To run the pipeline, you need to create a git tag and submit it, the commands are shown below.

`git tag 1.3.0`
`git push origin 1.3.0`

I've set a version of 1.3.0, but feel free to use whatever version you like. After push, the pipeline will run automatically.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y67fgaj4zn9vxjw54gl1.png)

To view packages, you can go to your profile again and select Packages

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/luzvoaz79635rg1glrhg.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u4aa7exau61mj8gjxk3u.png)

You can now download and run the image from any location. Remembering that the image is published as private, to make it public just click on the image and make it visible to everyone.

That's all folks, stay tuned for the next posts.