diff --git a/.github/workflows/publish-mirror.yml b/.github/workflows/publish-mirror.yml index 4bc7aa282d..4181ca245a 100644 --- a/.github/workflows/publish-mirror.yml +++ b/.github/workflows/publish-mirror.yml @@ -9,6 +9,10 @@ on: plan: required: true type: string + signed-archives: + description: "Artifact ID for the verified native archives and checksums" + required: true + type: string permissions: {} @@ -29,6 +33,12 @@ jobs: pattern: artifacts-* path: artifacts merge-multiple: true + - name: "Use signed native release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ inputs.signed-archives }} + path: artifacts + merge-multiple: true - name: "Upload to R2" env: AWS_ACCESS_KEY_ID: ${{ secrets.MIRROR_R2_ACCESS_KEY_ID }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 6656f5ccba..a374a43af8 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -10,6 +10,10 @@ on: plan: required: true type: string + artifacts: + description: "Artifact ID for the complete verified PyPI distributions" + required: true + type: string env: ACTIONS_CACHE_MODE: none @@ -33,7 +37,7 @@ jobs: enable-cache: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - pattern: wheels-* + artifact-ids: ${{ inputs.artifacts }} path: wheels merge-multiple: true - name: Publish to PyPi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 38a9bbf2ed..586c387bc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,13 +57,14 @@ jobs: release-gate: # N.B. This name should not change, it is used for downstream checks. name: release-gate - if: ${{ github.event_name == 'workflow_dispatch' && inputs.tag != 'dry-run' }} + if: ${{ github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' }} runs-on: ${{ github.repository == 'astral-sh/ty' && 'github-ubuntu-24.04-x86_64-4' || 'ubuntu-latest' }} # This environment requires a 2-factor approval, i.e., the workflow must be approved by another # team member. GitHub fires approval events on every job that deploys to an environment, so we # have a dedicated environment for this purpose instead of using the `release` environment. # We use a GitHub App with a deployment protection rule webhook to ensure that the `release` - # environment is only approved when the `release-gate` job succeeds. + # environment is only approved when the `release-gate` job succeeds. Dry-runs also require + # approval because they use the real signing keys. environment: name: release-gate deployment: true @@ -149,10 +150,24 @@ jobs: "id-token": "write" "packages": "write" + # Signing is a manually maintained stage between binary builds and cargo-dist manifests. + sign-release-binaries: + needs: + - plan + - release-gate + - custom-build-binaries + if: ${{ always() && github.repository == 'astral-sh/ty' && github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && needs.plan.result == 'success' && needs.custom-build-binaries.result == 'success' && needs.release-gate.result == 'success' }} + uses: ./.github/workflows/sign-release-binaries.yml + secrets: inherit # zizmor: ignore[secrets-inherit] + permissions: + contents: read + id-token: write + # The custom binary jobs produce checksum sidecars outside cargo-dist. # Include them in a local manifest before generating the installers. generate-checksum-manifest: needs: + - sign-release-binaries - plan - custom-build-binaries if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} @@ -183,6 +198,12 @@ jobs: pattern: artifacts-* path: target/distrib/ merge-multiple: true + - name: "Use signed native release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-release-binaries.outputs.github-archives }} + path: target/distrib/ + merge-multiple: true - name: Generate local dist manifest shell: bash env: @@ -205,6 +226,7 @@ jobs: # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: + - sign-release-binaries - plan - custom-build-binaries - custom-build-docker @@ -233,6 +255,12 @@ jobs: pattern: artifacts-* path: target/distrib/ merge-multiple: true + - name: "Use signed native release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-release-binaries.outputs.github-archives }} + path: target/distrib/ + merge-multiple: true - id: cargo-dist shell: bash run: | @@ -261,13 +289,14 @@ jobs: # Determines if we should publish/announce host: needs: + - sign-release-binaries - plan - custom-build-binaries - custom-build-docker - build-global-artifacts - generate-checksum-manifest # Require checksums for all built archives before publishing. Other build jobs may be skipped. - if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && needs.generate-checksum-manifest.result == 'success' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }} + if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && needs.sign-release-binaries.result == 'success' && needs.generate-checksum-manifest.result == 'success' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }} permissions: "contents": "read" env: @@ -295,6 +324,12 @@ jobs: pattern: artifacts-* path: target/distrib/ merge-multiple: true + - name: "Use signed native release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-release-binaries.outputs.github-archives }} + path: target/distrib/ + merge-multiple: true # This is a harmless no-op for GitHub Releases, hosting for that happens in "announce" - id: host shell: bash @@ -318,6 +353,7 @@ jobs: custom-publish-pypi: needs: + - sign-release-binaries - plan - host - release-gate @@ -325,6 +361,7 @@ jobs: uses: $/.github/workflows/publish-pypi.yml with: plan: ${{ needs.plan.outputs.val }} + artifacts: ${{ needs.sign-release-binaries.outputs.pypi-artifacts }} secrets: inherit # publish jobs get escalated permissions permissions: @@ -334,6 +371,7 @@ jobs: # Create a GitHub Release while uploading all files to it announce: needs: + - sign-release-binaries - plan - host - release-gate @@ -341,7 +379,7 @@ jobs: # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! - if: ${{ always() && needs.host.result == 'success' && needs.release-gate.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }} + if: ${{ always() && needs.host.result == 'success' && needs.sign-release-binaries.result == 'success' && needs.release-gate.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }} runs-on: "depot-ubuntu-latest-4" environment: name: release @@ -361,6 +399,12 @@ jobs: pattern: artifacts-* path: artifacts merge-multiple: true + - name: "Use signed native release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-release-binaries.outputs.github-archives }} + path: artifacts + merge-multiple: true - name: Cleanup run: | # Remove the granular manifests @@ -436,11 +480,13 @@ jobs: custom-publish-mirror: needs: + - sign-release-binaries - plan - announce uses: $/.github/workflows/publish-mirror.yml with: plan: ${{ needs.plan.outputs.val }} + signed-archives: ${{ needs.sign-release-binaries.outputs.github-archives }} secrets: inherit permissions: "contents": "read" diff --git a/.github/workflows/sign-release-binaries.yml b/.github/workflows/sign-release-binaries.yml new file mode 100644 index 0000000000..7f183ffad0 --- /dev/null +++ b/.github/workflows/sign-release-binaries.yml @@ -0,0 +1,428 @@ +# Sign and verify ty's macOS and Windows wheels and GitHub release archives. +name: "Sign release binaries" + +on: + workflow_call: + outputs: + pypi-artifacts: + description: "Artifact ID for all release wheels and the source distribution" + value: ${{ jobs.assemble.outputs.pypi-artifacts }} + github-archives: + description: "Artifact ID for signed native GitHub archives and checksums" + value: ${{ jobs.assemble.outputs.github-archives }} + +permissions: {} + +env: + ACTIONS_CACHE_MODE: none + UV_LOCKED: "1" + # Keep Azure CLI response metadata out of the workflow log. + AZURE_CORE_OUTPUT: none + +jobs: + prepare: + name: "prepare signing inputs" + runs-on: depot-ubuntu-24.04-4 + permissions: + contents: read + outputs: + targets: ${{ steps.targets.outputs.targets }} + artifacts: ${{ steps.targets.outputs.artifacts }} + macos: ${{ steps.targets.outputs.macos }} + windows: ${{ steps.targets.outputs.windows }} + unsigned-macos: ${{ steps.macos.outputs.artifact-id }} + unsigned-windows: ${{ steps.windows.outputs.artifact-id }} + assembly: ${{ steps.assembly.outputs.artifact-id }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: "Plan signed release targets" + id: targets + uses: astral-sh/github-actions/plan-release-signing@b61c7106c7746375df8005eab240be92e4e450b2 # shared release signing actions + with: + archive-package: ty + packages: >- + { + "macos": {"ty": ["ty"]}, + "windows": {"ty": ["ty.exe"]} + } + + - name: "Download built wheels and source distribution" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: wheels-* + path: wheel-artifacts + + - name: "Download built GitHub release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: artifacts-* + path: github-archives + merge-multiple: true + + - name: "Collect built release wheels" + shell: bash + env: + TARGETS: ${{ steps.targets.outputs.targets }} + run: | + set -euo pipefail + shopt -s failglob + while read -r target; do + artifact_target="$target" + if [[ "$target" == x86_64-apple-darwin ]]; then + artifact_target=macos-x86_64 + fi + candidates=(wheel-artifacts/"wheels-$artifact_target"/ty-*.whl) + test "${#candidates[@]}" -eq 1 + mkdir -p "wheels/$target" + cp "${candidates[0]}" "wheels/$target/" + done < <(jq -r '.[]' <<< "$TARGETS") + candidates=(wheel-artifacts/wheels-sdist/ty-*.tar.gz) + test "${#candidates[@]}" -eq 1 + mkdir sdists + cp "${candidates[0]}" sdists/ + + - name: "Prepare release signing inputs" + uses: astral-sh/github-actions/prepare-release-signing@b61c7106c7746375df8005eab240be92e4e450b2 # shared release signing actions + with: + targets: ${{ steps.targets.outputs.artifacts }} + output-directory: prepared + + - name: "Upload unsigned macOS executables" + id: macos + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: unsigned-binaries-macos-${{ github.run_id }}-${{ github.run_attempt }} + path: prepared/unsigned/macos + if-no-files-found: error + + - name: "Upload unsigned Windows executables" + id: windows + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: unsigned-binaries-windows-${{ github.run_id }}-${{ github.run_attempt }} + path: prepared/unsigned/windows + if-no-files-found: error + + - name: "Upload assembly inputs" + id: assembly + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: signing-assembly-inputs-${{ github.run_id }}-${{ github.run_attempt }} + path: | + wheels + sdists + github-archives + if-no-files-found: error + + sign-macos: + name: "sign and notarize macos binaries" + needs: prepare + if: ${{ github.repository == 'astral-sh/ty' && github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' }} + runs-on: ${{ github.repository_owner == 'astral-sh' && 'github-ubuntu-24.04-x86_64-4' || 'ubuntu-24.04' }} + environment: release + permissions: + contents: read + id-token: write + outputs: + artifact: ${{ steps.upload.outputs.artifact-id }} + steps: + - name: "Download unsigned executable wheel members" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.prepare.outputs.unsigned-macos }} + path: unsigned + merge-multiple: true + + - name: "Sign and notarize macOS executables" + uses: astral-sh/github-actions/sign-macos@a30f3520f325da7fed035847afec51629d0ef4fa # shared release signing actions + with: + unsigned-directory: ${{ github.workspace }}/unsigned + signed-directory: ${{ github.workspace }}/signed + azure-client-id: ${{ secrets.CODESIGN_AZURE_CLIENT_ID_MACOS }} + azure-tenant-id: ${{ secrets.CODESIGN_AZURE_TENANT_ID_MACOS }} + azure-subscription-id: ${{ secrets.CODESIGN_AZURE_SUBSCRIPTION_ID_MACOS }} + storage-account: ${{ secrets.CODESIGN_STORAGE_ACCOUNT }} + storage-container: ${{ secrets.CODESIGN_STORAGE_CONTAINER }} + component-pkcs11-blob: ${{ secrets.CODESIGN_COMPONENT_PKCS11_BLOB }} + component-pkcs11-sha256: ${{ secrets.CODESIGN_COMPONENT_PKCS11_SHA256 }} + azure-keyvault-name: ${{ secrets.CODESIGN_AZURE_KEYVAULT_NAME }} + azure-keyvault-key-version: ${{ secrets.CODESIGN_AZURE_KEYVAULT_KEY_VERSION }} + key-name: ${{ secrets.CODESIGN_KEY_NAME }} + certificate-sha256: ${{ secrets.CODESIGN_CERTIFICATE_SHA256 }} + apple-notarization-akv-key-name: ${{ secrets.CODESIGN_APPLE_NOTARIZATION_AKV_KEY_NAME }} + + - name: "Upload signed executable wheel members" + id: upload + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: signed-binaries-macos-${{ github.run_id }}-${{ github.run_attempt }} + path: signed + if-no-files-found: error + + sign-windows: + name: "sign windows binaries" + needs: prepare + if: ${{ github.repository == 'astral-sh/ty' && github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' }} + runs-on: windows-2025 + environment: release + permissions: + id-token: write + outputs: + artifact: ${{ steps.upload.outputs.artifact-id }} + steps: + - name: "Download unsigned executable wheel members" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.prepare.outputs.unsigned-windows }} + path: unsigned + merge-multiple: true + + - name: "Sign Windows executables" + uses: astral-sh/github-actions/sign-windows@b61c7106c7746375df8005eab240be92e4e450b2 # shared release signing actions + with: + unsigned-directory: ${{ github.workspace }}/unsigned + signed-directory: ${{ github.workspace }}/signed + azure-client-id: ${{ secrets.CODESIGN_AZURE_CLIENT_ID_WINDOWS }} + azure-tenant-id: ${{ secrets.CODESIGN_AZURE_TENANT_ID_WINDOWS }} + azure-subscription-id: ${{ secrets.CODESIGN_AZURE_SUBSCRIPTION_ID_WINDOWS }} + endpoint: ${{ secrets.CODESIGN_ENDPOINT }} + signing-account-name: ${{ secrets.CODESIGN_SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.CODESIGN_CERTIFICATE_PROFILE_NAME }} + certificate-subject: ${{ secrets.CODESIGN_CERTIFICATE_SUBJECT }} + + - name: "Upload signed executable wheel members" + id: upload + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: signed-binaries-windows-${{ github.run_id }}-${{ github.run_attempt }} + path: signed + if-no-files-found: error + + assemble: + name: "assemble signed release artifacts" + needs: [prepare, sign-macos, sign-windows] + runs-on: depot-ubuntu-24.04-4 + permissions: + contents: read + outputs: + macos-wheels: ${{ steps.macos-wheels.outputs.artifact-id }} + windows-wheels: ${{ steps.windows-wheels.outputs.artifact-id }} + pypi-artifacts: ${{ steps.pypi-artifacts.outputs.artifact-id }} + github-archives: ${{ steps.github-archives.outputs.artifact-id }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: "Download assembly inputs" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.prepare.outputs.assembly }} + merge-multiple: true + + - name: "Download signed macOS executables" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-macos.outputs.artifact }} + path: signed + merge-multiple: true + + - name: "Download signed Windows executables" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-windows.outputs.artifact }} + path: signed + merge-multiple: true + + - name: "Assemble signed release artifacts" + uses: astral-sh/github-actions/assemble-signed-release@b61c7106c7746375df8005eab240be92e4e450b2 # shared release signing actions + with: + targets: ${{ needs.prepare.outputs.artifacts }} + # The complete release inputs retain the originals checked during preparation. + prepared-directory: . + signed-directory: signed + output-directory: signed-artifacts + + - name: "Collect complete PyPI distributions" + shell: bash + run: | + set -euo pipefail + shopt -s failglob + mkdir pypi + for wheel in wheels/*/ty-*.whl; do + test ! -e "pypi/$(basename "$wheel")" + cp "$wheel" pypi/ + done + cp sdists/ty-*.tar.gz pypi/ + for wheel in signed-artifacts/pypi/ty/*.whl; do + test -f "pypi/$(basename "$wheel")" + cp "$wheel" pypi/ + done + + - name: "Upload signed macOS wheels" + id: macos-wheels + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: signed-wheels-macos-${{ github.run_id }}-${{ github.run_attempt }} + path: signed-artifacts/wheels/macos + if-no-files-found: error + + - name: "Upload signed Windows wheels" + id: windows-wheels + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: signed-wheels-windows-${{ github.run_id }}-${{ github.run_attempt }} + path: signed-artifacts/wheels/windows + if-no-files-found: error + + - name: "Upload PyPI distributions" + id: pypi-artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-pypi-${{ github.run_id }}-${{ github.run_attempt }} + path: pypi + if-no-files-found: error + + - name: "Upload signed GitHub release archives" + id: github-archives + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: signed-github-archives-${{ github.run_id }}-${{ github.run_attempt }} + path: signed-artifacts/github-archives/* + if-no-files-found: error + + verify-macos: + name: "verify signed ${{ matrix.platform.target }} artifacts" + needs: [prepare, sign-macos, assemble] + strategy: + fail-fast: false + matrix: + platform: ${{ fromJSON(needs.prepare.outputs.macos) }} + runs-on: ${{ matrix.platform.runner }} + permissions: + contents: read + env: + TARGET: ${{ matrix.platform.target }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + cache: "" + python-version: "3.13" + architecture: ${{ matrix.platform.python-architecture }} + + - name: "Download signed executables" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-macos.outputs.artifact }} + path: signed + merge-multiple: true + + - name: "Download signed wheels" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.assemble.outputs.macos-wheels }} + path: wheels + merge-multiple: true + + - name: "Download signed GitHub release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.assemble.outputs.github-archives }} + path: github-archives + merge-multiple: true + + - name: "Verify signed release artifacts" + uses: astral-sh/github-actions/verify-release@b61c7106c7746375df8005eab240be92e4e450b2 # shared release signing actions + with: + target: ${{ toJSON(matrix.platform) }} + signed-directory: signed/${{ matrix.platform.target }} + wheels-directory: wheels/${{ matrix.platform.target }} + github-archives-directory: github-archives + + - name: "Install and run the signed wheels" + shell: bash + run: | + set -euo pipefail + python=(python) + # Intel wheels are verified on an ARM runner and need the Intel Python slice. + if [[ "$TARGET" == x86_64-apple-darwin ]]; then + python=(arch -x86_64 python) + fi + "${python[@]}" -m pip install wheels/"$TARGET"/ty-*.whl --force-reinstall --no-index --no-deps + ty --version + ty --help + "${python[@]}" -m ty --help + + verify-windows: + name: "verify signed ${{ matrix.platform.target }} artifacts" + needs: [prepare, sign-windows, assemble] + strategy: + fail-fast: false + matrix: + platform: ${{ fromJSON(needs.prepare.outputs.windows) }} + runs-on: ${{ matrix.platform.runner }} + permissions: + contents: read + env: + TARGET: ${{ matrix.platform.target }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + cache: "" + python-version: "3.13" + architecture: ${{ matrix.platform.python-architecture }} + + - name: "Download signed executables" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.sign-windows.outputs.artifact }} + path: signed + merge-multiple: true + + - name: "Download signed wheels" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.assemble.outputs.windows-wheels }} + path: wheels + merge-multiple: true + + - name: "Download signed GitHub release archives" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + artifact-ids: ${{ needs.assemble.outputs.github-archives }} + path: github-archives + merge-multiple: true + + - name: "Verify signed release artifacts" + uses: astral-sh/github-actions/verify-release@b61c7106c7746375df8005eab240be92e4e450b2 # shared release signing actions + with: + target: ${{ toJSON(matrix.platform) }} + signed-directory: signed/${{ matrix.platform.target }} + wheels-directory: wheels/${{ matrix.platform.target }} + github-archives-directory: github-archives + + - name: "Install and run the signed wheels" + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $wheel = Get-ChildItem -LiteralPath "wheels/$env:TARGET" -Filter 'ty-*.whl' + if (@($wheel).Count -ne 1) { throw 'Expected exactly one signed wheel' } + python -m pip install $wheel.FullName --force-reinstall --no-index --no-deps + if ($LASTEXITCODE -ne 0) { throw 'Signed wheel installation failed' } + ty --version + if ($LASTEXITCODE -ne 0) { throw 'Signed executable failed' } + ty --help + if ($LASTEXITCODE -ne 0) { throw 'Signed executable help failed' } + python -m ty --help + if ($LASTEXITCODE -ne 0) { throw 'Signed module invocation failed' } diff --git a/dist-workspace.toml b/dist-workspace.toml index ed0191093d..5caa316e9e 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -28,7 +28,6 @@ targets = [ "armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-musleabihf", "x86_64-apple-darwin", - "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "riscv64gc-unknown-linux-gnu", "s390x-unknown-linux-gnu",