Skip to content

Commit b7bf252

Browse files
committed
Add Windows CI (build-test-installer-release)
* Add Windows GitHub Actions workflow - build-test-installer-release * Versioning is now handled via latest tag * Publish test results in the PR * rename SkipOnTeamCity to SkipOnCI
1 parent da4ae62 commit b7bf252

19 files changed

+320
-178
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
name: Build Test Installer Release
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
tags:
11+
- 'v*'
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: write
16+
id-token: write
17+
18+
env:
19+
Configuration: Release
20+
21+
jobs:
22+
build_test:
23+
name: Build and test
24+
runs-on: windows-latest
25+
outputs:
26+
version: ${{ steps.compute_version.outputs.version }}
27+
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v5
31+
with:
32+
fetch-depth: 0
33+
34+
- name: Compute Version
35+
id: compute_version
36+
shell: bash
37+
run: |
38+
# If this is a tag push and it starts with v, just use it and strip off the v
39+
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
40+
Version="${GITHUB_REF#refs/tags/}"
41+
Version="${Version#v}"
42+
echo "version=$Version" >> $GITHUB_OUTPUT
43+
echo "detected tag push: $Version"
44+
exit 0
45+
fi
46+
47+
# Otherwise find the latest tag that starts with v and strip off the v
48+
LAST_TAG=$(git describe --tags --match "v*" --abbrev=0 2>/dev/null || echo "v0.0.0")
49+
50+
# Compute a build number based on number of commits since that tag
51+
COMMITS_SINCE_TAG=$(git rev-list ${LAST_TAG}..HEAD --count)
52+
53+
# Final version: X.Y.Z
54+
Version="${LAST_TAG#v}.${COMMITS_SINCE_TAG}"
55+
echo "version=$Version" >> $GITHUB_OUTPUT
56+
echo "no tag push; using $Version"
57+
58+
- name: Setup NuGet
59+
uses: NuGet/setup-nuget@v2
60+
61+
- name: Restore NuGet packages
62+
run: nuget restore SayMore.sln
63+
64+
- name: Setup MSBuild
65+
uses: microsoft/setup-msbuild@v2
66+
with:
67+
vs-version: '[17.0,18.0)'
68+
69+
- name: Build solution
70+
run: msbuild build\SayMore.proj /t:Build /p:Configuration=$env:Configuration /p:Version=$env:Version /m
71+
72+
- name: Run tests
73+
run: msbuild build\SayMore.proj /t:Test /p:Configuration=$env:Configuration /p:useNUnit-x86=true /p:excludedCategories=SkipOnCI /m
74+
75+
- name: Upload test results
76+
if: always()
77+
uses: actions/upload-artifact@v4
78+
with:
79+
name: saymore-test-results
80+
if-no-files-found: warn
81+
path: output/${{ env.Configuration }}/TestResults.xml
82+
83+
build_installer:
84+
name: Build installer, sign, and publish artifacts
85+
needs: build_test
86+
if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))) }}
87+
runs-on: windows-latest
88+
env:
89+
Version: ${{ needs.build_test.outputs.version }}
90+
91+
steps:
92+
- name: Checkout
93+
uses: actions/checkout@v5
94+
with:
95+
fetch-depth: 0
96+
97+
- name: Setup NuGet
98+
uses: NuGet/setup-nuget@v2
99+
100+
- name: Restore NuGet packages
101+
run: nuget restore SayMore.sln
102+
103+
- name: Fetch documentation artifacts
104+
shell: bash
105+
run: build/getDependencies-windows.sh
106+
107+
- name: Setup MSBuild
108+
uses: microsoft/setup-msbuild@v2
109+
with:
110+
vs-version: '[17.0,18.0)'
111+
112+
- name: Build installer
113+
run: msbuild build\SayMore.proj /t:Installer /p:Configuration=$env:Configuration /p:useNUnit-x86=true /p:Version=$env:Version /m
114+
115+
- name: Sign installer
116+
if: ${{ github.event_name != 'pull_request' }}
117+
uses: sillsdev/codesign/trusted-signing-action@v3
118+
with:
119+
credentials: ${{ secrets.TRUSTED_SIGNING_CREDENTIALS }}
120+
files-folder: output/installer
121+
files-folder-filter: SayMoreInstaller*.msi
122+
123+
- name: Upload installer artifacts
124+
uses: actions/upload-artifact@v4
125+
with:
126+
name: saymore-installer
127+
if-no-files-found: error
128+
path: |
129+
output/installer/SayMoreInstaller*.msi
130+
output/installer/*.download_info
131+
output/installer/appcast.xml
132+
output/releasenotes.download_info
133+
134+
- name: Create release
135+
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
136+
uses: softprops/action-gh-release@v2
137+
with:
138+
files: |
139+
output/installer/SayMoreInstaller*.msi
140+
output/installer/*.download_info
141+
output/installer/appcast.xml
142+
output/releasenotes.download_info
143+
draft: true
144+
generate_release_notes: true

.github/workflows/test-report.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: 'Test Report'
2+
# This is recommended to run after the CI workflow to publish test results since it has different
3+
# permissions
4+
on:
5+
workflow_run:
6+
workflows: ['CI'] # runs after CI workflow
7+
types:
8+
- completed
9+
permissions:
10+
contents: read
11+
actions: read
12+
checks: write
13+
jobs:
14+
report:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: dorny/test-reporter@v2
18+
with:
19+
artifact: saymore-test-results
20+
name: NUnit Tests
21+
path: '*.xml' # Path to test results (inside artifact .zip)
22+
reporter: dotnet-nunit

build/SayMore.proj

Lines changed: 38 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<PropertyGroup>
3-
<RootDir Condition="'$(teamcity_version)' == ''">$(MSBuildProjectDirectory)\..</RootDir>
4-
<RootDir Condition="'$(teamcity_version)' != ''">$(teamcity_build_checkoutDir)</RootDir>
5-
<BUILD_NUMBER Condition="'$(BUILD_NUMBER)'==''">3.7.100</BUILD_NUMBER>
3+
<RootDir>$(MSBuildProjectDirectory)\..</RootDir>
4+
<RootDir Condition="'$(GITHUB_WORKSPACE)' != ''">$(GITHUB_WORKSPACE)</RootDir>
65
<BuildTasksDll Condition="Exists('$(RootDir)/packages/SIL.BuildTasks.3.1.1/tools/SIL.BuildTasks.dll')">$(RootDir)/packages/SIL.BuildTasks.3.1.1/tools/SIL.BuildTasks.dll</BuildTasksDll>
76
<BuildTasksDll Condition="!Exists('$(RootDir)/packages/SIL.BuildTasks.3.1.1/tools/SIL.BuildTasks.dll')">$(RootDir)/packages/SIL.BuildTasks/tools/SIL.BuildTasks.dll</BuildTasksDll>
87
<SILReleaseTasksProps>$(RootDir)/packages/SIL.ReleaseTasks.3.1.1/build/SIL.ReleaseTasks.props</SILReleaseTasksProps>
@@ -20,7 +19,6 @@
2019
<Exec Command='$(NuGetCommand) install SIL.BuildTasks -excludeVersion -PreRelease -source "$(PackageSources)" -solutionDirectory "$(SolutionDir)."' />
2120
<Exec Command='$(NuGetCommand) install SIL.ReleaseTasks -excludeVersion -PreRelease -source "$(PackageSources)" -solutionDirectory "$(SolutionDir)"' />
2221
<Exec Command='$(NuGetCommand) install SIL.libpalaso.l10ns -excludeVersion -PreRelease -source "$(PackageSources)" -solutionDirectory "$(SolutionDir)."' />
23-
<Exec Command='$(NuGetCommand) install NUnit.Extension.TeamCityEventListener -excludeVersion -source "$(PackageSources)" -solutionDirectory "$(SolutionDir)."' />
2422
<MSBuild Projects="$(MSBuildProjectFullPath)" Targets="BuildInternal"
2523
Properties="Configuration=$(Configuration)" Condition="$(RestartBuild)" />
2624
</Target>
@@ -33,20 +31,12 @@
3331
<UsingTask TaskName="NUnit3" AssemblyFile="$(BuildTasksDll)" Condition="Exists('$(BuildTasksDll)')" />
3432

3533
<Target Name="VersionNumbers">
36-
<Message Text="BUILD_NUMBER: $(BUILD_NUMBER)" Importance="high"/>
34+
<PropertyGroup>
35+
<!-- The Version should be set from the outside, if not, set a meaningless default value -->
36+
<Version Condition="'$(Version)' == ''">1.0.0.0</Version>
37+
</PropertyGroup>
3738

38-
<Split Input="$(BUILD_NUMBER)" Delimiter="." OutputSubString="2">
39-
<Output TaskParameter="ReturnValue" PropertyName="BuildCounter" />
40-
</Split>
41-
42-
<Message Text="BuildCounter: $(BuildCounter)" Importance="high"/>
43-
44-
<!-- Note, after some thought, we've decided this is the best place to keep the version number (not on TeamCity, not in the assemblies). -->
45-
<CreateProperty Value="3.7.$(BuildCounter)">
46-
<Output PropertyName="Version" TaskParameter="Value"/>
47-
</CreateProperty>
48-
49-
<Message Text="Version: $(Version)" Importance="high"/>
39+
<Message Text="Version: $(Version)" Importance="high"/>
5040
</Target>
5141

5242
<Import Project="$(SILReleaseTasksProps)" Condition="Exists('$(SILReleaseTasksProps)')" />
@@ -84,7 +74,7 @@
8474
<ItemGroup>
8575
<TestAssemblies Include="$(RootDir)/output/release/*Tests.dll;"/>
8676
</ItemGroup>
87-
<NUnit3 Condition="'$(teamcity_version)' == ''"
77+
<NUnit3
8878
Assemblies="@(TestAssemblies)"
8979
ToolPath="$(RootDir)/packages/NUnit.ConsoleRunner.3.20.1/tools"
9080
ExcludeCategory="$(excludedCategories)"
@@ -94,15 +84,7 @@
9484
OutputXmlFile="$(RootDir)/output/$(Configuration)/TestResults.xml"
9585
UseNUnit3Xml = "true"
9686
TeamCity="false"/>
97-
<NUnit3 Condition="'$(teamcity_version)' != ''"
98-
Assemblies="@(TestAssemblies)"
99-
ToolPath="$(RootDir)/packages/NUnit.ConsoleRunner.3.20.1/tools"
100-
ExcludeCategory="SkipOnTeamCity,$(excludedCategories)"
101-
WorkingDirectory="$(RootDir)/output/$(Configuration)"
102-
Force32Bit="$(useNUnit-x86)"
103-
Verbose="true"
104-
TeamCity="true"/>
105-
</Target>
87+
</Target>
10688

10789
<Target Name="UpdateDownloadInfo" DependsOnTargets="VersionNumbers" >
10890

@@ -149,21 +131,34 @@
149131
changing of the script, but I haven't figured that out. -->
150132

151133
<FileUpdate File="$(RootDir)\src\Installer\Installer.wxs" Regex='Property_ProductVersion = ".*"'
152-
ReplacementText ="Property_ProductVersion = &quot;$(Version)&quot;" />
134+
ReplacementText ="Property_ProductVersion = &quot;$(Version)&quot;" />
153135

154136
<Message Text="Making Installer Version: $(Version)" Importance="high" />
155137

156138
<MSBuild Projects="$(RootDir)\src\Installer\Installer.wixproj"/>
157139

158-
<!-- remove an existing one with the same name, if necessary -->
159-
<Delete Files="$(RootDir)\output\installer\SayMoreInstaller.$(Version).msi" TreatErrorsAsWarnings="false" />
140+
<!-- Ensure destination folder exists -->
141+
<MakeDir Directories="$(RootDir)\output\installer" />
160142

161-
<Exec Command='sign /d "SayMoreInstaller.$(Version).msi" "$(RootDir)\output\installer\SayMoreInstaller.msi"'></Exec>
143+
<PropertyGroup>
144+
<InstallerSourceMsi>$(RootDir)\output\installer\SayMoreInstaller.msi</InstallerSourceMsi>
145+
<InstallerSourceMsi Condition="!Exists('$(InstallerSourceMsi)')">$(RootDir)\src\Installer\bin\$(Configuration)\SayMoreInstaller.msi</InstallerSourceMsi>
146+
<InstallerSourceMsi Condition="!Exists('$(InstallerSourceMsi)')">$(RootDir)\src\Installer\bin\$(Configuration)\en-us\SayMoreInstaller.msi</InstallerSourceMsi>
147+
</PropertyGroup>
162148

163-
<Copy SourceFiles="$(RootDir)\output\installer\SayMoreInstaller.msi"
164-
DestinationFiles="$(RootDir)\output\installer\SayMoreInstaller.$(Version).msi"
165-
/>
166-
149+
<!-- Helpful debug -->
150+
<Message Text="Looking for MSI at: $(InstallerSourceMsi)" Importance="high" />
151+
152+
<!-- Guard with an explicit error if it's missing -->
153+
<Error
154+
Text="Expected MSI not found at $(InstallerSourceMsi)."
155+
Condition="!Exists('$(InstallerSourceMsi)')" />
156+
157+
<!-- Copy + rename with version -->
158+
<Copy
159+
SourceFiles="$(InstallerSourceMsi)"
160+
DestinationFiles="$(RootDir)\output\installer\SayMoreInstaller.$(Version).msi" />
161+
167162
<!-- appcast.xml is used as part of the update notification system -->
168163
<Copy SourceFiles ="$(RootDir)\src\Installer\appcast.xml"
169164
DestinationFolder ="$(RootDir)\output\installer"/>
@@ -177,46 +172,27 @@
177172

178173
</Target>
179174

180-
<!-- The "Installer" Target used to depend on this, but the Edolo Sample data was removed
181-
in version 3 of SayMore, so there is no need to continue to generate this WIX include
182-
file. (See SampleData\ReadMe.txt)
183-
<Target Name="MakeWixForSampleData">
184-
<MakeDir ContinueOnError ="true" Directories ="$(RootDir)\output\Installer\"/>
185-
186-
<MakeWixForDirTree
187-
DirectoryReferenceId="SampleDataDir"
188-
GiveAllPermissions="true"
189-
ComponentGroupId="SampleData"
190-
RootDirectory="$(RootDir)\SampleData"
191-
OutputFilePath="$(RootDir)\output\Installer\GeneratedSampleDataFiles.wxs"
192-
MatchRegExPattern=".*"
193-
>
194-
<Output TaskParameter="OutputFilePath" ItemName="Compile" />
195-
</MakeWixForDirTree>
196-
</Target>
197-
-->
198-
199175
<Target Name="copyLibL10ns" DependsOnTargets="RestoreLocalPackages">
200176
<Error Text="Palaso L10ns package missing. Expected at $(RootDir)/packages/SIL.libpalaso.l10ns"
201177
Condition="!Exists('$(RootDir)/packages/SIL.libpalaso.l10ns/SIL.libpalaso.l10ns.nupkg')" />
202178
<ItemGroup>
203179
<XliffFiles Include="$(RootDir)/packages/SIL.libpalaso.l10ns/content/**/*.xlf"/>
204180
</ItemGroup>
205181
<Copy SourceFiles="@(XliffFiles)"
206-
DestinationFiles="@(XliffFiles->'$(RootDir)/DistFiles/%(Filename)%(Extension)')"
207-
SkipUnchangedFiles="true"/>
182+
DestinationFiles="@(XliffFiles->'$(RootDir)/DistFiles/%(Filename)%(Extension)')"
183+
SkipUnchangedFiles="true"/>
208184
</Target>
209185

