Skip to content

Commit 4a46f93

Browse files
authored
Updates to the DevOps processes.
1 parent c81ffcc commit 4a46f93

File tree

3 files changed

+105
-54
lines changed

3 files changed

+105
-54
lines changed

.azure-devops/pipelines/build.yml

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1-
# Enable the pipeline trigger when any specified branch has changed
2-
trigger:
3-
batch: true
4-
branches:
5-
include:
6-
- main
7-
- features/*
8-
9-
# Enable the pipeline trigger when new PR is created
10-
pr:
11-
autoCancel: true
12-
branches:
13-
include:
14-
- main
15-
16-
pool:
17-
vmImage: ubuntu-latest
1+
# Manual trigger only
2+
trigger: none
3+
4+
parameters:
5+
- name: buildConfiguration
6+
displayName: 'Build configuration'
7+
type: string
8+
default: Debug
9+
values:
10+
- Debug
11+
- Release
1812

1913
jobs:
2014
- job: build
2115
displayName: 'Build'
2216
timeoutInMinutes: 20
2317

18+
strategy:
19+
matrix:
20+
Linux:
21+
imageName: 'ubuntu-latest'
22+
matrixName: Linux
23+
Windows:
24+
imageName: 'windows-latest'
25+
matrixName: Windows
26+
#Mac:
27+
# imageName: 'macOS-latest'
28+
# matrixName: Mac
29+
30+
pool:
31+
vmImage: $(imageName)
32+
2433
steps:
2534

2635
# Build .NET solution
@@ -40,10 +49,10 @@ jobs:
4049
projects: '$(System.DefaultWorkingDirectory)/**/*.csproj'
4150

4251
- task: DotNetCoreCLI@2
43-
displayName: 'Build'
52+
displayName: 'Build (${{ parameters.buildConfiguration }})'
4453
inputs:
4554
command: build
46-
arguments: '--no-restore'
55+
arguments: '--no-restore --configuration ${{ parameters.buildConfiguration }}'
4756
projects: '$(System.DefaultWorkingDirectory)/**/*.csproj'
4857

4958
# Install and configure Logic Apps runtime environment
@@ -58,29 +67,53 @@ jobs:
5867
inputs:
5968
version: 'latest'
6069

70+
# - task: Npm@1
71+
# displayName: 'Install Functions core tools (npm install)'
72+
# inputs:
73+
# command: 'custom'
74+
# customCommand: 'install -g azure-functions-core-tools@4 --unsafe-perm true'
75+
6176
- task: CmdLine@2
6277
displayName: 'Check Functions Core tools installation'
6378
inputs:
6479
script: func
65-
80+
6681
- task: Npm@1
6782
displayName: 'Install Azurite'
6883
inputs:
6984
command: 'custom'
7085
customCommand: 'install -g azurite@3.24.0'
7186

7287
- task: CmdLine@2
73-
displayName: 'Start Azurite services'
88+
displayName: 'Start Azurite services (not Windows)'
89+
condition: and(succeeded(), ne(variables.matrixName, 'Windows'))
7490
inputs:
7591
script: 'azurite &'
7692

93+
- task: CmdLine@2
94+
displayName: 'Start Azurite services (Windows)'
95+
condition: and(succeeded(), eq(variables.matrixName, 'Windows'))
96+
inputs:
97+
script: 'start /b azurite'
98+
7799
# Run tests and publish test results to Azure DevOps
78100

79101
- task: DotNetCoreCLI@2
80-
continueOnError: true
81102
displayName: 'Run tests'
103+
continueOnError: true
82104
inputs:
83105
command: test
84-
arguments: '--no-restore --verbosity normal'
106+
arguments: '--no-restore --verbosity normal --configuration ${{ parameters.buildConfiguration }}'
85107
projects: '$(System.DefaultWorkingDirectory)/**/*.Tests.csproj'
86108
publishTestResults: true
109+
testRunTitle: 'Tests ($(matrixName))'
110+
111+
# Publish NuGet package
112+
113+
- task: PublishPipelineArtifact@1
114+
displayName: 'Publish NuGet package (Release build only)'
115+
condition: and(succeeded(), eq('${{ parameters.buildConfiguration }}', 'Release'))
116+
inputs:
117+
targetPath: '$(System.DefaultWorkingDirectory)/LogicAppUnit/bin/${{ parameters.buildConfiguration }}'
118+
artifact: 'NuGetPackage-$(matrixName)'
119+
publishLocation: 'pipeline'

