Skip to content

Commit 66423b3

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 66423b3

20 files changed

+331
-181
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

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SayMore
1+
# SayMore
22

33
SayMore provides an intuitive and enjoyable way to manage language documentation tasks.
44

@@ -22,6 +22,14 @@ Please see [Tips for Testing Palaso Software](https://docs.google.com/document/d
2222

2323
Reports can be entered in https://jira.sil.org/projects/SP/issues. They can be entered there via email by sending to saymore_issues@sil.org.
2424

25-
### Continuous Build System
25+
## Github Actions CI
26+
Every PR triggers a build and runs tests.
2627

27-
Each time code is checked in, an automatic build begins on our [TeamCity build server](https://build.palaso.org/project.html?projectId=SayMore&tab=projectOverview), running all the unit tests. This automatic build doesn't publish a new installer, however. That kind of build is launched manually, by pressing a button on the TeamCity page. This "publish" process builds SayMore, makes and installer, rsyncs it to the distribution server, and writes out a little bit of html which the [SayMore download page](https://software.sil.org/saymore/download/) then displays to the user. It also creates a build artifact that enables the SayMore program to check to see whether a newer version is available.
28+
Every commit on the main branch will produce a signed installer for user testing as desired. Simply go to the Actions tab and find the Actions run for that commit if you want to download the installer. The installer is attached to the run artifact.
29+
30+
# Release Process
31+
To release a new version, just tag a commit on the main branch e.g. v3.7.5
32+
This will trigger Github Actions to produce a Release with the signed installer attached
33+
34+
# Release TODO
35+
Implement the "publish" process on TeamCity, which builds SayMore, makes and installer, rsyncs it to the distribution server, and writes out a little bit of html which the [SayMore download page](https://software.sil.org/saymore/download/) then displays to the user. It also creates a build artifact that enables the SayMore program to check to see whether a newer version is available.

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>

0 commit comments

Comments
 (0)