From 0849fa3490edf8fe2add99f5fca5f843dc1b8ae5 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sun, 20 Sep 2026 12:47:05 +0200 Subject: [PATCH] THRIFT-2208: Package the Windows Thrift compiler for Chocolatey Client: build Open since 2013. Windows developers who use Chocolatey have no supported way to get the Thrift compiler through it. The package downloads the Windows installer published with the release and runs it silently for all users, rather than carrying a copy of the compiler. So what gets installed is what was voted on and signed, the package stays small, and no binary is redistributed through a third party CDN. The URL is archive.apache.org, which keeps every release; downloads.apache.org only carries the current one, so a package naming it would stop installing at the next release. 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 package depends on vcredist140. The compiler links the shared runtime and needs the Visual C++ redistributable, which the ASF cannot ship; Chocolatey installs it first, so a "choco install thrift" does not end in a compiler that will not start. That makes this a post release step, like the WinGet submission: it cannot run until the archive has the release. The workflow runs when the release is published and can be run again from the Actions tab. Nothing is pushed yet, and that is deliberate. The thrift id on the Chocolatey community repository belongs to a third party package that last shipped 0.12.0 in February 2019 - thirteen releases behind - maintained by chaliy and Lite. Publishing under it needs a maintainer takeover request under Chocolatey's process for abandoned packages. Until the id has been handed over and a CHOCO_API_KEY secret exists, the workflow builds and checks the package, attaches it to the run, and says in its summary that it did not push. ReleaseManagement.md describes the takeover, and names the free fallback ids should it be refused. test-chocolatey-package.ps1 checks that the URL, the checksum and the silent install arguments reach the install script, that no placeholder survives into a file that would be published verbatim, that LICENSE and NOTICE are staged and are not empty, and that a malformed version or checksum is refused. Given a packed .nupkg it also looks inside it, and checks it carries no executable - the compiler is downloaded at install time, and a package that had one would be shipping an unsigned copy. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/chocolatey.yml | 190 +++++++++++++++++ build/windows/README.md | 29 +++ build/windows/build-chocolatey-package.ps1 | 172 +++++++++++++++ .../chocolatey/test-chocolatey-package.ps1 | 195 ++++++++++++++++++ build/windows/chocolatey/thrift.nuspec.in | 73 +++++++ .../chocolatey/tools/chocolateyinstall.ps1.in | 40 ++++ doc/ReleaseManagement.md | 32 +++ 7 files changed, 731 insertions(+) create mode 100644 .github/workflows/chocolatey.yml create mode 100644 build/windows/build-chocolatey-package.ps1 create mode 100644 build/windows/chocolatey/test-chocolatey-package.ps1 create mode 100644 build/windows/chocolatey/thrift.nuspec.in create mode 100644 build/windows/chocolatey/tools/chocolateyinstall.ps1.in diff --git a/.github/workflows/chocolatey.yml b/.github/workflows/chocolatey.yml new file mode 100644 index 00000000000..6fd898995f9 --- /dev/null +++ b/.github/workflows/chocolatey.yml @@ -0,0 +1,190 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Packages the Windows Thrift compiler for Chocolatey, so that it can be +# installed with "choco install thrift". +# +# The package downloads the installer from archive.apache.org rather than +# carrying a copy, so it cannot be built until the release has been promoted +# and the archive has picked it up. If the release run is too early, run it +# again from the Actions tab once the archive has the file. +# +# Nothing is pushed until the CHOCO_API_KEY secret exists. The "thrift" id on +# the Chocolatey community repository currently belongs to an abandoned third +# party package, and a maintainer takeover has to be granted first. See +# doc/ReleaseManagement.md. + +name: "Chocolatey" + +on: + release: + types: [published] + pull_request: + branches: + - master + paths: + - ".github/workflows/chocolatey.yml" + - "build/windows/chocolatey/**" + - "build/windows/build-chocolatey-package.ps1" + - "doc/ReleaseManagement.md" + workflow_dispatch: + inputs: + version: + description: "Released version to package, for example 0.26.0. Taken from CMakeLists.txt when empty." + required: false + type: string + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + package: + name: Package + # choco pack needs Chocolatey, which is only on the Windows images. + runs-on: windows-2025 + timeout-minutes: 30 + permissions: + contents: read + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Test the package builder + shell: pwsh + run: ./build/windows/chocolatey/test-chocolatey-package.ps1 + + - name: Determine the version + id: version + shell: pwsh + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + INPUT_VERSION: ${{ inputs.version }} + run: | + $version = '' + if ($env:INPUT_VERSION) { + $version = $env:INPUT_VERSION + } + elseif ($env:RELEASE_TAG) { + $version = $env:RELEASE_TAG -replace '^v', '' + } + else { + foreach ($line in Get-Content CMakeLists.txt) { + if ($line -match '^\s*set\(thrift_VERSION\s+"(\d+\.\d+\.\d+)"\)') { + $version = $Matches[1] + break + } + } + } + if ($version -notmatch '^\d+\.\d+\.\d+$') { + Write-Error "Could not determine a major.minor.patch version (got '$version')." + exit 1 + } + + Write-Host "Version: $version" + "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Build the package + shell: pwsh + env: + VERSION: ${{ steps.version.outputs.version }} + IS_RELEASE: ${{ github.event_name == 'release' }} + run: | + if ($env:IS_RELEASE -eq '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 + } + else { + # Nothing is published for a pull request, so this packages an + # obviously fake checksum. It proves the templates render and that + # choco pack accepts the result; it is not pushable, and the job + # 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 + } + + - name: Check the packed package + shell: pwsh + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + ./build/windows/chocolatey/test-chocolatey-package.ps1 ` + -Package "chocolatey-package/thrift.$env:VERSION.nupkg" + + - name: Archive the package + if: ${{ github.event_name != 'pull_request' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: chocolatey-package + if-no-files-found: error + path: chocolatey-package/*.nupkg + retention-days: 14 + + push: + name: Push to Chocolatey + needs: package + if: ${{ github.event_name == 'release' && !github.event.release.prerelease }} + runs-on: windows-2025 + timeout-minutes: 20 + environment: release + permissions: + contents: read + steps: + - name: Download the package + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: chocolatey-package + path: chocolatey-package + + - name: Push + shell: pwsh + env: + CHOCO_API_KEY: ${{ secrets.CHOCO_API_KEY }} + VERSION: ${{ needs.package.outputs.version }} + run: | + $package = "chocolatey-package/thrift.$env:VERSION.nupkg" + + if (-not $env:CHOCO_API_KEY) { + # Expected until the "thrift" id has been handed over. The package + # is built and attached to the run either way. + Write-Host '::warning::CHOCO_API_KEY is not set, so nothing was pushed to Chocolatey.' + @( + '### Chocolatey push skipped', + '', + 'The `CHOCO_API_KEY` secret is not set in the `release` environment, so the', + 'package was built but not pushed. It is attached to this run as the', + '`chocolatey-package` artifact.', + '', + 'The `thrift` id on the Chocolatey community repository still belongs to an', + 'abandoned third party package; a maintainer takeover has to be granted before', + 'anything can be pushed under it. See `doc/ReleaseManagement.md`.' + ) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + exit 0 + } + + choco push $package --source https://push.chocolatey.org/ --api-key $env:CHOCO_API_KEY + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/build/windows/README.md b/build/windows/README.md index c6fb48983e2..f88b8d82f23 100644 --- a/build/windows/README.md +++ b/build/windows/README.md @@ -160,3 +160,32 @@ in there and are not empty - and then proves the installed command works. It runs anywhere PowerShell and the .NET SDK do. Off Windows it checks the launcher's refusal instead of the compiler's output, which is the behaviour that keeps a Linux user from a confusing failure. + +## `build-chocolatey-package.ps1` and `chocolatey/` + +Builds the [Chocolatey](https://chocolatey.org/) package, so that the compiler +can be installed with `choco install thrift`. + +```powershell +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. + +Pass `-StageOnly` to render the package without packing it, which works on a +machine that has no Chocolatey. + +### `chocolatey/test-chocolatey-package.ps1` + +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. +Given `-Package`, it also looks inside a packed `.nupkg` - including that it +carries no executable, since the compiler is downloaded at install time. + +Runs anywhere PowerShell does; only the `choco pack` step needs Windows. diff --git a/build/windows/build-chocolatey-package.ps1 b/build/windows/build-chocolatey-package.ps1 new file mode 100644 index 00000000000..e398096881b --- /dev/null +++ b/build/windows/build-chocolatey-package.ps1 @@ -0,0 +1,172 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +<# +.SYNOPSIS + Builds the Chocolatey package for the Windows Thrift compiler. + +.DESCRIPTION + Renders the templates in build/windows/chocolatey and runs "choco pack". + + 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. + +.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. + +.PARAMETER Sha256 + The installer's SHA-256. Computed from the downloaded file when omitted. + +.PARAMETER OutputDir + Where the staged package and the .nupkg are written. + +.PARAMETER SourceRoot + Root of the Thrift source tree, used for LICENSE and NOTICE. Defaults to + the checkout this script lives in. + +.PARAMETER StageOnly + Render the package without packing it, so that the generated files can be + reviewed on a machine that has no Chocolatey. + +.EXAMPLE + pwsh build/windows/build-chocolatey-package.ps1 -Version 0.26.0 +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string] $Version, + [string] $InstallerUrl = '', + [string] $Sha256 = '', + [string] $OutputDir = 'chocolatey-package', + [string] $SourceRoot = '', + [switch] $StageOnly +) + +$ErrorActionPreference = 'Stop' + +if ($Version -notmatch '^\d+\.\d+\.\d+$') { + throw "Version '$Version' is not major.minor.patch." +} +if (-not $InstallerUrl) { + $InstallerUrl = "https://archive.apache.org/dist/thrift/$Version/thrift-$Version-setup.exe" +} +if (-not $SourceRoot) { + $SourceRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path +} +foreach ($required in @('LICENSE', 'NOTICE')) { + if (-not (Test-Path -LiteralPath (Join-Path $SourceRoot $required))) { + throw "$required not found below -SourceRoot $SourceRoot" + } +} + +if ($Sha256) { + if ($Sha256 -notmatch '^[0-9a-fA-F]{64}$') { + throw "Sha256 '$Sha256' is not 64 hexadecimal digits." + } + $Sha256 = $Sha256.ToUpperInvariant() +} +else { + Write-Host "Downloading $InstallerUrl" + $download = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString('N') + '.exe') + try { + try { + 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.') + } + $Sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $download).Hash.ToUpperInvariant() + Write-Host " sha256: $Sha256" + } + finally { + Remove-Item -LiteralPath $download -Force -ErrorAction SilentlyContinue + } +} + +$templateDir = Join-Path $PSScriptRoot 'chocolatey' +$stageDir = Join-Path $OutputDir 'staging' +if (Test-Path -LiteralPath $stageDir) { + Remove-Item -LiteralPath $stageDir -Recurse -Force +} +New-Item -Path (Join-Path $stageDir 'tools') -ItemType Directory -Force | Out-Null + +$substitutions = @{ + '__VERSION__' = $Version + '__INSTALLER_URL__' = $InstallerUrl + '__INSTALLER_SHA256__' = $Sha256 +} + +foreach ($template in (Get-ChildItem -Path $templateDir -Filter '*.in' -Recurse)) { + $relative = $template.FullName.Substring($templateDir.Length).TrimStart('\', '/') + $target = Join-Path $stageDir ($relative -replace '\.in$', '') + + $content = Get-Content -LiteralPath $template.FullName -Raw + foreach ($key in $substitutions.Keys) { + $content = $content.Replace($key, $substitutions[$key]) + } + + # A placeholder that survived would be published verbatim. + $leftovers = [regex]::Matches($content, '__[A-Z0-9_]+__') + if ($leftovers.Count -gt 0) { + throw ("$relative still holds placeholders after substitution: " + + (($leftovers | ForEach-Object { $_.Value } | Sort-Object -Unique) -join ', ')) + } + + New-Item -Path (Split-Path -Parent $target) -ItemType Directory -Force | Out-Null + [System.IO.File]::WriteAllText($target, $content, [System.Text.UTF8Encoding]::new($false)) + Write-Host "Rendered $target" +} + +# Chocolatey shows LICENSE.txt from the tools directory, and the ASF wants the +# notice to travel with the package either way. +Copy-Item -LiteralPath (Join-Path $SourceRoot 'LICENSE') -Destination (Join-Path $stageDir 'tools\LICENSE.txt') -Force +Copy-Item -LiteralPath (Join-Path $SourceRoot 'NOTICE') -Destination (Join-Path $stageDir 'tools\NOTICE.txt') -Force + +if ($StageOnly) { + Write-Host "Staged in $stageDir" + Write-Output $stageDir + return +} + +if (-not (Get-Command 'choco' -ErrorAction SilentlyContinue)) { + throw 'Chocolatey was not found. Install it from https://chocolatey.org/install, or pass -StageOnly.' +} + +$outputPath = (Resolve-Path -LiteralPath $OutputDir).Path +& choco pack (Join-Path $stageDir 'thrift.nuspec') --output-directory $outputPath +if ($LASTEXITCODE -ne 0) { + throw "choco pack failed with exit code $LASTEXITCODE" +} + +$package = Join-Path $outputPath "thrift.$Version.nupkg" +if (-not (Test-Path -LiteralPath $package)) { + throw "choco pack reported success but $package is not there." +} + +Write-Host "Built $package" +Write-Output $package diff --git a/build/windows/chocolatey/test-chocolatey-package.ps1 b/build/windows/chocolatey/test-chocolatey-package.ps1 new file mode 100644 index 00000000000..98dee1e1c96 --- /dev/null +++ b/build/windows/chocolatey/test-chocolatey-package.ps1 @@ -0,0 +1,195 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +<# +.SYNOPSIS + Tests build/windows/build-chocolatey-package.ps1. + +.DESCRIPTION + Renders the package with known inputs and checks what came out: whether + the download URL and checksum reached the install script, whether the + licence files are there, and what the script refuses to build. + + Runs anywhere PowerShell does and needs neither Chocolatey nor the + network: the checksum is passed in rather than computed from a download. + +.PARAMETER Package + An already packed .nupkg to inspect as well. Optional; the packing itself + needs Chocolatey, so it only happens in CI. + +.EXAMPLE + pwsh build/windows/chocolatey/test-chocolatey-package.ps1 +#> + +[CmdletBinding()] +param( + [string] $Package = '' +) + +$ErrorActionPreference = 'Stop' + +$builder = Join-Path $PSScriptRoot '..\build-chocolatey-package.ps1' +if (-not (Test-Path -LiteralPath $builder)) { + throw "build-chocolatey-package.ps1 not found next to $PSCommandPath" +} + +$workDir = Join-Path ([System.IO.Path]::GetTempPath()) ("thrift-choco-tests-" + [System.Guid]::NewGuid().ToString('N')) + +$failures = [System.Collections.Generic.List[string]]::new() +$passed = 0 + +function Assert-True { + param([string] $Name, [bool] $Condition, [string] $Detail = '') + + if ($Condition) { + Write-Host " PASS $Name" + $script:passed++ + } + else { + Write-Host " FAIL $Name$(if ($Detail) { " - $Detail" })" + $script:failures.Add($Name) + } +} + +function Assert-Throws { + param([string] $Name, [scriptblock] $Action, [string] $Matching = '') + + try { + & $Action | Out-Null + Assert-True $Name $false 'it was accepted' + } + catch { + if ($Matching -and ($_.Exception.Message -notmatch $Matching)) { + Assert-True $Name $false "wrong message: $($_.Exception.Message)" + } + else { + Assert-True $Name $true + } + } +} + +$version = '1.2.3' +$sha = ('D' * 64) + +Write-Host 'build-chocolatey-package.ps1' + +try { + $stageDir = & $builder -Version $version -Sha256 $sha -OutputDir (Join-Path $workDir 'out') -StageOnly | + Select-Object -Last 1 + + $nuspecPath = Join-Path $stageDir 'thrift.nuspec' + $installPath = Join-Path $stageDir 'tools\chocolateyinstall.ps1' + + Assert-True 'a nuspec is rendered' (Test-Path -LiteralPath $nuspecPath) + Assert-True 'an install script is rendered' (Test-Path -LiteralPath $installPath) + Assert-True 'no template is left in the staged package' ` + (@(Get-ChildItem -Path $stageDir -Filter '*.in' -Recurse).Count -eq 0) + + # The nuspec has to parse, or choco pack fails at release time. + $nuspec = [xml](Get-Content -LiteralPath $nuspecPath -Raw) + Assert-True 'the package id is thrift' ($nuspec.package.metadata.id -eq 'thrift') ` + $nuspec.package.metadata.id + Assert-True 'the nuspec carries the version' ($nuspec.package.metadata.version -eq $version) ` + $nuspec.package.metadata.version + Assert-True 'the nuspec names the licence' ` + ($nuspec.package.metadata.licenseUrl -match 'apache\.org/licenses/LICENSE-2\.0') + + # The compiler needs the Visual C++ redistributable; without this a + # "choco install thrift" would succeed and then not run. + $dependencies = @($nuspec.package.metadata.dependencies.dependency) + Assert-True 'the redistributable is declared as a dependency' ` + (@($dependencies | Where-Object { $_.id -eq 'vcredist140' }).Count -eq 1) ` + (($dependencies | ForEach-Object { $_.id }) -join ', ') + + $install = Get-Content -LiteralPath $installPath -Raw + Assert-True 'the install script holds no placeholders' ($install -notmatch '__[A-Z0-9_]+__') + Assert-True 'the checksum reaches the install script' ($install -match "checksum64\s*=\s*'$sha'") + Assert-True 'the checksum type is sha256' ($install -match "checksumType64\s*=\s*'sha256'") + + # downloads.apache.org only carries the current release, so a package + # naming it stops installing at the next one. + Assert-True 'the download URL defaults to the Apache archive' ` + ($install -match "url64bit\s*=\s*'https://archive\.apache\.org/dist/thrift/$version/thrift-$version-setup\.exe'") $install + # Look at the value, not the whole file: the script explains in a comment + # why downloads.apache.org is the wrong host. + $urlValue = ([regex]::Match($install, "url64bit\s*=\s*'([^']*)'")).Groups[1].Value + Assert-True 'the download URL is not downloads.apache.org' ` + ($urlValue -notmatch 'downloads\.apache\.org') $urlValue + + # Chocolatey installs machine wide, and the installer only touches the + # machine PATH when both of these are passed. + Assert-True 'the installer is run for all users' ($install -match '/ALLUSERS') + Assert-True 'the installer is asked to modify PATH' ($install -match 'modifypath') + Assert-True 'the installer is run silently' ($install -match '/VERYSILENT') + + foreach ($legal in @('tools\LICENSE.txt', 'tools\NOTICE.txt')) { + $path = Join-Path $stageDir $legal + Assert-True "$legal is staged and not empty" ` + ((Test-Path -LiteralPath $path) -and ((Get-Item -LiteralPath $path).Length -gt 0)) + } + + $custom = & $builder -Version $version -Sha256 $sha -InstallerUrl 'https://example.invalid/thrift.exe' ` + -OutputDir (Join-Path $workDir 'custom') -StageOnly | Select-Object -Last 1 + $customInstall = Get-Content -LiteralPath (Join-Path $custom 'tools\chocolateyinstall.ps1') -Raw + Assert-True 'a given download URL is used' ` + ($customInstall -match "url64bit\s*=\s*'https://example\.invalid/thrift\.exe'") + + Assert-Throws 'a version that is not major.minor.patch is refused' ` + { & $builder -Version '1.2' -Sha256 $sha -OutputDir (Join-Path $workDir 'bad1') -StageOnly } 'major.minor.patch' + + Assert-Throws 'a checksum that is not 64 hex digits is refused' ` + { & $builder -Version $version -Sha256 'nope' -OutputDir (Join-Path $workDir 'bad2') -StageOnly } 'hexadecimal' + + # ---- an already packed package, when one was given --------------------- + + if ($Package) { + Assert-True 'the packed package exists' (Test-Path -LiteralPath $Package) $Package + if (Test-Path -LiteralPath $Package) { + Add-Type -AssemblyName System.IO.Compression.FileSystem + $archive = [System.IO.Compression.ZipFile]::OpenRead((Resolve-Path -LiteralPath $Package).Path) + try { + $entries = @($archive.Entries | ForEach-Object { $_.FullName }) + foreach ($required in @('thrift.nuspec', 'tools/chocolateyinstall.ps1', + 'tools/LICENSE.txt', 'tools/NOTICE.txt')) { + Assert-True "the packed package contains $required" ($entries -contains $required) ` + ($entries -join ', ') + } + # The compiler is downloaded at install time, not carried here. + Assert-True 'the packed package carries no executable' ` + (@($entries | Where-Object { $_ -like '*.exe' }).Count -eq 0) ($entries -join ', ') + } + finally { + $archive.Dispose() + } + } + } +} +finally { + Remove-Item -LiteralPath $workDir -Recurse -Force -ErrorAction SilentlyContinue +} + +Write-Host '' +if ($failures.Count -gt 0) { + Write-Host "$($failures.Count) of $($failures.Count + $passed) checks failed:" + foreach ($failure in $failures) { Write-Host " $failure" } + exit 1 +} + +Write-Host "$passed checks passed." +exit 0 diff --git a/build/windows/chocolatey/thrift.nuspec.in b/build/windows/chocolatey/thrift.nuspec.in new file mode 100644 index 00000000000..41e99506ebc --- /dev/null +++ b/build/windows/chocolatey/thrift.nuspec.in @@ -0,0 +1,73 @@ + + + + + thrift + __VERSION__ + Apache Thrift Compiler + Apache Thrift Developers + Apache Software Foundation + https://thrift.apache.org/ + https://github.com/apache/thrift + https://github.com/apache/thrift/tree/master/build/windows/chocolatey + https://thrift.apache.org/docs/ + https://issues.apache.org/jira/browse/THRIFT + https://thrift.apache.org/mailing + https://www.apache.org/licenses/LICENSE-2.0 + false + https://thrift.apache.org/assets/images/thrift-logo.svg + Copyright (c) The Apache Software Foundation + apache thrift rpc idl codegen compiler serialization admin + The Apache Thrift IDL compiler + + https://github.com/apache/thrift/blob/master/CHANGES.md + + + + + + + + + diff --git a/build/windows/chocolatey/tools/chocolateyinstall.ps1.in b/build/windows/chocolatey/tools/chocolateyinstall.ps1.in new file mode 100644 index 00000000000..c324ff4bad7 --- /dev/null +++ b/build/windows/chocolatey/tools/chocolateyinstall.ps1.in @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Rendered by build/windows/build-chocolatey-package.ps1. + +$ErrorActionPreference = 'Stop' + +$packageArgs = @{ + packageName = 'thrift' + fileType = 'exe' + + # The installer published with the Apache Thrift release. The archive keeps + # every release; downloads.apache.org only carries the current one, so a + # package naming it would stop installing at the next release. + url64bit = '__INSTALLER_URL__' + checksum64 = '__INSTALLER_SHA256__' + checksumType64 = 'sha256' + + # Inno Setup. /ALLUSERS because Chocolatey installs machine wide, and the + # modifypath task is what puts thrift.exe on PATH. + silentArgs = '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /ALLUSERS /TASKS="modifypath"' + validExitCodes = @(0) +} + +Install-ChocolateyPackage @packageArgs diff --git a/doc/ReleaseManagement.md b/doc/ReleaseManagement.md index d5367b51965..0e12b9a4dfc 100644 --- a/doc/ReleaseManagement.md +++ b/doc/ReleaseManagement.md @@ -476,6 +476,38 @@ GitHub is what the signatures on `dist.apache.org` cover. ~$ gh release upload v1.0.0 thrift-1.0.0-setup.exe --clobber --repo apache/thrift ``` +##### Chocolatey + +The [`Chocolatey`](../.github/workflows/chocolatey.yml) workflow builds the +[Chocolatey](https://chocolatey.org/) package, so that the compiler can be +installed with `choco install thrift`. + +The package does not carry the compiler. It downloads the installer from +`archive.apache.org` and records its checksum, so it can only be built once the +archive has the release - `downloads.apache.org` only carries the current +release, and a package naming it would stop installing at the next one. If the +release run was too early, run it again from the Actions tab once the archive +has the file. + +**Nothing is pushed yet.** The `thrift` id on the Chocolatey community +repository belongs to a third-party package that last shipped 0.12.0 in February +2019, maintained by `chaliy` and `Lite` from +. Before Apache Thrift can publish +under that id, a maintainer takeover has to be requested from Chocolatey under +their process for abandoned packages: + +1. Contact the current maintainers through their Chocolatey profile page and + allow the response time Chocolatey's policy requires. +1. If there is no response, open a maintainer takeover request with Chocolatey. +1. Once the id has been handed over, add the API key of the account that owns it + as a secret named `CHOCO_API_KEY` in the `release` environment. + +Until then the workflow builds and checks the package, attaches it to the run as +an artifact, and says in its summary that it did not push. If the takeover is +refused, the free ids `apache-thrift` and `thrift-compiler` are the fallback; +changing the id means editing `build/windows/chocolatey/thrift.nuspec.in` and +the package name the build script expects. + #### Third Party Package Managers See https://thrift.apache.org/lib/ for the current status of each external package manager's distribution. The information below is from the 0.12.0 release: