Skip to content

Commit d4759d6

Browse files
authored
chore: native binary distribution via winget (#430)
* chore: create the windows binary, installer and publish to winget
1 parent d6277e6 commit d4759d6

14 files changed

+259
-29
lines changed

.github/actions/build-binaries/windows/action.yaml

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,87 @@ inputs:
44
package_name:
55
description: "The name of the package"
66
required: true
7+
version:
8+
description: "The version to use for this artifact"
79
artifacts_dir:
810
description: "The directory to write artifacts you want to publish"
911
required: true
10-
version:
11-
description: "The version to use for this artifact"
12+
production_release:
13+
description: "Flag to determine if this is a production release"
14+
required: true
15+
azure_tenant_id:
16+
description: "The Microsoft Entra tenant (directory) ID."
17+
required: true
18+
azure_client_id:
19+
description: "The client (application) ID of an App Registration in the tenant."
20+
required: true
21+
azure_client_secret:
22+
description: "A client secret that was generated for the App Registration."
23+
required: true
1224

1325
runs:
1426
using: "composite"
1527
steps:
28+
- name: Configure build environment
29+
shell: bash
30+
run: |
31+
echo 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64' >> $GITHUB_PATH
32+
echo 'BINARY_BUILD_DIR=dist\algokit' >> $GITHUB_ENV
33+
echo 'WINGET_INSTALLER=${{ inputs.artifacts_dir }}\${{ inputs.package_name }}-winget.msix' >> $GITHUB_ENV
34+
1635
- name: Build binary
1736
shell: bash
1837
run: |
1938
poetry run poe package_windows
2039
21-
- name: Package binary artifact
40+
- name: Add metadata to binary
41+
shell: bash
42+
run: |
43+
echo winget > '${{ env.BINARY_BUILD_DIR }}\_internal\algokit\resources\distribution-method'
44+
45+
- name: Sign executable
46+
if: ${{ inputs.production_release == 'true' }}
47+
uses: azure/trusted-signing-action@v0.3.20
48+
with:
49+
azure-tenant-id: ${{ inputs.azure_tenant_id }}
50+
azure-client-id: ${{ inputs.azure_client_id }}
51+
azure-client-secret: ${{ inputs.azure_client_secret }}
52+
endpoint: https://weu.codesigning.azure.net/
53+
trusted-signing-account-name: algokit-signing
54+
certificate-profile-name: algokit
55+
files-folder: ${{ env.BINARY_BUILD_DIR }}
56+
files-folder-filter: exe
57+
file-digest: SHA256
58+
timestamp-rfc3161: http://timestamp.acs.microsoft.com
59+
timestamp-digest: SHA256
60+
61+
- name: Build winget installer
2262
shell: pwsh
2363
run: |
24-
Set-Location -Path dist\algokit\
25-
tar -zcf ${{ inputs.artifacts_dir }}\${{ inputs.package_name }}.tar.gz *
26-
Set-Location -Path ..\..
64+
& .\scripts\winget\build-installer.ps1 `
65+
-binaryDir '${{ env.BINARY_BUILD_DIR }}' `
66+
-releaseVersion '${{ inputs.version }}' `
67+
-outputFile '${{ env.WINGET_INSTALLER }}'
68+
69+
- name: Sign winget installer
70+
if: ${{ inputs.production_release == 'true' }}
71+
uses: azure/trusted-signing-action@v0.3.20
72+
with:
73+
azure-tenant-id: ${{ inputs.azure_tenant_id }}
74+
azure-client-id: ${{ inputs.azure_client_id }}
75+
azure-client-secret: ${{ inputs.azure_client_secret }}
76+
endpoint: https://weu.codesigning.azure.net/
77+
trusted-signing-account-name: algokit-signing
78+
certificate-profile-name: algokit
79+
files-folder: ${{ env.WINGET_INSTALLER }}
80+
files-folder-filter: msix
81+
file-digest: SHA256
82+
timestamp-rfc3161: http://timestamp.acs.microsoft.com
83+
timestamp-digest: SHA256
2784

28-
- name: Upload binary artifact
85+
- name: Upload winget artifact
2986
uses: actions/upload-artifact@v4
3087
with:
31-
name: ${{ inputs.package_name }}
32-
path: ${{ inputs.artifacts_dir }}\${{ inputs.package_name }}.tar.gz
88+
name: ${{ inputs.package_name }}-winget
89+
path: ${{ env.WINGET_INSTALLER }}
90+
if-no-files-found: error

.github/workflows/build-binaries.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ jobs:
6666
package_name: ${{ env.PACKAGE_NAME }}
6767
version: ${{ env.RELEASE_VERSION }}
6868
artifacts_dir: ${{ env.ARTIFACTS_DIR }}
69+
production_release: ${{ inputs.production_release }}
70+
azure_tenant_id: ${{ secrets.AZURE_TENANT_ID }}
71+
azure_client_id: ${{ secrets.AZURE_CLIENT_ID }}
72+
azure_client_secret: ${{ secrets.AZURE_CLIENT_SECRET }}
6973

7074
- name: Build linux binary
7175
if: ${{ runner.os == 'Linux' }}

.github/workflows/publish-release-packages.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ on:
2222
default: true
2323
type: boolean
2424
description: "Publish to snap repository"
25+
do_winget:
26+
required: false
27+
default: true
28+
type: boolean
29+
description: "Publish to Winget repository"
2530
workflow_dispatch:
2631
inputs:
2732
release:
@@ -38,6 +43,11 @@ on:
3843
default: true
3944
type: boolean
4045
description: "Publish to Chocolatey repository"
46+
do_winget:
47+
required: false
48+
default: true
49+
type: boolean
50+
description: "Publish to Winget repository"
4151
do_snap:
4252
required: false
4353
default: true
@@ -114,6 +124,20 @@ jobs:
114124
with:
115125
args: push --source https://push.chocolatey.org/
116126

127+
publish-winget:
128+
runs-on: windows-latest
129+
if: ${{ inputs.do_winget }}
130+
steps:
131+
- name: Publish to winget
132+
shell: pwsh
133+
env:
134+
WINGET_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
135+
run: |
136+
echo 'Publishing to winget'
137+
# The below can be uncommented after we've manually created the first release on winget
138+
# & .\scripts\winget\update-package.ps1 `
139+
# -releaseVersionTag '${{ inputs.release }}'
140+
117141
publish-snap:
118142
runs-on: ubuntu-latest
119143
if: ${{ inputs.do_snap }}

poetry.lock

Lines changed: 19 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ aiohttp = "3.9.5"
2727
jsondiff = "^2.0.0"
2828

2929
[tool.poetry.group.dev.dependencies]
30-
pyinstaller = {version = "^6.3.0"}
30+
pyinstaller = {version = "^6.9.0"}
3131
pytest = "^7.2.0"
3232
ruff = ">=0.1.6,<0.5.0"
3333
pip-audit = "^2.4.7"

scripts/winget/build-installer.ps1

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
Param(
2+
[Parameter(Mandatory = $true)]
3+
[String]
4+
$binaryDir,
5+
6+
[Parameter(Mandatory = $false)]
7+
[AllowEmptyString()]
8+
[String]
9+
$releaseVersion,
10+
11+
[Parameter(Mandatory = $true)]
12+
[String]
13+
$outputFile
14+
)
15+
16+
Function ThrowOnNonZeroExit {
17+
Param( [String]$Message )
18+
If ($LastExitCode -ne 0) {
19+
Throw $Message
20+
}
21+
}
22+
23+
$ErrorActionPreference = 'Stop'
24+
25+
Remove-Item -Path build -Recurse -ErrorAction Ignore
26+
$buildDir = New-Item -ItemType Directory -Path .\build\winget\installer
27+
$installerContentDir = '.\scripts\winget\installer'
28+
29+
# Add installer assets
30+
$assetsDir = New-Item -ItemType Directory -Path (Join-Path $buildDir assets)
31+
Copy-Item -Path "$installerContentDir\assets\*" -Destination $assetsDir -Recurse | Out-Null
32+
33+
# Add manifest file
34+
$version = if ($releaseVersion) {
35+
# Strip the pre-release meta, as it's not valid
36+
$releaseVersion -replace '-\w+|\+.+$', ''
37+
}
38+
else {
39+
'0.0.1'
40+
}
41+
(Get-Content (Resolve-Path "$installerContentDir\AppxManifest.xml")).Replace('0.0.1.0', $("$version.0")) | Set-Content (Join-Path $buildDir AppxManifest.xml)
42+
43+
# Generate pri resource map for installer assets
44+
$priConfig = (Resolve-Path "$installerContentDir\priconfig.xml")
45+
Push-Location $buildDir
46+
makepri new /ProjectRoot $buildDir /ConfigXml $priConfig | Out-Null
47+
ThrowOnNonZeroExit "Failed to create pri file"
48+
Pop-Location
49+
50+
# Add algokit binaries
51+
Copy-Item -Path (Join-Path $binaryDir *) -Destination $buildDir -Recurse | Out-Null
52+
53+
# Generate msix
54+
makeappx pack /o /h SHA256 /d $buildDir /p $outputFile | Out-Null
55+
ThrowOnNonZeroExit "Failed to build msix"
56+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap uap3 uap10 desktop rescap">
3+
<Identity Name="algokit" Publisher="CN=Algorand Foundation, O=Algorand Foundation, L=Singapore, C=SG" Version="0.0.1.0" ProcessorArchitecture="x64" />
4+
<Properties>
5+
<DisplayName>AlgoKit</DisplayName>
6+
<PublisherDisplayName>Algorand Foundation</PublisherDisplayName>
7+
<Description>The Algorand AlgoKit CLI is the one-stop shop tool for developers building on the Algorand network.</Description>
8+
<Logo>assets\Square70x70Logo.png</Logo>
9+
</Properties>
10+
<Resources>
11+
<Resource Language="en-us" />
12+
</Resources>
13+
<Dependencies>
14+
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.0" />
15+
</Dependencies>
16+
<Applications>
17+
<Application Id="algokit" Executable="algokit.exe" EntryPoint="Windows.FullTrustApplication">
18+
<uap:VisualElements BackgroundColor="transparent" DisplayName="Algorand AlgoKit CLI" Square150x150Logo="assets\Square150x150Logo.png" Square44x44Logo="assets\Square44x44Logo.png" Description="The Algorand AlgoKit CLI is the one-stop shop tool for developers building on the Algorand network.">
19+
<uap:DefaultTile ShortName="AlgoKit" Square71x71Logo="assets\Square70x70Logo.png" />
20+
</uap:VisualElements>
21+
<Extensions>
22+
<uap3:Extension Category="windows.appExecutionAlias">
23+
<uap3:AppExecutionAlias>
24+
<desktop:ExecutionAlias Alias="algokit.exe" />
25+
</uap3:AppExecutionAlias>
26+
</uap3:Extension>
27+
</Extensions>
28+
</Application>
29+
</Applications>
30+
<Capabilities>
31+
<rescap:Capability Name="runFullTrust" />
32+
</Capabilities>
33+
</Package>
Loading
893 Bytes
Loading
Loading
Loading
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<resources targetOsVersion="10.0.0" majorVersion="1">
3+
<packaging>
4+
<autoResourcePackage qualifier="Language"/>
5+
<autoResourcePackage qualifier="Scale"/>
6+
<autoResourcePackage qualifier="DXFeatureLevel"/>
7+
</packaging>
8+
<index root="\" startIndexAt="\">
9+
<default>
10+
<qualifier name="Language" value="en-US"/>
11+
<qualifier name="Contrast" value="standard"/>
12+
<qualifier name="Scale" value="100"/>
13+
<qualifier name="HomeRegion" value="001"/>
14+
<qualifier name="TargetSize" value="256"/>
15+
<qualifier name="LayoutDirection" value="LTR"/>
16+
<qualifier name="Theme" value="dark"/>
17+
<qualifier name="AlternateForm" value=""/>
18+
<qualifier name="DXFeatureLevel" value="DX9"/>
19+
<qualifier name="Configuration" value=""/>
20+
<qualifier name="DeviceFamily" value="Universal"/>
21+
<qualifier name="Custom" value=""/>
22+
</default>
23+
<indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="."/>
24+
<indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/>
25+
<indexer-config type="resjson" initialPath=""/>
26+
<indexer-config type="PRI"/>
27+
</index>
28+
<!--<index startIndexAt="Start Index Here" root="Root Here">-->
29+
<!-- <indexer-config type="resfiles" qualifierDelimiter="."/>-->
30+
<!-- <indexer-config type="priinfo" emitStrings="true" emitPaths="true" emitEmbeddedData="true"/>-->
31+
<!--</index>-->
32+
</resources>

scripts/winget/update-package.ps1

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Param(
2+
[Parameter(Mandatory=$true)]
3+
[String]
4+
$releaseVersionTag
5+
)
6+
7+
Function ThrowOnNonZeroExit {
8+
Param( [String]$Message )
9+
If ($LastExitCode -ne 0) {
10+
Throw $Message
11+
}
12+
}
13+
14+
$wingetPackage = 'AlgorandFoundation.AlgoKit'
15+
$release = Invoke-RestMethod -uri "https://api.github.com/repos/algorandfoundation/algokit-cli/releases/tags/$releaseVersionTag"
16+
$installerUrl = $release | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match '-windows_x64-winget\.msix$' | Select -ExpandProperty browser_download_url
17+
$releaseVersion = $releaseVersionTag.Trim("v")
18+
19+
$wingetDir = New-Item -Force -ItemType Directory -Path .\build\winget
20+
$wingetExecutable = "$wingetDir\wingetcreate.exe"
21+
Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile $wingetExecutable
22+
& $wingetExecutable update $wingetPackage -s -v $releaseVersion -u "$installerUrl" -t "$env:WINGET_GITHUB_TOKEN"
23+
ThrowOnNonZeroExit "Failed to update winget package"

0 commit comments

Comments
 (0)