mirror of
https://github.com/apache/thrift.git
synced 2026-10-28 16:35:10 +08:00
Client: build The .NET tool workflow built thrift.exe from the release tag and packed that build. A new build of the same source is not the same bytes, so Apache.Thrift.Compiler did not carry the thrift-<version>.exe the release vote covered: the compiler in the 0.25.0 package has SHA-256 3d3f2e8a..., the voted one 9e1cb466.... THRIFT-6397 made the Windows installer package the voted executable, and the .NET tool now does the same. When the GitHub release is published, a new voted-compiler job runs the tests of get-voted-compiler.ps1 and then uses it to fetch thrift-<version>.exe from dist/release, checking its checksums and its signature against KEYS. The package carries that file and the LICENSE and NOTICE of the release tag. If the fetch or a check fails, nothing is packed or published: a release does not fall back to building its own compiler. A manual run with the new compiler_url input packs the executable at that URL, for example a release candidate's, and never publishes. Pull requests, and manual runs without compiler_url, still build the compiler from source. test-dotnet-tool.ps1 gets -ExpectedSha256, which the workflow uses to check that the thrift.exe in the package is the file it packed. The run summary names the compiler's source and hash. The README shown on nuget.org, build/windows/README.md and doc/ReleaseManagement.md describe it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
357 lines
14 KiB
YAML
357 lines
14 KiB
YAML
#
|
|
# 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.
|
|
#
|
|
# Publishes the Windows Thrift compiler to NuGet as a .NET tool, so that it can
|
|
# be installed with "dotnet tool install --global Apache.Thrift.Compiler".
|
|
#
|
|
# The package of a release has to carry the thrift.exe the vote covered, not a
|
|
# new build of the same source: two builds are not the same bytes. So where
|
|
# there is such an executable, this workflow packs that one, as
|
|
# windows-packages.yml does for the installer:
|
|
#
|
|
# - for a GitHub release, the thrift-<version>.exe in the release's
|
|
# directory on dist.apache.org;
|
|
# - for a manual run with compiler_url, the executable at that URL, such as
|
|
# the one a release candidate put on dist/dev. A manual run never
|
|
# publishes; it shows that the package works on a Windows runner.
|
|
#
|
|
# It is used only once its checksum and its signature against KEYS check out.
|
|
# Pull requests, and manual runs without compiler_url, build the compiler from
|
|
# source instead. That build mirrors the compiler-windows job in cmake.yml,
|
|
# which is what actually tests it on every push.
|
|
|
|
name: ".NET tool"
|
|
|
|
on:
|
|
release:
|
|
types: [published]
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
paths:
|
|
- ".github/workflows/dotnet-tool.yml"
|
|
- "build/windows/dotnet-tool/**"
|
|
- "build/windows/build-dotnet-tool.ps1"
|
|
- "build/windows/check-compiler-imports.ps1"
|
|
- "doc/ReleaseManagement.md"
|
|
workflow_dispatch:
|
|
inputs:
|
|
compiler_url:
|
|
description: "A signed thrift-X.Y.Z.exe on dist.apache.org to pack, for example https://dist.apache.org/repos/dist/release/thrift/0.25.0/thrift-0.25.0.exe. Empty builds the compiler from the branch instead. A manual run never publishes."
|
|
required: false
|
|
type: string
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
voted-compiler:
|
|
name: Voted compiler
|
|
if: ${{ github.event_name == 'release' || inputs.compiler_url != '' }}
|
|
# gpg is at hand on Linux.
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
permissions:
|
|
contents: read
|
|
outputs:
|
|
version: ${{ steps.fetch.outputs.version }}
|
|
url: ${{ steps.fetch.outputs.url }}
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# windows-packages.yml runs these on every pull request that touches
|
|
# them. Here they run before the download they guard, so that a release
|
|
# does not rely on checks that no longer work.
|
|
- name: Test the download checks
|
|
shell: pwsh
|
|
run: ./build/windows/get-voted-compiler-tests.ps1
|
|
|
|
- name: Download and check the compiler
|
|
id: fetch
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
INPUT_URL: ${{ inputs.compiler_url }}
|
|
run: |
|
|
$url = $env:INPUT_URL
|
|
if (-not $url) {
|
|
# A release is promoted to dist/release before its GitHub release
|
|
# is published, so the executable is already there.
|
|
$version = $env:RELEASE_TAG -replace '^v', ''
|
|
$url = "https://dist.apache.org/repos/dist/release/thrift/$version/thrift-$version.exe"
|
|
}
|
|
"url=$url" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
./build/windows/get-voted-compiler.ps1 -Url $url -OutputDir voted
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
|
|
- name: Collect LICENSE and NOTICE
|
|
env:
|
|
VERSION: ${{ steps.fetch.outputs.version }}
|
|
run: |
|
|
# The package carries the LICENSE and NOTICE of the release it
|
|
# packs. A release has a tag to take them from. A release
|
|
# candidate has none yet, so its files come from the branch this
|
|
# runs on, which then has to be at that version.
|
|
if git fetch --quiet --depth=1 origin "refs/tags/v${VERSION}:refs/tags/v${VERSION}" 2>/dev/null; then
|
|
git show "v${VERSION}:LICENSE" > voted/LICENSE
|
|
git show "v${VERSION}:NOTICE" > voted/NOTICE
|
|
echo "LICENSE and NOTICE from tag v${VERSION}"
|
|
else
|
|
branch_version=$(sed -n 's/^ *set(thrift_VERSION "\([0-9.]*\)").*/\1/p' CMakeLists.txt)
|
|
if [ "$branch_version" != "$VERSION" ]; then
|
|
echo "::error::There is no tag v${VERSION}, and this branch is at ${branch_version}. Run this from the branch the release candidate was made from."
|
|
exit 1
|
|
fi
|
|
cp LICENSE NOTICE voted/
|
|
echo "LICENSE and NOTICE from ${GITHUB_REF_NAME}"
|
|
fi
|
|
|
|
- name: Archive the compiler
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: voted-compiler
|
|
if-no-files-found: error
|
|
path: voted/
|
|
retention-days: 3
|
|
|
|
pack:
|
|
name: Pack
|
|
needs: voted-compiler
|
|
# Runs when the voted compiler is there, and when there was none to fetch
|
|
# and voted-compiler was skipped. Not when fetching or checking it failed:
|
|
# a release must not fall back to building its own compiler.
|
|
if: ${{ !cancelled() && needs.voted-compiler.result != 'failure' }}
|
|
runs-on: windows-2025
|
|
timeout-minutes: 60
|
|
permissions:
|
|
contents: read
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Determine the version
|
|
id: version
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
VOTED_VERSION: ${{ needs.voted-compiler.outputs.version }}
|
|
run: |
|
|
$sourceVersion = ''
|
|
foreach ($line in Get-Content CMakeLists.txt) {
|
|
if ($line -match '^\s*set\(thrift_VERSION\s+"(\d+\.\d+\.\d+)"\)') {
|
|
$sourceVersion = $Matches[1]
|
|
break
|
|
}
|
|
}
|
|
if (-not $sourceVersion) {
|
|
Write-Error 'Could not read thrift_VERSION from CMakeLists.txt.'
|
|
exit 1
|
|
}
|
|
|
|
$version = $sourceVersion
|
|
if ($env:RELEASE_TAG) {
|
|
# A NuGet version can never be replaced, so refuse to publish a
|
|
# package whose version disagrees with the release it comes from.
|
|
# release_rust.yml does the same for the crate.
|
|
$tagVersion = $env:RELEASE_TAG -replace '^v', ''
|
|
if ($tagVersion -ne $sourceVersion) {
|
|
Write-Error "Version mismatch: CMakeLists.txt=$sourceVersion, tag=$env:RELEASE_TAG"
|
|
exit 1
|
|
}
|
|
$version = $tagVersion
|
|
}
|
|
|
|
if ($env:VOTED_VERSION) {
|
|
# On a release the voted executable was fetched for the tag's
|
|
# version. A manual run packs whatever release compiler_url names,
|
|
# which need not be the one this branch is at.
|
|
if ($env:RELEASE_TAG -and $env:VOTED_VERSION -ne $version) {
|
|
Write-Error "Version mismatch: voted compiler=$env:VOTED_VERSION, tag=$env:RELEASE_TAG"
|
|
exit 1
|
|
}
|
|
$version = $env:VOTED_VERSION
|
|
}
|
|
|
|
Write-Host "Version: $version"
|
|
"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
|
|
- name: Download the voted compiler
|
|
if: ${{ needs.voted-compiler.result == 'success' }}
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: voted-compiler
|
|
path: voted
|
|
|
|
- name: Install dependencies
|
|
if: ${{ needs.voted-compiler.result != 'success' }}
|
|
shell: pwsh
|
|
timeout-minutes: 10
|
|
# Pinned to the version build/docker/msvc/Dockerfile uses.
|
|
run: choco install winflexbison3 --version=2.5.24.20210105 -y --no-progress
|
|
|
|
- name: Build the compiler
|
|
if: ${{ needs.voted-compiler.result != 'success' }}
|
|
shell: pwsh
|
|
timeout-minutes: 30
|
|
run: |
|
|
# No -DWITH_MT: the released compiler links the shared runtime. See THRIFT-6320.
|
|
cmake -S . -B cmake_build -DBUILD_LIBRARIES=OFF -DBUILD_TESTING=OFF
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
cmake --build cmake_build --config Release
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
cmake --install cmake_build --config Release --prefix cmake_install
|
|
|
|
- name: Choose the compiler to pack
|
|
id: compiler
|
|
shell: pwsh
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
VOTED: ${{ needs.voted-compiler.result == 'success' }}
|
|
run: |
|
|
if ($env:VOTED -eq 'true') {
|
|
$compiler = "voted/thrift-$env:VERSION.exe"
|
|
# The LICENSE and NOTICE of that release, collected with it.
|
|
$sourceRoot = 'voted'
|
|
}
|
|
else {
|
|
$compiler = 'cmake_install/bin/thrift.exe'
|
|
$sourceRoot = ''
|
|
}
|
|
$sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $compiler).Hash.ToLowerInvariant()
|
|
Write-Host "Packing $compiler, sha256 $sha256"
|
|
"path=$compiler" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
"source_root=$sourceRoot" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
"sha256=$sha256" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
|
|
- name: Check what the compiler imports
|
|
shell: pwsh
|
|
env:
|
|
COMPILER: ${{ steps.compiler.outputs.path }}
|
|
# A published NuGet version cannot be replaced, so this has to hold
|
|
# before the package is built, not only in cmake.yml afterwards.
|
|
run: ./build/windows/check-compiler-imports.ps1 -Path $env:COMPILER
|
|
|
|
- name: Pack the tool
|
|
shell: pwsh
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
COMPILER: ${{ steps.compiler.outputs.path }}
|
|
SOURCE_ROOT: ${{ steps.compiler.outputs.source_root }}
|
|
run: |
|
|
$toolArgs = @{ Version = $env:VERSION; Compiler = $env:COMPILER; OutputDir = 'nupkg' }
|
|
if ($env:SOURCE_ROOT) {
|
|
$toolArgs['SourceRoot'] = $env:SOURCE_ROOT
|
|
}
|
|
./build/windows/build-dotnet-tool.ps1 @toolArgs
|
|
|
|
- name: Test the package
|
|
shell: pwsh
|
|
timeout-minutes: 15
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
COMPILER_SHA256: ${{ steps.compiler.outputs.sha256 }}
|
|
run: |
|
|
# The hash shows that the package carries the very file it was
|
|
# given - for a release, the one the vote covered.
|
|
./build/windows/dotnet-tool/test-dotnet-tool.ps1 `
|
|
-Package "nupkg/Apache.Thrift.Compiler.$env:VERSION.nupkg" `
|
|
-Version $env:VERSION `
|
|
-ExpectedCompilerOutput "Thrift version $env:VERSION" `
|
|
-ExpectedSha256 $env:COMPILER_SHA256
|
|
|
|
- name: Report the compiler
|
|
shell: pwsh
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
COMPILER_SHA256: ${{ steps.compiler.outputs.sha256 }}
|
|
COMPILER_URL: ${{ needs.voted-compiler.outputs.url }}
|
|
run: |
|
|
$package = "nupkg/Apache.Thrift.Compiler.$env:VERSION.nupkg"
|
|
$hash = (Get-FileHash -Algorithm SHA256 $package).Hash.ToLowerInvariant()
|
|
@(
|
|
'### .NET tool',
|
|
'',
|
|
"| | |",
|
|
"|---|---|",
|
|
"| package | ``$([System.IO.Path]::GetFileName($package))`` |",
|
|
"| package sha256 | ``$hash`` |",
|
|
"| compiler | $(if ($env:COMPILER_URL) { $env:COMPILER_URL } else { 'built from source' }) |",
|
|
"| compiler sha256 | ``$env:COMPILER_SHA256`` |"
|
|
) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append
|
|
|
|
- name: Archive the package
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: dotnet-tool
|
|
if-no-files-found: error
|
|
path: nupkg/*.nupkg
|
|
retention-days: 3
|
|
|
|
publish:
|
|
name: Publish to NuGet
|
|
needs: pack
|
|
if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
environment: release
|
|
permissions:
|
|
contents: read
|
|
# Mandatory for NuGet trusted publishing.
|
|
id-token: write
|
|
steps:
|
|
- name: Download the package
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: dotnet-tool
|
|
path: nupkg
|
|
|
|
- name: Set up .NET
|
|
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
|
|
with:
|
|
dotnet-version: "8.0"
|
|
|
|
# Trusted publishing, as for PyPI in pypi.yml and crates.io in
|
|
# release_rust.yml: nuget.org hands back a short lived key in exchange
|
|
# for this run's OIDC token, so no long lived secret is stored here.
|
|
# It needs a trusted publishing policy on nuget.org naming this
|
|
# repository, this workflow and the release environment.
|
|
- name: Authenticate to NuGet
|
|
id: nuget-auth
|
|
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
|
|
with:
|
|
user: ${{ vars.NUGET_USER }}
|
|
|
|
- name: Publish
|
|
env:
|
|
NUGET_API_KEY: ${{ steps.nuget-auth.outputs.NUGET_API_KEY }}
|
|
VERSION: ${{ needs.pack.outputs.version }}
|
|
run: |
|
|
dotnet nuget push "nupkg/Apache.Thrift.Compiler.${VERSION}.nupkg" \
|
|
--source https://api.nuget.org/v3/index.json \
|
|
--api-key "$NUGET_API_KEY" \
|
|
--skip-duplicate
|