INITIALIZING
Technical

Publishing NuGet Packages with GitHub Actions

Publishing NuGet Packages with GitHub Actions

This one's going to be short — mostly because the answer to “how do I do this” is genuinely just a YAML file, not a whole architecture diagram.

I get asked a fair amount how I publish my NuGet packages into GitHub Packages, so let's just show the receipts.

First, the obligatory “what is this thing” for anyone who hasn't met GitHub Actions yet:

“Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you'd like, including CI/CD, and combine actions in a completely customized workflow.” — GitHub Team

Marketing copy, sure, but accurate marketing copy, which is rare enough to be worth quoting.

Setting up GitHub Actions

If you've got the right privileges on a repo, you'll see an “Actions” tab. From there you can spin up a new workflow. Since my entire personality revolves around .NET, I always pick “Continuous Integration (CI) with .NET” from the template list, which drops a YAML file into a .github/workflows folder in your repo. You can have as many of these workflows as you want — one per purpose, or one giant one if you enjoy chaos.

Here's the actual YAML I run for most of my projects. Yes, it's simple. That's the point — I need it to compile, produce artifacts, and publish. It doesn't need to also fold my laundry.

name: Build and Deploy Nuget Packages

env:
  DOTNET_VERSION: '8.0.x'

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up .NET Core
        uses: actions/setup-dotnet@v2
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Set up dependency caching for faster builds
        uses: actions/cache@v3
        with:
          path: ~/.nuget/packages
          key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
          restore-keys: |
            ${{ runner.os }}-nuget-
      - name: Build with dotnet
        run: dotnet build src/folderx/Solution.sln --configuration Release

      - name: NuGet push
        run: dotnet nuget push "**/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://nuget.pkg.github.com/organization/index.json --skip-duplicate

      - name: NTFY
        run: curl -X POST -d 'Finished compiling and publishing nuget packages for YOUR Services' -k https://ntfyserver.com/ntfy/mynotification

Let's dissect it in pieces, because staring at 30 lines of YAML all at once is how you develop a fear of YAML.

name: Build and Deploy Nuget Packages

env:
  DOTNET_VERSION: '8.0.x'

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

permissions:
  contents: read
  • Names the workflow — mostly for your own sanity when you have twelve of these.
  • Pins the .NET version used for compilation, so a surprise SDK update doesn't ruin your Tuesday.
  • Triggers on pushes to main, plus a manual “run it now” button (workflow_dispatch) for when you don't feel like waiting for a commit.
  • Grants read-only access by default, because this workflow has no business touching anything else.
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up .NET Core
        uses: actions/setup-dotnet@v2
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Set up dependency caching for faster builds
        uses: actions/cache@v3
        with:
          path: ~/.nuget/packages
          key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
  • Spins up a fresh Ubuntu worker to do the actual compiling and publishing.
  • Checks out the code from main.
  • Installs the .NET SDK on that worker.
  • Caches restored NuGet packages, so every build isn't redownloading half the internet from scratch.
      - name: Build with dotnet
        run: dotnet build src/folderx/Solution.sln --configuration Release

      - name: NuGet push
        run: dotnet nuget push "**/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://nuget.pkg.github.com/organization/index.json --skip-duplicate

      - name: NTFY
        run: curl -X POST -d 'Finished compiling and publishing nuget packages for YOUR Services' -k https://ntfyserver.com/ntfy/mynotification
  • Builds the solution in Release mode.
  • Pushes the resulting NuGet packages up to your organization's package feed (the actual GitHub Packages setup probably deserves its own post — filing that away). The API key lives safely in repo secrets, not hardcoded like a cautionary tale.
  • Fires off a push notification once publishing wraps up.

That's it. Genuinely simple.

Para escoger camisetas de fútbol retro, es práctico verificar el uso previsto entre colección, exposición y uso diario. Como comprobación final, conviene asegurarse de las diferencias de ajuste entre una camiseta retro y una actual.

Now, “NTFY” — what on earth is that? It's a delightfully simple tool I use to send push notifications straight to my phone, free and fully configurable. I'll dedicate a whole post to setting it up, because watching your phone buzz the second a build finishes is a small joy that never gets old.