1. Require changes into the trunk be approved
  2. Automatically push approved changes to the next environment
    1. Define variables
    2. Define secrets
    3. Create the Actions
    4. Adding Github tagging of the version
    5. Adding Versioning of the Solution
  3. Other posts in this series

If we consider that the trunk main or master represents what should be released, changes being pushed to it (or pulled into it) should go through a review process. After this review process is approved, it is possible to automatically push the change onto the next Environment.

It is also possible (and perhaps desirable) to have an intermediate branch representing a release. This scenario is not discussed in this post.

Require changes into the trunk be approved

in the GitHub repository, open Settings, then Rulesets on the left-navigation. Then click the New Ruleset button drop-down and select New branch ruleset.

Give the ruleset a name in the Ruleset name field.

In teh Targets section, for the target branch, click the Add target drop-down button and select Include by pattern. When prompted, enter the trunk branch (eg. main or master) and click Add inclusion pattern.

Within Branch rules select Require a pull request before merging and configure the requirements for approval.

Click Save changes to apply the changes.

Automatically push approved changes to the next environment

It is possible to add GitHub Actions to an approved Pull Request which will deploy to the next environment, eg. QA.

The settings defined here refer to the target environment.

Define variables

A number of variables will be required to be created in GitHub for the Action to work:

VariableDescription
POWERPLATFORM_CLIENT_IDID of the Client.
POWERPLATFORM_TENANT_IDThe tenant of the CRM.
POWERPLATFORM_ENV_URLURL of the environment.

Define secrets

A single secret must be defined. Secrets can only be set once and cannot be viewed once set.

SecretDescription
POWERPLATFORM_CLIENT_SECRETClient secret for the Environment.

GitHub variables and secrets are defined in Settings, then Secrets and variables then Actions.

Create the Actions

The Actions can be executed automatically based on rules defined in the YAML file.

Save the script below in a file, eg. promote-to-environment.yml in the folder `.github/workflows`

This script:

  • Is activated when an attempt is made to push to a trunk branch, in this case main (as a result of an approved Pull Request).
  • Runs on an Ubuntu instance.
  • Checks out the code.
  • Installs .NET v8.
  • Installs the Microsoft PAC tool and adds to the system path..
  • Authenticates with Power Platform using the variables defined above.
  • Packs the Solution into a zip file as an unmanaged Solution. (You should consider changing this to a managed Solutuion if promoting to a QA or Production environment.)
  • Imports the Solution into the Environment.
name: Pack, Version, Tag, and Deploy Solution

on:
  push:
    branches:
      - main

jobs:
  pack-version-deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.x'

    - name: Install PAC CLI
      run: dotnet tool install --global Microsoft.PowerPlatform.Cli

    - name: Add PAC CLI to PATH
      run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH

    - name: Authenticate with Power Platform
      run: |
          pac auth create --clientId ${{ vars.POWERPLATFORM_CLIENT_ID }} \
                          --clientSecret ${{ secrets.POWERPLATFORM_CLIENT_SECRET }} \
                          --tenant ${{ vars.POWERPLATFORM_TENANT_ID }} \
                          --url ${{ vars.POWERPLATFORM_ENV_URL }}

    - name: Pack Solution
      run: |
        mkdir -p packed
        pac solution pack --folder solutions/MySolution --zipfile packed/MySolution.zip --packagetype Unmanaged

    - name: Import to Target Environment
      run: |
        pac solution import --path packed/MySolution.zip --publish-changes

Adding Github tagging of the version

Before the pac solution import is called, add the following:

    - name: Generate Version Number
      id: version
      run: echo "VERSION=v1.0.$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV

    - name: Tag Commit with Version
      run: |
        git config user.name "github-actions"
        git config user.email "github-actions@github.com"
        git tag ${{ env.VERSION }}
        git push origin ${{ env.VERSION }}

This:

  • Generates a new version number, eg. v1.0.20250523 and stores it in the $GITHUB_ENV environment variable.
  • Tags the commit in GitHub with the generated version.

Adding Versioning of the Solution

The version number of the Solution is stored in the Other.Solution.xml file in the pach (XPath) Solution@version.

This should be updated to the generated version number before the Solution is packed. Therefore, insert the following before the pack solution pack is called:

    - name: Generate Version Number
      id: version
      run: echo "VERSION=v1.0.$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV

(This is the same as the fragment above, for Github tagging and if you want to do both, shouldn’t be duplicated.)

Then add the following script fragment before the pack solution pack is called:

    - name: Update Solution Version
      run: |
        VERSION=${{ env.VERSION }}
        sed -i "s/version=\"[^\"]*\"/version=\"${VERSION#v}\"/" solutions/MySolution/Other/Solution.xml

This:

  • Updates the Solution for the MySolution Solution, which is currently unpacked.
  • uses the sed utility to “search and replace” where it sees the text that matches the regular expression version=\"[^\"]*\" and replaces with the version version=\"${VERSION#v}\"/" (removing the preceding v character, which would make the version number invalid).

Obviously this will only work for a single defined Solution. Use of a single Solution is often easier to manage between Environments, particularly when marshalling changes like deletions.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Quote of the era

In the beginning there was Jack … and Jack had a groove. And from this groove came the groove of all grooves. And while one day viciously throwing down on his box, Jack boldly declared “Let There Be House” and House music was born.

~ Chuck Roberts