Skip to content
Merged
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
25 changes: 22 additions & 3 deletions .github/workflows/chocolatey.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
# once the archive has the file, or start this workflow from the Actions tab
# with the version.
#
# An installer the release vote did not cover is not on dist.apache.org, and
# so not in the archive either. For such a release, start this workflow from
# the Actions tab with installer_source set to github: the package then
# downloads the copy on the GitHub release. See doc/ReleaseManagement.md.
#
# Nothing is pushed unless the CHOCO_API_KEY secret is set, to the API key of
# an account that maintains the "thrift" package on the Chocolatey community
# repository. See doc/ReleaseManagement.md.
Expand All @@ -48,6 +53,14 @@ on:
description: "Released version to package, for example 0.26.0. Taken from CMakeLists.txt when empty."
required: false
type: string
installer_source:
description: "Where the package downloads the installer from: archive (archive.apache.org) when the release vote covered the installer, github (the copy on the GitHub release) when it did not."
required: false
type: choice
options:
- archive
- github
default: archive

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -109,15 +122,21 @@ jobs:
shell: pwsh
env:
VERSION: ${{ steps.version.outputs.version }}
INSTALLER_SOURCE: ${{ inputs.installer_source }}
# A release and a manual run both push, so only a pull request
# packages a placeholder.
IS_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
run: |
# Only a manual run has inputs. A release run uses the archive.
$source = if ($env:INSTALLER_SOURCE) { $env:INSTALLER_SOURCE } else { 'archive' }
Write-Host "Installer source: $source"

if ($env:IS_PULL_REQUEST -ne 'true') {
# No checksum given, so the published installer is downloaded and
# hashed. The package then cannot record a checksum the published
# file does not have.
./build/windows/build-chocolatey-package.ps1 -Version $env:VERSION -OutputDir chocolatey-package
./build/windows/build-chocolatey-package.ps1 -Version $env:VERSION `
-InstallerSource $source -OutputDir chocolatey-package
}
else {
# Nothing is published for a pull request, so this packages an
Expand All @@ -126,7 +145,7 @@ jobs:
# does not upload it.
Write-Host '::notice::Packaging with a placeholder checksum; this run cannot push.'
./build/windows/build-chocolatey-package.ps1 -Version $env:VERSION `
-Sha256 ('DEADBEEF' * 8) -OutputDir chocolatey-package
-Sha256 ('DEADBEEF' * 8) -InstallerSource $source -OutputDir chocolatey-package
}

- name: Check the packed package
Expand All @@ -150,7 +169,7 @@ jobs:
name: Push to Chocolatey
needs: package
# A manual run is how a release gets pushed once the archive has the
# installer, so it pushes too.
# installer, or from the GitHub release, so it pushes too.
if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'release' && !github.event.release.prerelease) }}
runs-on: windows-2025
timeout-minutes: 20
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/windows-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
# The installer that counts is the one on dist.apache.org, signed by a release
# manager. What this workflow attaches to the GitHub release is an unsigned
# convenience copy, so that the link is there straight away; the release
# manager overwrites it with the signed file afterwards. See
# manager overwrites it with the signed file afterwards. When the vote did not
# cover the installer, there is no signed file, and WinGet and Chocolatey
# download this copy instead, so it must then stay as it is. See
# doc/ReleaseManagement.md.

name: "Windows packages"
Expand Down Expand Up @@ -343,4 +345,6 @@ jobs:
# This is the unsigned convenience copy, around the voted compiler.
# The release manager replaces it with the signed file from
# dist.apache.org after the vote, which is also a --clobber upload.
# When the vote did not cover the installer, WinGet and Chocolatey
# download this copy, and it is not replaced.
gh release upload "$RELEASE_TAG" "$installer" --clobber
25 changes: 22 additions & 3 deletions .github/workflows/winget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
# run until the release has been promoted and the archive has picked it up. If
# the release run is too early, re-run it once the archive has the file, or
# start this workflow from the Actions tab with the version and release date.
#
# An installer the release vote did not cover is not on dist.apache.org, and
# so not in the archive either. For such a release, start this workflow from
# the Actions tab with installer_source set to github: the manifest then points
# at the copy on the GitHub release. See doc/ReleaseManagement.md.

name: "WinGet"

Expand All @@ -48,6 +53,14 @@ on:
description: "Release date as yyyy-MM-dd. Today when empty, which is only right on the day of the release."
required: false
type: string
installer_source:
description: "Where the manifest points for the installer: archive (archive.apache.org) when the release vote covered the installer, github (the copy on the GitHub release) when it did not."
required: false
type: choice
options:
- archive
- github
default: archive

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -133,16 +146,21 @@ jobs:
env:
VERSION: ${{ steps.version.outputs.version }}
RELEASE_DATE: ${{ steps.version.outputs.release_date }}
INSTALLER_SOURCE: ${{ inputs.installer_source }}
# A release and a manual run both submit, so only a pull request
# renders with a placeholder.
IS_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
run: |
# Only a manual run has inputs. A release run uses the archive.
$source = if ($env:INSTALLER_SOURCE) { $env:INSTALLER_SOURCE } else { 'archive' }
Write-Host "Installer source: $source"

if ($env:IS_PULL_REQUEST -ne 'true') {
# No checksum given, so the renderer downloads the published
# installer and hashes that. The manifest then cannot claim a
# checksum the published file does not have.
./build/windows/build-winget-manifests.ps1 -Version $env:VERSION `
-ReleaseDate $env:RELEASE_DATE -OutputDir winget-manifests
-ReleaseDate $env:RELEASE_DATE -InstallerSource $source -OutputDir winget-manifests
}
else {
# Nothing is published for a pull request, so the manifests are
Expand All @@ -151,7 +169,8 @@ jobs:
# and the job does not upload them.
Write-Host '::notice::Rendering with a placeholder checksum; this run cannot submit.'
./build/windows/build-winget-manifests.ps1 -Version $env:VERSION `
-ReleaseDate $env:RELEASE_DATE -Sha256 ('DEADBEEF' * 8) -OutputDir winget-manifests
-ReleaseDate $env:RELEASE_DATE -Sha256 ('DEADBEEF' * 8) -InstallerSource $source `
-OutputDir winget-manifests
}

- name: Set up Python
Expand Down Expand Up @@ -195,7 +214,7 @@ jobs:
name: Submit to winget-pkgs
needs: manifests
# A manual run is how a release gets submitted once the archive has the
# installer, so it submits too.
# installer, or from the GitHub release, so it submits too.
if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'release' && !github.event.release.prerelease) }}
# wingetcreate only runs on Windows.
runs-on: windows-2025
Expand Down
21 changes: 14 additions & 7 deletions build/windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,12 @@ PS C:\thrift> .\build\windows\build-chocolatey-package.ps1 -Version 0.26.0

The package does not carry the compiler. It downloads the installer published
with the release and runs it silently for all users, so what a user installs is
what was voted on and signed, and no binary is redistributed through a third
party CDN. As with the WinGet manifest, the URL is `archive.apache.org` and the
checksum is computed from the file at that very URL, so the package cannot
record a checksum the published file does not have.
the voted compiler, and no binary is redistributed through a third party CDN.
As with the WinGet manifest, the URL is `archive.apache.org` and the checksum is
computed from the file at that very URL, so the package cannot record a
checksum the published file does not have. For a release whose vote did not
cover the installer, `-InstallerSource github` takes the copy on the GitHub
release instead.

Pass `-StageOnly` to render the package without packing it, which works on a
machine that has no Chocolatey.
Expand All @@ -224,7 +226,8 @@ machine that has no Chocolatey.

Tests the builder: that the URL, checksum and silent install arguments reach
the install script, that no placeholder survives, that `LICENSE` and `NOTICE`
are staged and not empty, and that a malformed version or checksum is refused.
are staged and not empty, and that a malformed version or checksum, or an
unknown installer source, is refused.
Given `-Package`, it also looks inside a packed `.nupkg` - including that it
carries no executable, since the compiler is downloaded at install time.

Expand All @@ -246,17 +249,21 @@ The manifest points at `archive.apache.org`, which keeps every release.
would stop working at the next release and take every older version in
winget-pkgs with it.

An installer the release vote did not cover is not on `dist.apache.org`, and so
not in the archive either. For such a release, `-InstallerSource github` points
the manifest at the copy on the GitHub release, whose URL is just as permanent.

Unless `-Sha256` is given, the installer is downloaded from the very URL that
goes into the manifest and hashed, so the manifest cannot claim a checksum the
published file does not have. That also means this cannot be run before the
release has reached the archive.
installer is there: for the archive, before the release has reached it.

### `winget/test-winget-manifests.ps1`

Tests the renderer: that the values land where they belong, that no placeholder
survives into a file that would be submitted verbatim, that the output is UTF-8
without a BOM and with LF endings, and that a malformed version, checksum or
date is refused.
date, or an unknown installer source, is refused.

### `winget/validate_manifests.py`

Expand Down
44 changes: 36 additions & 8 deletions build/windows/build-chocolatey-package.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,28 @@

The package does not carry the compiler. It downloads the installer
published with the release and runs it, so that what a user installs is
what was voted on and signed. Unless a checksum is given, it is computed
from the file at that very URL, which means this cannot run before the
release has reached the archive.
the voted compiler. Unless a checksum is given, it is computed from the
file at that very URL, which means this cannot run before the installer is
there: for the archive, until the release has reached it.

.PARAMETER Version
The released version, for example 0.26.0.

.PARAMETER InstallerUrl
Where the installer is published. Defaults to the Apache archive, which
keeps every release; downloads.apache.org only carries the current one.
Where the installer is published. Defaults to the URL -InstallerSource
names.

.PARAMETER InstallerSource
Where the installer is taken from when -InstallerUrl is not given:

- archive, the default: the Apache archive, which keeps every release. The
installer gets there through dist/release when the release vote
covered it.
- github: the convenience copy on the GitHub release, for a release whose
vote did not cover the installer, so that it is not on dist.apache.org.

downloads.apache.org is not offered: it only carries the current release,
so a package naming it stops installing at the next one.

.PARAMETER Sha256
The installer's SHA-256. Computed from the downloaded file when omitted.
Expand All @@ -53,13 +65,18 @@

.EXAMPLE
pwsh build/windows/build-chocolatey-package.ps1 -Version 0.26.0

.EXAMPLE
pwsh build/windows/build-chocolatey-package.ps1 -Version 0.25.0 -InstallerSource github
#>

[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string] $Version,
[string] $InstallerUrl = '',
[ValidateSet('archive', 'github')]
[string] $InstallerSource = 'archive',
[string] $Sha256 = '',
[string] $OutputDir = 'chocolatey-package',
[string] $SourceRoot = '',
Expand All @@ -71,8 +88,19 @@ $ErrorActionPreference = 'Stop'
if ($Version -notmatch '^\d+\.\d+\.\d+$') {
throw "Version '$Version' is not major.minor.patch."
}
if ($InstallerUrl -and $PSBoundParameters.ContainsKey('InstallerSource')) {
throw 'Pass either -InstallerUrl or -InstallerSource, not both.'
}
$downloadHint = ''
if (-not $InstallerUrl) {
$InstallerUrl = "https://archive.apache.org/dist/thrift/$Version/thrift-$Version-setup.exe"
if ($InstallerSource -eq 'github') {
$InstallerUrl = "https://github.com/apache/thrift/releases/download/v$Version/thrift-$Version-setup.exe"
$downloadHint = "The Windows packages workflow attaches the installer to the GitHub release v$Version."
}
else {
$InstallerUrl = "https://archive.apache.org/dist/thrift/$Version/thrift-$Version-setup.exe"
$downloadHint = 'If the release was just promoted, the Apache archive may not have picked it up yet. Wait and try again.'
}
}
if (-not $SourceRoot) {
$SourceRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
Expand All @@ -97,8 +125,8 @@ else {
Invoke-WebRequest -Uri $InstallerUrl -OutFile $download -MaximumRedirection 5
}
catch {
throw ("Could not download $InstallerUrl : " + $_.Exception.Message + "`n" +
'If the release was just promoted, the Apache archive may not have picked it up yet. Wait and try again.')
throw ("Could not download $InstallerUrl : " + $_.Exception.Message +
$(if ($downloadHint) { "`n$downloadHint" }))
}
$Sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $download).Hash.ToUpperInvariant()
Write-Host " sha256: $Sha256"
Expand Down
42 changes: 35 additions & 7 deletions build/windows/build-winget-manifests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,27 @@
Unless a checksum is given, the installer is downloaded from the very URL
that goes into the manifest and hashed, so that the manifest cannot claim a
checksum the published file does not have. That also means this cannot run
before the release has reached the archive.
before the installer is there: for the archive, until the release has
reached it.