210186
<Target Name="MakeWixForDistFiles" DependsOnTargets="copyLibL10ns">
211187
<MakeDir ContinueOnError ="true" Directories ="$(RootDir)\output\Installer\"/>
212188

213189
<MakeWixForDirTree
214-
DirectoryReferenceId="ProgramDir"
215-
ComponentGroupId="DistFiles"
216-
RootDirectory="$(RootDir)\DistFiles"
217-
OutputFilePath="$(RootDir)\output\Installer\GeneratedDistFiles.wxs"
218-
MatchRegExPattern=".*"
219-
>
190+
DirectoryReferenceId="ProgramDir"
191+
ComponentGroupId="DistFiles"
192+
RootDirectory="$(RootDir)\DistFiles"
193+
OutputFilePath="$(RootDir)\output\Installer\GeneratedDistFiles.wxs"
194+
MatchRegExPattern=".*"
195+
>
220196
<!--what does this do?-->
221197
<Output TaskParameter="OutputFilePath" ItemName="Compile" />
222198
</MakeWixForDirTree>

src/SayMoreTests/ApplicationContextTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void TearDown()
2929
/// <summary>
3030
/// This is mostly a "smoke test" for the Dependency Injection System
3131
/// </summary>
32-
[Test][Category("SkipOnTeamCity")]
32+
[Test][Category("SkipOnCI")]
3333
public void CreateWelcomeDialog_NotNull()
3434
{
3535
using(var appContext = new ApplicationContainer())
@@ -41,7 +41,7 @@ public void CreateWelcomeDialog_NotNull()
4141
}
4242
}
4343

