Published in Articles on by Michiel van Oosterhout ~ 4 min read

This article demonstrates an implementation of the simple Git-based workflow described in the previous article. The workflow, implemented using GitHub Actions, will publish a PowerShell script package to either a prerelease or a release channel.

GitHub Actions
GitHub Actions Source: github.blog

Git repository layout

The code for the script package will be hosted in a dedicated GitHub repository. The name of this dedicated GitHub repository should be the same as the name of the script package. The script file and its unit tests file should be added to the root of the repository. GitHub workflows are YAML files and must be saved in the .github/workflows directory of a GitHub repository.

Assuming a GitHub repository named Example, the repository layout should be as follows:

  • .github/
    • workflows/
      • publish-powershell-script.yaml
  • Example.ps1
  • Example.Tests.ps1
  • Publish-PowerShellScript.ps1

(The Publish-PowerShellScript.ps1 file is described in detail in the next article.)

GitHub workflow

Triggers

Triggers are events that cause a workflow to run. We can configure a workflow to run when a specific activity happens, at a scheduled time, or when an event outside of GitHub occurs.

on:
  push:
    branches:
      - main
    tags:
      - v[0-9]+.[0-9]+.[0-9]+
  pull_request: {}

Our workflow should be triggered when commits are pushed to the main branch, when a pull request is (re)opened or its head branch is updated, and when a version tag (e.g. v1.2.3) is created.

GitHub Actions executing a workflow with two jobs, triggered by a `push` event
GitHub Actions executing a workflow with two jobs, triggered by a push event

Defaults

The defaults key can be used to set default settings that will apply to all steps in the workflow. We use it to set the shell to PowerShell:

defaults:
  run:
    shell: pwsh

Release channels

The workflow consists of two jobs. The first job selects the GitHub environment that the script package will be published to in the second job. Environment in this context should be understood to mean release channel as explained in the previous article.

jobs:
  prepare:
    name: Prepare
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.select.outputs.environment }}
    steps:
      - name: Select environment
        id: select
        run: 'Write-Host ("::set-output name=environment::" + ("${{ github.ref_type }}" -eq "tag" ? "PowerShell Gallery" : "MyGet"))'

We use a workflow command to set an output parameter named environment depending on the event that triggered the workflow. The environment will be PowerShell Gallery when the job is triggered by the creation of a tag, otherwise it will be MyGet. These environments should be added to the GitHub repository. During development new versions of the script package, including prerelease versions, become available on MyGet, and only after a particular version is tagged does it become generally available available on the PowerShell Gallery.

GitHub environments as shown on the landing page of a repository
GitHub environments as shown on the landing page of a repository
GitHub deployments to an environment named MyGet
GitHub deployments to an environment named MyGet

Checkout

The second job starts only after the first job completes (due to the needs property). The environment property is set using the first job's environment output. The first step checks out the code.

  publish:
    name: Publish
    runs-on: ubuntu-latest
    needs: prepare
    environment: ${{ needs.prepare.outputs.environment }}

    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 2

The fetch-depth must be set to at least 2, in order for the script to be able to compare a commit on the main branch to its parent.

Publish

The next step does the actual publishing by dot sourcing a script and executing the publish function defined by it.

      - name: Publish
        run: |
          . ./Publish-PowerShellScript.ps1
          Publish-PowerShellScript `
              -Ref ("${{ github.event_name }}" -eq "pull_request" ? "${{ github.head_ref }}" : "${{ github.ref_name }}") `
              -Build ${{ github.run_number }} `
              -NuGetApiKey "${{ secrets.NUGET_API_KEY }}" `
              -NuGetUrl "${{ secrets.NUGET_URL }}" `
              -InformationAction Continue

The value of the -Ref parameter is either the name of the pull request branch (github.head_ref), or the name of the main branch or the tag (github.ref_name). This will be used by the script to determine the correct versioning strategy.

The value of the -Build parameter is an incrementing number for this particular workflow (github.run_number), and will be used by the script to generate a unique prerelease label for each iteration of a pull request.

Notice that the values for both -NuGet parameters are provided by GitHub secrets. These secrets should be set in each GitHub environment. This way the script does not need to know about any specific release channel.

Summary

Triggers in GitHub Actions can be combined with GitHub environments to automate the publishing of PowerShell Get script packages to multiple release channels. The next article will describe the script in detail.

Links

Updates

  • Added mention of GitHub Packages and PowerShellGet 3.0.
  • Added an explanation of the fetch-depth: 2 requirement