.github/workflows/build.yml

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ on:
44
push:
55
branches:
66
- main
7-
- automated_build
87
- 'features/**'
98
pull_request:
109
branches:
@@ -14,7 +13,12 @@ on:
1413
jobs:
1514

1615
Build:
17-
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
os: [ubuntu-latest, windows-latest, macos-latest]
20+
21+
runs-on: ${{ matrix.os }}
1822

1923
steps:
2024
- name: Checkout
@@ -43,6 +47,11 @@ jobs:
4347
- name: Install Functions Core tools
4448
run: 'npm install -g azure-functions-core-tools@4 --unsafe-perm true'
4549

50+
- name: Set Functions Core tools path (Windows only)
51+
if: matrix.os == 'windows-latest'
52+
run: 'setx /m Path "C:\npm\prefix\node_modules\azure-functions-core-tools\bin;%Path%"'
53+
shell: cmd
54+
4655
- name: Check Functions Core tools installation
4756
run: 'func'
4857

@@ -51,25 +60,27 @@ jobs:
5160

5261
- name: Start Azurite services
5362
run: 'azurite &'
63+
shell: bash
5464

5565
# Run tests
5666

5767
- name: Run tests
58-
run: dotnet test --no-restore --verbosity normal --logger "TRX"
68+
run: dotnet test --no-restore --verbosity normal --logger "trx"
5969

6070
# Publish artefacts and test results
6171

