There are lots of things that can be possible with the mighty PowerApps Component Framework. The component development experience is great thanks to the scripting provided by the framework, however once you’re happy with your component, “shipping it” may not be so easy. You still have to deal with the produced solution archive (.zip) file. Moreover, the properties of this solution file is in an XML file in your repository, for example, you’ll have to increment the version number for the solution that will be output by your build process manually. This is fine if it’s a one-off operation, but it can be a little daunting when you have to repeat it over and over during your dev/test loop, or if you want to implement continuous delivery.
An example scenario
I have recently developed a PCF control to learn the framework, and decided to implement continuous integration and delivery. To achieve this, I decided to use Azure Pipelines and release the control as a managed solution to GitHub.
Let’s start with a CI pipeline that builds our new control:
1
2
3
4
5
6
7
8
9
10
11
12
stages:
- stage: 'build'
jobs:
- job: 'build_job'
pool:
vmAgent: 'windows-latest'
steps:
- task: VSBuild@1
displayName: "Build"
inputs:
solution: 'Solutions\Solutions.cdsproj'
msbuildArgs: '/t:build /restore'
Well, that was easy. However, our solution is getting built in Debug
mode, and this produces an unmanaged
solution by default. You can manually override this setting by specifying a property in the Solutions.cdsproj
file. (Hint: The property is included as a comment in the generated project file).
In this scenario, we will just tell the pipeline to build in Release
mode:
1
2
# ...
msbuildArgs: '/t:build /restore /p:configuration=Release' # build in Release mode to get managed solution
Great, now we have our managed solution. Let’s give our build a name to provide unique version numbers to each build:
1
2
3
4
5
6
variables:
version: '1.0' # Set a constant version number (<major>.<minor>)
name: $(version).$(build.buildId) # Add the build ID as the patch version incremental versions in the format '1.0.x'
# stages: ...
Now each build has its own unique version number, but our solution version never changes. To set the solution and control version numbers automatically, we will use a couple of simple PowerShell steps:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# ...
steps:
# Set solution version
- powershell: |
echo "Updating solution version: $($env:BUILD_NUMBER)"
$solution = [xml](gc "$($env:SOLUTION_XML_PATH)")
$solution.ImportExportXml.SolutionManifest.Version = "$($env:BUILD_NUMBER)";
$solution.Save("$($env:SOLUTION_XML_PATH)")
displayName: 'Update solution version'
env:
BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.5523
SOLUTION_XML_PATH: 'Solutions\Other\Solution.xml'
# Set control version
- powershell: |
echo "Updating control version: $($env:BUILD_NUMBER)"
$xml = [xml](gc "$($env:CONTROL_MANIFEST_XML_PATH)")
$xml.manifest.control.version = "$($env:BUILD_NUMBER)";
$xml.Save("$($env:CONTROL_MANIFEST_XML_PATH)")
displayName: 'Update control version'
env:
BUILD_NUMBER: "$(build.buildNumber)"
CONTROL_MANIFEST_XML_PATH: 'path/to/ControlManifest.Input.xml' # This should specify the path to your control's manifest XML file
# - task: VSBuild@1 ...
Now, every build produces a managed solution with a unique version number. It’s time to tag the source in GitHub, and publish our managed solution to GitHub as a release.
To do this, we will first need to add a checkout step with persistCredentials
flag set to true
at the beginning of our build job, and a PowerShell script to create and push a tag to our repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
steps:
- checkout: self
persistCredentials: true
# ... build tasks
- powershell: |
echo "Tagging Build: $($env:BUILD_NUMBER)"
git tag $env:BUILD_NUMBER
git push origin $env:BUILD_NUMBER
env:
BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
GIT_REDIRECT_STDERR: 2>&1 # This is needed because git writes warnings to error stream and this fails the task
Now we can use the GitHub Release task to release our solution to GitHub. The githubConnection
parameter accepts a Service Connection name which can be configured on Azure Pipelines in Project Settings > Service Connections
.
1
2
3
4
5
6
7
8
- task: GitHubRelease@1
inputs:
gitHubConnection: 'azure-pipelines-github-connection'
repositoryName: '$(Build.Repository.Name)'
action: 'create'
target: '$(Build.SourceVersion)'
tagSource: 'gitTag'
assets: Solutions\bin\Release\*.zip
Our final pipeline definition looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
variables:
version: '1.0.0'
name: $(version).$(build.buildId)
stages:
- stage: 'build'
jobs:
- job: 'build_job'
pool:
vmAgent: 'windows-latest'
steps:
- checkout: self
persistCredentials: true
- powershell: |
echo "Updating solution version: $($env:BUILD_NUMBER)"
$solution = [xml](gc "$($env:SOLUTION_XML_PATH)")
$solution.ImportExportXml.SolutionManifest.Version = "$($env:BUILD_NUMBER)";
$solution.Save("$($env:SOLUTION_XML_PATH)")
displayName: 'Update solution version'
env:
BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
SOLUTION_XML_PATH: 'Solutions\Other\Solution.xml'
- powershell: |
echo "Updating control version: $($env:BUILD_NUMBER)"
$xml = [xml](gc "$($env:CONTROL_MANIFEST_XML_PATH)")
$xml.manifest.control.version = "$($env:BUILD_NUMBER)";
$xml.Save("$($env:CONTROL_MANIFEST_XML_PATH)")
displayName: 'Update control version'
env:
BUILD_NUMBER: "$(build.buildNumber)"
CONTROL_MANIFEST_XML_PATH: 'path/to/ControlManifest.Input.xml'
- task: VSBuild@1
displayName: "Build"
inputs:
solution: 'Solutions\Solutions.cdsproj'
msbuildArgs: '/t:build /restore'
- powershell: |
echo "Tagging Build: $($env:BUILD_NUMBER)"
git tag $env:BUILD_NUMBER
git push origin $env:BUILD_NUMBER
env:
BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
GIT_REDIRECT_STDERR: 2>&1 # This is needed because git writes warnings to error stream and this fails the task
- task: GitHubRelease@1
inputs:
gitHubConnection: 'azure-pipelines-github-connection'
repositoryName: '$(Build.Repository.Name)'
action: 'create'
target: '$(Build.SourceVersion)'
tagSource: 'gitTag'
assets: Solutions\bin\Release\*.zip
Hope this helps!