44-
[Test][Category("SkipOnTeamCity")]
44+
[Test][Category("SkipOnCI")]
4545
public void CreateWelcomeDialog_CanCreateOnAfterAnother()
4646
{
4747
using (var appContext = new ApplicationContainer())
@@ -55,7 +55,7 @@ public void CreateWelcomeDialog_CanCreateOnAfterAnother()
5555
/// This is mostly a "smoke test" for the Dependency Injection System
5656
/// </summary>
5757
[Test, Apartment(ApartmentState.STA)]
58-
[Category("SkipOnTeamCity")]
58+
[Category("SkipOnCI")]
5959
public void CreateProjectContext_ProjectWindowIsNotNull()
6060
{
6161
using (var appContext = new ApplicationContainer())
@@ -74,7 +74,7 @@ private ProjectContext CreateProjectContext(ApplicationContainer appContext)
7474

7575

7676
[Test, Apartment(ApartmentState.STA)]
77-
[Category("SkipOnTeamCity")]
77+
[Category("SkipOnCI")]
7878
public void CreateProjectContext_CanCreateTwoProjectsConsecutively()
7979
{
8080
using (var appContext = new ApplicationContainer())
@@ -93,7 +93,7 @@ public void CreateProjectContext_CanCreateTwoProjectsConsecutively()
9393
}
9494

9595
[Test, Apartment(ApartmentState.STA)]
96-
[Category("SkipOnTeamCity")]
96+
[Category("SkipOnCI")]
9797
public void ContainerSanityCheck_CanGet_ComponentFile_Factory()
9898
{
9999
using (var appContext = new ApplicationContainer())

0 commit comments

Comments
 (0)