Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Windows x64 and arm64 builds #1846

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
windows:
name: Windows
runs-on: windows-latest
strategy:
matrix:
runtime: [win-x86, win-x64, win-arm64]

steps:
- uses: actions/checkout@v4
Expand All @@ -27,24 +30,31 @@ jobs:
run: dotnet restore

- name: Build
run: dotnet build --configuration WindowsRelease
run: |
dotnet build src/windows/Installer.Windows/Installer.Windows.csproj `
--configuration=Release `
--runtime=${{ matrix.runtime }}
Comment on lines -30 to +36
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, dotnet build doesn't support setting --runtime at the solution level. I basically took the same logic as #1633 by setting it at the project level: the Installer.Windows project has all the necessary dependencies so this builds correctly.

- name: Test
# GitHub's hosted runners are x64 so can test x64 and x86, but not arm64
if: matrix.runtime != 'win-arm64'
run: |
dotnet test --verbosity normal --configuration=WindowsRelease
dotnet test --verbosity normal `
--configuration=WindowsRelease `
--runtime=${{ matrix.runtime }}
- name: Prepare artifacts
shell: bash
run: |
mkdir -p artifacts/bin
mv out/windows/Installer.Windows/bin/Release/net472/win-x86 artifacts/bin/
cp out/windows/Installer.Windows/bin/Release/net472/win-x86.sym/* artifacts/bin/win-x86/
mv out/windows/Installer.Windows/bin/Release/net472/gcm*.exe artifacts/
mv out/windows/Installer.Windows/bin/Release/net472/${{ matrix.runtime }}/gcm*.exe artifacts/
mv out/windows/Installer.Windows/bin/Release/net472/${{ matrix.runtime }} artifacts/bin/
cp out/windows/Installer.Windows/bin/Release/net472/${{ matrix.runtime }}.sym/* artifacts/bin/${{ matrix.runtime }}/
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: win-x86
name: ${{ matrix.runtime }}
path: |
artifacts
Expand Down
21 changes: 15 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ jobs:
runs-on: windows-latest
environment: release
needs: prereqs
strategy:
matrix:
runtime: [win-x86, win-x64, win-arm64]
steps:
- uses: actions/checkout@v4

Expand All @@ -156,18 +159,23 @@ jobs:

- name: Build
run: |
dotnet build --configuration=WindowsRelease
dotnet build src/windows/Installer.Windows/Installer.Windows.csproj `
--configuration=Release `
--runtime=${{ matrix.runtime }}

- name: Run Windows unit tests
# GitHub's hosted runners are x64 so can test x64 and x86, but not arm64
if: matrix.runtime != 'win-arm64'
run: |
dotnet test --configuration=WindowsRelease
dotnet test --configuration=WindowsRelease --runtime=${{ matrix.runtime }}

- name: Lay out Windows payload and symbols
run: |
cd $env:GITHUB_WORKSPACE\src\windows\Installer.Windows\
./layout.ps1 -Configuration WindowsRelease `
./layout.ps1 -Configuration Release `
-Output $env:GITHUB_WORKSPACE\payload `
-SymbolOutput $env:GITHUB_WORKSPACE\symbols
-SymbolOutput $env:GITHUB_WORKSPACE\symbols `
-RuntimeIdentifier ${{ matrix.runtime }}

- name: Log into Azure
uses: azure/login@v2
Expand Down Expand Up @@ -198,9 +206,10 @@ jobs:
run: |
dotnet build $env:GITHUB_WORKSPACE\src\windows\Installer.Windows `
/p:PayloadPath=$env:GITHUB_WORKSPACE\payload /p:NoLayout=true `
--configuration=WindowsRelease
--configuration=WindowsRelease `
--runtime=${{ matrix.runtime }}
mkdir installers
Move-Item -Path .\out\windows\Installer.Windows\bin\Release\net472\*.exe `
Move-Item -Path .\out\windows\Installer.Windows\bin\Release\net472\${{ matrix.runtime }}\gcm*.exe `
-Destination $env:GITHUB_WORKSPACE\installers

- name: Sign installers with Azure Code Signing
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OSPlatform)'=='windows'">net472;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;osx-x64;linux-x64;osx-arm64;linux-arm64;linux-arm</RuntimeIdentifiers>
<PlatformTarget Condition="'$(OSPlatform)'=='windows'">x86</PlatformTarget>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;osx-x64;linux-x64;osx-arm64;linux-arm64;linux-arm</RuntimeIdentifiers>
<AssemblyName>git-credential-manager</AssemblyName>
<RootNamespace>GitCredentialManager</RootNamespace>
<ApplicationIcon>$(RepoAssetsPath)gcmicon.ico</ApplicationIcon>
Expand Down
18 changes: 13 additions & 5 deletions src/windows/Installer.Windows/Installer.Windows.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<Project>
<Project>
<!-- Implicit SDK props import -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />

<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<EnableDefaultItems>false</EnableDefaultItems>
<PayloadPath>$(PlatformOutPath)Installer.Windows\bin\$(Configuration)\net472\win-x86</PayloadPath>
<PayloadPath>$(PlatformOutPath)Installer.Windows\bin\$(Configuration)\net472\$(RuntimeIdentifier)</PayloadPath>
<InnoSetupVersion>6.3.1</InnoSetupVersion>
</PropertyGroup>

Expand All @@ -27,12 +27,20 @@

<Target Name="CoreCompile" Condition="'$(OSPlatform)'=='windows'">
<PropertyGroup>
<InnoSetupCommandSystem>"$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=system "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)"</InnoSetupCommandSystem>
<InnoSetupCommandUser>"$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=user "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)"</InnoSetupCommandUser>
<InnoSetupCommandSystem>"$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=system /DGcmRuntimeIdentifier="$(RuntimeIdentifier)" "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)"</InnoSetupCommandSystem>
<InnoSetupCommandUser>"$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=user /DGcmRuntimeIdentifier="$(RuntimeIdentifier)" "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)"</InnoSetupCommandUser>
</PropertyGroup>

<Message Text="Lay Out" Importance="High" />
<Exec Condition="'$(NoLayout)'!='true'" Command="powershell.exe –NonInteractive –ExecutionPolicy Unrestricted -Command &quot;&amp; {&amp;'$(MSBuildProjectDirectory)\layout.ps1' -Configuration '$(Configuration)' -Output '$(PayloadPath)'}&quot;" />
<Exec Condition="'$(NoLayout)'!='true'"
ConsoleToMSBuild="true"
Command="powershell.exe –NonInteractive –ExecutionPolicy Unrestricted -Command &quot;&amp; {&amp;'$(MSBuildProjectDirectory)\layout.ps1' -Configuration '$(Configuration)' -Output '$(PayloadPath)' -RuntimeIdentifier '$(RuntimeIdentifier)'; if ($?) { exit 0 } else { exit 1 }}&quot;"
IgnoreExitCode="true">
<!-- If we want to display the console output if the exit code is not 0, we need to capture it and then output it using the <Error /> below -->
<Output TaskParameter="ExitCode" PropertyName="ExitCodeOfExec" />
<Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
</Exec>
<Error Condition="'$(NoLayout)'!='true' AND '$(ExitCodeOfExec)' != '0'" Text="Layout script failed with exit code $(ExitCodeOfExec) and message $(OutputOfExec)" />
Comment on lines -35 to +43
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While updating layout.ps1, this initially just errored with the non-helpful ... exited with code 1. The changes here ensure that the actual output of the layout.ps1 script will also be shown in case it fails.

<Message Text="$(InnoSetupCommandSystem)" Importance="High" />
<Exec Command="$(InnoSetupCommandSystem)" />
<Message Text="$(InnoSetupCommandUser)" Importance="High" />
Expand Down
15 changes: 13 additions & 2 deletions src/windows/Installer.Windows/Setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#error Installer target property 'InstallTarget' must be specifed
#endif

#ifndef GcmRuntimeIdentifier
#error GCM Runtime Identifier 'GcmRuntimeIdentifier' must be specifed (e.g. win-x64)
#endif

#if InstallTarget == "user"
#define GcmAppId "{{aa76d31d-432c-42ee-844c-bc0bc801cef3}}"
#define GcmLongName "Git Credential Manager (User)"
Expand All @@ -40,7 +44,6 @@
#define GcmRepoRoot "..\..\.."
#define GcmAssets GcmRepoRoot + "\assets"
#define GcmExe "git-credential-manager.exe"
#define GcmArch "x86"

#ifnexist PayloadDir + "\" + GcmExe
#error Payload files are missing
Expand All @@ -67,9 +70,17 @@ AppUpdatesURL={#GcmUrl}
AppContact={#GcmUrl}
AppCopyright={#GcmCopyright}
AppReadmeFile={#GcmReadme}
; Windows ARM64 supports installing and running x64 binaries, but not vice versa.
#if GcmRuntimeIdentifier=="win-x64"
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
#elif GcmRuntimeIdentifier=="win-arm64"
ArchitecturesAllowed=arm64
ArchitecturesInstallIn64BitMode=arm64
#endif
Comment on lines +73 to +80
Copy link
Author

@dennisameling dennisameling Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this logic, the supported installation types are:

Machine x86 GCM installer x64 GCM installer arm64 GCM installer
x86
x64
arm64

This ensures that e.g. x64 Windows can not accidentally install the arm64 GCM version, which it can't run. If you want, we could even make this more strict and force users of x64 Windows to install the x64 version and prohibit installation of the x86 version on that architecture, even though it will work on there as well. But that probably goes a bit too far.

Also, on x64 and arm64, this will install GCM into C:\Program Files\Git Credential Manager instead of C:\Program Files (x86)\Git Credential Manager.

VersionInfoVersion={#GcmVersion}
LicenseFile={#GcmRepoRoot}\LICENSE
OutputBaseFilename={#GcmSetupExe}-win-{#GcmArch}-{#GcmVersionSimple}
OutputBaseFilename={#GcmSetupExe}-{#GcmRuntimeIdentifier}-{#GcmVersionSimple}
DefaultDirName={autopf}\{#GcmShortName}
Compression=lzma2
SolidCompression=yes
Expand Down
12 changes: 8 additions & 4 deletions src/windows/Installer.Windows/layout.ps1
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Inputs
param ([Parameter(Mandatory)] $CONFIGURATION, [Parameter(Mandatory)] $OUTPUT, $SYMBOLOUTPUT)
param ([Parameter(Mandatory)] $CONFIGURATION, [Parameter(Mandatory)] $OUTPUT, [Parameter(Mandatory)] $RuntimeIdentifier, $SYMBOLOUTPUT)

Write-Output "Output: $OUTPUT"

if ($RuntimeIdentifier -ne 'win-x86' -and $RuntimeIdentifier -ne 'win-x64' -and $RuntimeIdentifier -ne 'win-arm64') {
Write-Host "Unsupported RuntimeIdentifier: $RuntimeIdentifier"
exit 1
}

# Directories
$THISDIR = $pwd.path
$ROOT = (Get-Item $THISDIR).parent.parent.parent.FullName
Expand Down Expand Up @@ -39,15 +44,14 @@ Write-Output "Publishing core application..."
dotnet publish "$GCM_SRC" `
--framework net472 `
--configuration "$CONFIGURATION" `
--runtime win-x86 `
--runtime $RuntimeIdentifier `
--output "$PAYLOAD"

# Delete libraries that are not needed for Windows but find their way
# into the publish output.
Remove-Item -Path "$PAYLOAD/*.dylib" -Force

# Delete extraneous files that get included for other architectures
# We only care about x86 as the core GCM executable is only targeting x86
# Delete extraneous files that get included for other runtimes
Remove-Item -Path "$PAYLOAD/arm/" -Recurse -Force
Remove-Item -Path "$PAYLOAD/arm64/" -Recurse -Force
Remove-Item -Path "$PAYLOAD/x64/" -Recurse -Force
Expand Down