.PARAMETER Version
The released version, for example 0.26.0.

.PARAMETER InstallerUrl
Where the installer is published. Defaults to the Apache archive, which
keeps every release; downloads.apache.org only carries the current one, so
a manifest naming it stops working at the next release.
Where the installer is published. Defaults to the URL -InstallerSource
names.

.PARAMETER InstallerSource
Where the installer is taken from when -InstallerUrl is not given:

- archive, the default: the Apache archive, which keeps every release. The
installer gets there through dist/release when the release vote
covered it.
- github: the convenience copy on the GitHub release, for a release whose
vote did not cover the installer, so that it is not on dist.apache.org.

downloads.apache.org is not offered: it only carries the current release,
so a manifest naming it stops working at the next one.

.PARAMETER Sha256
The installer's SHA-256. Computed from the downloaded file when omitted.
Expand All @@ -51,13 +63,18 @@

.EXAMPLE
pwsh build/windows/build-winget-manifests.ps1 -Version 0.26.0

.EXAMPLE
pwsh build/windows/build-winget-manifests.ps1 -Version 0.25.0 -InstallerSource github -ReleaseDate 2026-09-30
#>

[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string] $Version,
[string] $InstallerUrl = '',
[ValidateSet('archive', 'github')]
[string] $InstallerSource = 'archive',
[string] $Sha256 = '',
[string] $ReleaseDate = '',
[string] $OutputDir = 'winget-manifests'
Expand All @@ -70,8 +87,19 @@ $PackageIdentifier = 'Apache.Thrift'
if ($Version -notmatch '^\d+\.\d+\.\d+$') {
throw "Version '$Version' is not major.minor.patch."
}
if ($InstallerUrl -and $PSBoundParameters.ContainsKey('InstallerSource')) {
throw 'Pass either -InstallerUrl or -InstallerSource, not both.'
}
$downloadHint = ''
if (-not $InstallerUrl) {
$InstallerUrl = "https://archive.apache.org/dist/thrift/$Version/thrift-$Version-setup.exe"
if ($InstallerSource -eq 'github') {
$InstallerUrl = "https://github.com/apache/thrift/releases/download/v$Version/thrift-$Version-setup.exe"
$downloadHint = "The Windows packages workflow attaches the installer to the GitHub release v$Version."
}
else {
$InstallerUrl = "https://archive.apache.org/dist/thrift/$Version/thrift-$Version-setup.exe"
$downloadHint = 'If the release was just promoted, the Apache archive may not have picked it up yet. Wait and try again.'
}
}
if (-not $ReleaseDate) {
$ReleaseDate = (Get-Date).ToString('yyyy-MM-dd')
Expand All @@ -94,8 +122,8 @@ else {
Invoke-WebRequest -Uri $InstallerUrl -OutFile $download -MaximumRedirection 5
}
catch {
throw ("Could not download $InstallerUrl : " + $_.Exception.Message + "`n" +
'If the release was just promoted, the Apache archive may not have picked it up yet. Wait and try again.')
throw ("Could not download $InstallerUrl : " + $_.Exception.Message +
$(if ($downloadHint) { "`n$downloadHint" }))
}
$Sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $download).Hash.ToUpperInvariant()
Write-Host " sha256: $Sha256"
Expand Down
Loading
Loading