6272
- name: Publish test log
6373
uses: actions/upload-artifact@v3
64-
if: always()
74+
if: success() || failure()
6575
with:
66-
name: test-results
67-
path: ${{ github.workspace }}/LogicAppUnit.Samples.LogicApps.Tests/TestResults/**.trx
76+
name: test-results.${{ matrix.os }}
77+
path: ${{ github.workspace }}/LogicAppUnit.Samples.LogicApps.Tests/TestResults/*.trx
6878

6979
- name: Publish test results
80+
if: (success() || failure()) && github.event_name != 'pull_request'
7081
uses: dorny/test-reporter@v1
71-
if: always()
7282
with:
73-
name: Test Results
74-
path: ${{ github.workspace }}/LogicAppUnit.Samples.LogicApps.Tests/TestResults/**.trx
83+
name: Test Results (${{ matrix.os }})
84+
path: ${{ github.workspace }}/LogicAppUnit.Samples.LogicApps.Tests/TestResults/*.trx
85+
path-replace-backslashes: true
7586
reporter: dotnet-trx

LogicAppUnit/Hosting/WorkflowTestHost.cs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
2-
// Licensed under the MIT License. See License.txt in the project root for license information.
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Threading.Tasks;
37

48
namespace LogicAppUnit.Hosting
59
{
6-
using System;
7-
using System.Collections.Generic;
8-
using System.Diagnostics;
9-
using System.IO;
10-
using System.Linq;
11-
using System.Threading.Tasks;
12-
1310
/// <summary>
1411
/// The function test host.
1512
/// </summary>
1613
internal class WorkflowTestHost : IDisposable
1714
{
15+
private const string FunctionsExecutableName = "func";
16+
1817
/// <summary>
1918
/// Get or sets the output data.
2019
/// </summary>
@@ -127,12 +126,12 @@ protected void StartFunctionRuntime(WorkflowTestInput[] inputs, string localSett
127126
RedirectStandardOutput = true,
128127
RedirectStandardError = true,
129128
UseShellExecute = false,
130-
CreateNoWindow = true,
129+
CreateNoWindow = true
131130
}
132131
};
133132

133+
// Hook up an event handler for the Standard Output stream
134134
var processStarted = new TaskCompletionSource<bool>();
135-
136135
this.Process.OutputDataReceived += (sender, args) =>
137136
{
138137
var outputData = args.Data;
@@ -153,6 +152,7 @@ protected void StartFunctionRuntime(WorkflowTestInput[] inputs, string localSett
153152
}
154153
};
155154

155+
// Hook up an event handler for the Standard Error stream
156156
var errorData = string.Empty;
157157
this.Process.ErrorDataReceived += (sender, args) =>
158158
{
@@ -165,11 +165,12 @@ protected void StartFunctionRuntime(WorkflowTestInput[] inputs, string localSett
165165
}
166166
};
167167

168+
// Start the Functions Core Tools process
168169
this.Process.Start();
169-
170170
this.Process.BeginOutputReadLine();
171171
this.Process.BeginErrorReadLine();
172172

173+
// Wait for the Functions runtime to start, or timeout after 2 minutes
173174
var result = Task.WhenAny(processStarted.Task, Task.Delay(TimeSpan.FromMinutes(2))).Result;
174175

175176
if (result != processStarted.Task)
@@ -200,15 +201,15 @@ protected void StartFunctionRuntime(WorkflowTestInput[] inputs, string localSett
200201
/// </summary>
201202
private static void KillFunctionHostProcesses()
202203
{
203-
Process[] processes = Process.GetProcessesByName("func");
204+
Process[] processes = Process.GetProcessesByName(FunctionsExecutableName);
204205
foreach (var process in processes)
205206
{
206207
process.Kill(true);
207208
}
208209
}
209210

210211
/// <summary>
211-
/// Retrieve the path of the 'func' executable (Azure Function core tools).
212+
/// Retrieve the path of the 'func' executable (Azure Functions Core tools).
212213
/// </summary>
213214
/// <returns>The path to the 'func' executable.</returns>
214215
/// <exception cref="Exception">Thrown when the location of the 'func' executable could not be found.</exception>
@@ -220,24 +221,30 @@ private static string GetEnvPathForFunctionTools()
220221
// Handle the differences between platforms
221222
if (OperatingSystem.IsWindows())
222223
{
223-
enviromentPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process);
224-
exeName = "func.exe";
224+
// The path to the 'func' executable can be in any of the environment variable scopes, depending on how the Functions Core Tools were installed.
225+
// If a DevOps build pipeline has updated the PATH environment variable for the 'Machine' or 'User' scopes, the 'Process' scope is not automatically updated to reflect the change.
226+
// So merge all three scopes to be sure!
227+
environmentPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + Path.PathSeparator +
228+
Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User) + Path.PathSeparator +
229+
Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
230+
exeName = $"{FunctionsExecutableName}.exe";
225231
}
226232
else
227233
{
228234
environmentPath = Environment.GetEnvironmentVariable("PATH");
229-
exeName = "func";
235+
exeName = FunctionsExecutableName;
230236
}
231237

232-
string exePath = environmentPath.Split(Path.PathSeparator).Select(x => Path.Combine(x, exeName)).Where(x => File.Exists(x)).FirstOrDefault();
233-
if (!string.IsNullOrWhiteSpace(exePath))
238+
var exePaths = environmentPath.Split(Path.PathSeparator).Distinct().Select(x => Path.Combine(x, exeName));
239+
string exePathMatch = exePaths.Where(x => File.Exists(x)).FirstOrDefault();
240+
if (!string.IsNullOrWhiteSpace(exePathMatch))
234241
{
235-
Console.WriteLine($"Path for Azure Function Core tools: {exePath}");
236-
return exePath;
242+
Console.WriteLine($"Path for Azure Function Core tools: {exePathMatch}");
243+
return exePathMatch;
237244
}
238245
else
239246
{
240-
throw new Exception("The enviroment variable PATH does not include the path for the 'func' executable.");
247+
throw new TestException($"The enviroment variable PATH does not include the path for the '{FunctionsExecutableName}' executable. Searched: {string.Join(Path.PathSeparator, exePaths)}");
241248
}
242249
}
243250

0 commit comments

Comments
 (0)