Compare commits

...

5 Commits

Author SHA1 Message Date
Niklas Merz
b73ae02569 Rename pauseCompositor/resumeCompositor in JNIServo (#41827)
pauseCompositor/resumeCompositor have been renamed
topausePainting/resumePainting in 824f551. This renames the Java methods
to fix the regression.

Testing: Built servoview.aar and tested in Android app.
Fixes #41826

Signed-off-by: Niklas Merz <niklasmerz@apache.org>
(cherry picked from commit 4f3b6fd267)
2026-01-22 08:23:18 +01:00
Jonathan Schwender
db5ab69419 Further release workflow improvements
This cherry-picks https://github.com/servo/servo/pull/41932

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-01-21 20:57:50 +01:00
Jonathan Schwender
37cf4bc7a3 Move upload_nightly to a dedicated script (#41922)
Follow-up to https://github.com/servo/servo/pull/41882

This further reduces the amount of code that is involved in handling our
publishing.

Testing: Manual triggering of a test release [workflow
run](https://github.com/servo/servo/actions/runs/21026468888)

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
(cherry picked from commit bca2e2ca74)
2026-01-21 20:55:36 +01:00
Jonathan Schwender
61675a03f8 CI: Refactor nightly release process (#41882)
This change moves the upload and attestation steps to the nightly
workflow. This results in overall slightly more code, but reduces the
amount of code that has access to extra privileges.

Additionally, this refactoring will allow to easily change the target
repo for the nightly release, i.e. allowing us to reuse the nightly
workflow for our monthly releases in the servo/servo repo (in a
follow-up PR).

We change upload_nightly to get an additional `packages` parameter, that
we use instead of a hardcoded table of expected packages per platform.
`download-artifact` doesn't preserve the whole directory structure, so
it would be a bit cumbersome to preserve that.
Since we anyway only use the upload script in CI, we can just directly
specify the packages in the CI, which also makes things slightly more
flexibel if we want to add new packages or platforms.

There are still more improvements to come, specifically also to the
`upload_nightly` script, with the goal to minimize the amount of code
that needs to be trusted.

Testing: Manually triggering the [release
workflow](https://github.com/servo/servo/actions/runs/20996682863/job/60358012404)
via workflow-dispatch.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
(cherry picked from commit f0b2db0994)
2026-01-21 20:55:31 +01:00
Jonathan Schwender
15a4845177 Backport: Set git revisions in Cargo.toml
This backports (the idea) of d33997c1 and encodes the hashes
of our git dependency in our Cargo.toml, since our Cargo.lock
will be ignored in projects depending on us.

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-01-21 17:28:36 +01:00
19 changed files with 1112 additions and 425 deletions

View File

@@ -1,18 +1,15 @@
name: Android
on:
workflow_call:
outputs:
artifact_ids:
value: ${{ jobs.build.outputs.artifact_ids }}
description: Comma-separated list of artifact IDs for the release artifacts
inputs:
profile:
required: false
default: "release"
type: string
upload:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
bencher:
required: false
default: false
@@ -40,6 +37,8 @@ jobs:
build:
name: Android Build
runs-on: ubuntu-22.04
outputs:
artifact_ids: ${{ steps.artifact_ids.outputs.artifact_ids }}
strategy:
matrix:
target: ['aarch64-linux-android', 'armv7-linux-androideabi', 'x86_64-linux-android']
@@ -117,36 +116,24 @@ jobs:
name: cargo-timings-android-${{ matrix.target }}-${{ inputs.profile }}
# Using a wildcard here ensures that the archive includes the path.
path: target/cargo-timings-*
- name: Upload nightly
if: ${{ inputs.upload && contains(matrix.target, 'aarch64') }}
run: |
./mach upload-nightly android \
--secret-from-environment \
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
- name: Generate artifact attestation for APK
if: ${{ inputs.upload }}
uses: actions/attest-build-provenance@v1
with:
subject-path: target/android/${{ matrix.target }}/${{ inputs.profile }}/servoapp.apk
- name: Upload APK artifact
uses: actions/upload-artifact@v4
id: upload-apk
with:
name: ${{ inputs.profile }}-binary-android-${{ matrix.target }}
path: target/android/${{ matrix.target }}/${{ inputs.profile }}/servoapp.apk
- name: Generate artifact attestation for AAR
if: ${{ inputs.upload }}
uses: actions/attest-build-provenance@v1
with:
subject-path: target/android/${{ matrix.target }}/${{ inputs.profile }}/servoview.aar
- name: Upload AAR artifact
uses: actions/upload-artifact@v4
id: upload-aar
with:
name: ${{ inputs.profile }}-library-android-${{ matrix.target }}
path: target/android/${{ matrix.target }}/${{ inputs.profile }}/servoview.aar
- name: Collect artifact IDs needed by the release workflow
if: matrix.target == 'aarch64-linux-android'
id: artifact_ids
shell: bash
run: |
echo "artifact_ids=${{steps.upload-apk.outputs.artifact-id}},${{steps.upload-aar.outputs.artifact-id}}" >> $GITHUB_OUTPUT
bencher:
needs: ["build"]

View File

@@ -34,7 +34,7 @@ jobs:
# <https://github.com/servo/servo/settings/variables/actions>
NO_SELF_HOSTED_RUNNERS: ${{ vars.NO_SELF_HOSTED_RUNNERS }}
# Any other boolean conditions that disable self-hosted runners go here.
force-github-hosted-runner: ${{ inputs.upload || inputs.force-github-hosted-runner }}
force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }}
lint:
needs:

View File

@@ -1,6 +1,10 @@
name: Linux
on:
workflow_call:
outputs:
artifact_ids:
value: ${{ jobs.build.outputs.artifact_ids }}
description: Comma-separated list of artifact IDs for the release artifacts
inputs:
profile:
required: false
@@ -33,13 +37,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
force-github-hosted-runner:
required: false
type: boolean
@@ -82,10 +79,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
force-github-hosted-runner:
required: false
type: boolean
@@ -125,13 +118,15 @@ jobs:
# <https://github.com/servo/servo/settings/variables/actions>
NO_SELF_HOSTED_RUNNERS: ${{ vars.NO_SELF_HOSTED_RUNNERS }}
# Any other boolean conditions that disable self-hosted runners go here.
force-github-hosted-runner: ${{ inputs.upload || inputs.force-github-hosted-runner }}
force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }}
build:
needs:
- runner-select
name: Linux Build [${{ needs.runner-select.outputs.unique-id }}]
runs-on: ${{ needs.runner-select.outputs.selected-runner-label }}
outputs:
artifact_ids: ${{ steps.artifact_ids.outputs.artifact_ids }}
steps:
- uses: servo/ci-runners/actions/checkout@44317e3cd86c5ff2ef0b08878b90da246bc237da
- name: Free Disk Space (Ubuntu)
@@ -208,19 +203,15 @@ jobs:
run: ./mach package --profile ${{ inputs.profile }}
- name: Upload artifact for mach package
uses: actions/upload-artifact@v4
id: upload-tarball
with:
name: ${{ inputs.profile }}-binary-linux
path: target/${{ inputs.profile }}/servo-tech-demo.tar.gz
- name: Upload nightly
if: ${{ inputs.upload }}
- name: Collect artifact IDs needed by the release workflow
id: artifact_ids
shell: bash
run: |
./mach upload-nightly linux \
--secret-from-environment \
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
echo "artifact_ids=${{steps.upload-tarball.outputs.artifact-id}}" >> $GITHUB_OUTPUT
wpt-2020:
if: ${{ inputs.wpt }}

View File

@@ -2,6 +2,10 @@ name: MacOS Arm64
on:
workflow_call:
outputs:
artifact_ids:
value: ${{ jobs.build.outputs.artifact_ids }}
description: Comma-separated list of artifact IDs for the release artifacts
inputs:
profile:
required: false
@@ -26,13 +30,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
force-github-hosted-runner:
required: false
type: boolean
@@ -63,10 +60,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
force-github-hosted-runner:
required: false
type: boolean
@@ -85,6 +78,8 @@ jobs:
build:
name: MacOS 15 Build
runs-on: macos-15
outputs:
artifact_ids: ${{ steps.artifact_ids.outputs.artifact_ids }}
steps:
# XProtect can cause random failures if it decides that the DMG we create
# during the packaging phase is malware.
@@ -182,19 +177,15 @@ jobs:
path: target/cargo-timings-*
- name: Upload artifact for mach package
uses: actions/upload-artifact@v4
id: upload-dmg
with:
name: ${{ inputs.profile }}-binary-mac-arm64
path: target/${{ inputs.profile }}/servo-tech-demo.dmg
- name: Upload nightly
if: ${{ inputs.upload }}
- name: Collect artifact IDs needed by the release workflow
id: artifact_ids
shell: bash
run: |
./mach upload-nightly mac-arm64 --secret-from-environment \
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
GITHUB_HOMEBREW_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
echo "artifact_ids=${{ steps.upload-dmg.outputs.artifact-id }}" >> $GITHUB_OUTPUT
- name: Build package for target
run: gtar -czf target.tar.gz target/${{ inputs.profile }}/servo target/${{ inputs.profile }}/lib/*.dylib resources
- name: Upload package for target

View File

@@ -2,6 +2,10 @@ name: MacOS
on:
workflow_call:
outputs:
artifact_ids:
value: ${{ jobs.build.outputs.artifact_ids }}
description: Comma-separated list of artifact IDs for the release artifacts
inputs:
profile:
required: false
@@ -26,13 +30,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
force-github-hosted-runner:
required: false
type: boolean
@@ -63,10 +60,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
force-github-hosted-runner:
required: false
type: boolean
@@ -111,6 +104,8 @@ jobs:
- runner-select
name: MacOS Build [${{ needs.runner-select.outputs.unique-id }}]
runs-on: ${{ needs.runner-select.outputs.selected-runner-label }}
outputs:
artifact_ids: ${{ steps.artifact_ids.outputs.artifact_ids }}
steps:
# XProtect can cause random failures if it decides that the DMG we create
# during the packaging phase is malware.
@@ -190,19 +185,15 @@ jobs:
path: target/cargo-timings-*
- name: Upload artifact for mach package
uses: actions/upload-artifact@v4
id: upload-dmg
with:
name: ${{ inputs.profile }}-binary-mac
path: target/${{ inputs.profile }}/servo-tech-demo.dmg
- name: Upload nightly
if: ${{ inputs.upload }}
- name: Collect artifact IDs needed by the release workflow
id: artifact_ids
shell: bash
run: |
./mach upload-nightly mac --secret-from-environment \
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
GITHUB_HOMEBREW_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
echo "artifact_ids=${{ steps.upload-dmg.outputs.artifact-id }}" >> $GITHUB_OUTPUT
- name: Build package for target
run: gtar -czf target.tar.gz target/${{ inputs.profile }}/servo target/${{ inputs.profile }}/lib/*.dylib resources
- name: Upload package for target

View File

@@ -1,14 +1,25 @@
name: Release nightly
name: Release
on:
schedule:
# Run at 5:30 am, daily.
- cron: '15 5 * * *'
workflow_dispatch:
inputs:
# Note: On scheduled runs `inputs.regular_release` will be `null`, which allows us to create
# defaults by using `${{ inputs.regular_release || 'my_default_value' }}` expressions.
regular_release:
description: '`true` to create a release on this repo, false to release to the nightly-releases repo'
type: boolean
default: false
release_tag:
required: true
type: string
description: 'The tag to create for the release'
env:
RUST_BACKTRACE: 1
SHELL: /bin/bash
RELEASE_REPO: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
jobs:
create-draft-release:
@@ -19,30 +30,38 @@ jobs:
steps:
- id: create-release
run: |
NIGHTLY_TAG=$(date "+%F")
RELEASE_URL=$(gh release create "${NIGHTLY_TAG}" \
if [[ "${{ inputs.release_tag }}" != "" ]]; then
RELEASE_TAG="${{ inputs.release_tag }}"
RELEASE_NOTES="Servo release ${{ inputs.release_tag }}"
else
RELEASE_TAG=$(date "+%F")
RELEASE_NOTES="Nightly build based on servo/servo@${{ github.sha }}"
fi
RELEASE_URL=$(gh release create "${RELEASE_TAG}" \
--draft \
--title "${NIGHTLY_TAG}" \
--notes 'Nightly builds based on servo/servo@${{ github.sha }}' \
--repo ${NIGHTLY_REPO})
--title "${RELEASE_TAG}" \
--notes "${RELEASE_NOTES}" \
--repo ${RELEASE_REPO})
TEMP_TAG=$(basename "$RELEASE_URL")
RELEASE_ID=$( \
gh api -H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${NIGHTLY_REPO}/releases/tags/${TEMP_TAG}" \
"/repos/${RELEASE_REPO}/releases/tags/${TEMP_TAG}" \
| jq '.id' \
)
echo "RELEASE_ID=${RELEASE_ID}" >> ${GITHUB_OUTPUT}
env:
GITHUB_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
GITHUB_TOKEN: ${{ inputs.regular_release && github.token || secrets.NIGHTLY_REPO_TOKEN }}
outputs:
release-id: ${{ steps.create-release.outputs.RELEASE_ID }}
publish-nightly-release:
# This job is only useful when run on upstream servo.
if: always() && (github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
name: Publish GH Release for nightly
# We only auto-publish nightly releases, so we do not use this job for regular releases.
if: |
always()
&& (github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
name: Publish GH Release
runs-on: ubuntu-latest
steps:
- name: Publish as latest (success)
@@ -52,7 +71,7 @@ jobs:
--method PATCH \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${NIGHTLY_REPO}/releases/${RELEASE_ID} \
/repos/${RELEASE_REPO}/releases/${RELEASE_ID} \
-F draft=false
- name: Publish as latest (failure)
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
@@ -61,105 +80,314 @@ jobs:
--method PATCH \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${NIGHTLY_REPO}/releases/${RELEASE_ID} \
/repos/${RELEASE_REPO}/releases/${RELEASE_ID} \
-F prerelease=true -F draft=false
env:
GITHUB_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
GITHUB_TOKEN: ${{ inputs.regular_release && github.token || secrets.NIGHTLY_REPO_TOKEN }}
RELEASE_ID: ${{ needs.create-draft-release.outputs.release-id }}
needs:
- create-draft-release
- upload-linux
- upload-win
- upload-mac
- upload-mac-arm64
- upload-android
- upload-ohos
- upload-linux-nightly
- upload-win-nightly
- upload-mac-nightly
- upload-mac-arm64-nightly
- upload-android-nightly
- upload-ohos-nightly
upload-win:
build-win:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload nightly (Windows)
needs:
- create-draft-release
name: Build production release (Windows)
uses: ./.github/workflows/windows.yml
with:
profile: "production"
upload: true
github-release-id: ${{ needs.create-draft-release.outputs.release-id }}
upload_zip: true
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
secrets: inherit
upload-mac:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload nightly (macOS)
upload-win-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
permissions:
id-token: write
attestations: write
needs:
- create-draft-release
- build-win
secrets:
github_upload_token: ${{ secrets.NIGHTLY_REPO_TOKEN }}
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-win.outputs.artifact_ids }}
artifact_platform: windows-msvc
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
upload-win-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
permissions:
id-token: write
attestations: write
# Necessary for the github token to upload artifacts to the release.
contents: write
needs:
- create-draft-release
- build-win
secrets:
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-win.outputs.artifact_ids }}
artifact_platform: windows-msvc
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
build-mac:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Build production release (macOS)
uses: ./.github/workflows/mac.yml
with:
profile: "production"
upload: true
github-release-id: ${{ needs.create-draft-release.outputs.release-id }}
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
secrets: inherit
upload-mac-arm64:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload nightly (macOS Arm64)
upload-mac-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
permissions:
id-token: write
attestations: write
needs:
- create-draft-release
- build-mac
secrets:
github_upload_token: ${{ secrets.NIGHTLY_REPO_TOKEN }}
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-mac.outputs.artifact_ids }}
artifact_platform: mac
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
upload-mac-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
permissions:
id-token: write
attestations: write
# Necessary for the github token to upload artifacts to the release.
contents: write
needs:
- create-draft-release
- build-mac
secrets:
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-mac.outputs.artifact_ids }}
artifact_platform: mac
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
build-mac-arm64:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Build production release (macOS Arm64)
uses: ./.github/workflows/mac-arm64.yml
with:
profile: "production"
upload: true
github-release-id: ${{ needs.create-draft-release.outputs.release-id }}
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
secrets: inherit
upload-linux:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload nightly (Linux)
upload-mac-arm64-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
permissions:
id-token: write
attestations: write
needs:
- create-draft-release
- build-mac-arm64
secrets:
github_upload_token: ${{ secrets.NIGHTLY_REPO_TOKEN }}
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-mac-arm64.outputs.artifact_ids }}
artifact_platform: mac-arm64
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
upload-mac-arm64-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
permissions:
id-token: write
attestations: write
# Necessary for the github token to upload artifacts to the release.
contents: write
needs:
- create-draft-release
- build-mac-arm64
secrets:
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-mac-arm64.outputs.artifact_ids }}
artifact_platform: mac-arm64
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
build-linux:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Build production release (Linux)
uses: ./.github/workflows/linux.yml
with:
profile: "production"
upload: true
github-release-id: ${{ needs.create-draft-release.outputs.release-id }}
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
secrets: inherit
upload-android:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload nightly (Android)
needs:
- create-draft-release
upload-linux-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
permissions:
id-token: write
attestations: write
needs:
- create-draft-release
- build-linux
secrets:
github_upload_token: ${{ secrets.NIGHTLY_REPO_TOKEN }}
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-linux.outputs.artifact_ids }}
artifact_platform: linux
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
upload-linux-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
permissions:
id-token: write
attestations: write
# Necessary for the github token to upload artifacts to the release.
contents: write
needs:
- create-draft-release
- build-linux
secrets:
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-linux.outputs.artifact_ids }}
artifact_platform: linux
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
build-android:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Build release (Android)
uses: ./.github/workflows/android.yml
with:
profile: "release"
upload: true
github-release-id: ${{ needs.create-draft-release.outputs.release-id }}
secrets: inherit
upload-ohos:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload nightly (OpenHarmony)
needs:
- create-draft-release
upload-android-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
permissions:
id-token: write
attestations: write
needs:
- create-draft-release
- build-android
secrets:
github_upload_token: ${{ secrets.NIGHTLY_REPO_TOKEN }}
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-android.outputs.artifact_ids }}
artifact_platform: android
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
upload-android-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
permissions:
id-token: write
attestations: write
# Necessary for the github token to upload artifacts to the release.
contents: write
needs:
- create-draft-release
- build-android
secrets:
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-android.outputs.artifact_ids }}
artifact_platform: android
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
build-ohos:
# This job is only useful when run on upstream servo.
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Build production release (OpenHarmony)
uses: ./.github/workflows/ohos.yml
with:
profile: "production"
upload: true
github-release-id: ${{ needs.create-draft-release.outputs.release-id }}
upload_library: true
secrets: inherit
upload-ohos-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
permissions:
id-token: write
attestations: write
needs:
- create-draft-release
- build-ohos
secrets:
github_upload_token: ${{ secrets.NIGHTLY_REPO_TOKEN }}
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-ohos.outputs.artifact_ids }}
artifact_platform: ohos
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}
upload-ohos-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
permissions:
id-token: write
attestations: write
# Necessary for the github token to upload artifacts to the release.
contents: write
needs:
- create-draft-release
- build-ohos
secrets:
s3_upload_token: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
uses: ./.github/workflows/upload_release.yml
with:
artifact_ids: ${{ needs.build-ohos.outputs.artifact_ids }}
artifact_platform: ohos
github_release_id: ${{ needs.create-draft-release.outputs.release-id }}
target_repo: ${{ github.repository_owner }}/${{ inputs.regular_release && 'servo' || 'servo-nightly-builds' }}

View File

@@ -6,18 +6,18 @@ on:
required: false
default: "release"
type: string
upload:
upload_library:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
bencher:
required: false
default: false
type: boolean
outputs:
artifact_ids:
value: ${{ jobs.build.outputs.artifact_ids }}
description: Comma-separated list of artifact IDs for the release artifacts
workflow_dispatch:
inputs:
profile:
@@ -46,6 +46,7 @@ jobs:
matrix:
target: ["aarch64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"]
outputs:
artifact_ids: ${{ steps.artifact_ids.outputs.artifact_ids }}
signed: ${{ steps.signing_config.outputs.signed }}
steps:
- uses: actions/checkout@v5
@@ -86,7 +87,7 @@ jobs:
id: signing_config
env:
SIGNING_MATERIAL: ${{ secrets.SERVO_OHOS_SIGNING_MATERIAL }}
if: ${{ inputs.upload || env.SIGNING_MATERIAL != '' }} # Allows the build to pass on forks.
if: ${{ env.SIGNING_MATERIAL != '' }} # Allows the build to pass on forks.
run: |
cd ~
echo "${SIGNING_MATERIAL}" | base64 -d > servo-ohos-material.zip
@@ -106,33 +107,35 @@ jobs:
name: cargo-timings-ohos-${{ matrix.target }}-${{ inputs.profile }}
# Using a wildcard here ensures that the archive includes the path.
path: target/cargo-timings-*
- name: Upload nightly
if: ${{ inputs.upload && contains(matrix.target, 'aarch64') }}
run: |
./mach upload-nightly ohos \
--secret-from-environment \
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
- name: Generate artifact attestation for HAP
if: ${{ inputs.upload }}
uses: actions/attest-build-provenance@v1
- name: Upload shared library
if: ${{ inputs.upload_library }}
uses: actions/upload-artifact@v4
id: upload-library
with:
subject-path: target/openharmony/${{ matrix.target }}/${{ inputs.profile }}/entry/build/default/outputs/default/servoshell-default-signed.hap
name: ${{ inputs.profile }}-library-ohos-${{ matrix.target }}
path: target/aarch64-unknown-linux-ohos/production/libservoshell.so
- name: Upload signed HAP artifact
if: ${{ env.SERVO_OHOS_SIGNING_CONFIG != '' }} # Build output has different name if not signed.
uses: actions/upload-artifact@v4
id: upload-hap-signed
with:
name: ${{ inputs.profile }}-binary-ohos-${{ matrix.target }}
path: target/openharmony/${{ matrix.target }}/${{ inputs.profile }}/entry/build/default/outputs/default/servoshell-default-signed.hap
- name: Upload unsigned HAP artifact
if: ${{ env.SERVO_OHOS_SIGNING_CONFIG == '' }} # Build output has different name if not signed.
uses: actions/upload-artifact@v4
id: upload-hap-unsigned
with:
name: ${{ inputs.profile }}-binary-ohos-${{ matrix.target }}
path: target/openharmony/${{ matrix.target }}/${{ inputs.profile }}/entry/build/default/outputs/default/servoshell-default-unsigned.hap
- name: Collect artifact IDs needed by the release workflow
# The implicit assumption here is that `upload_library` is true and the signed hap is uploaded,
# since this output will only be consumed by the release workflow.
if: matrix.target == 'aarch64-unknown-linux-ohos'
id: artifact_ids
shell: bash
run: |
echo "artifact_ids=${{steps.upload-library.outputs.artifact-id}},${{steps.upload-hap-signed.outputs.artifact-id}}" >> $GITHUB_OUTPUT
bencher:
needs: ["build"]

60
.github/workflows/upload_release.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Upload and Attest Release Assets
on:
workflow_call:
inputs:
artifact_platform:
type: string
required: true
description: "The platform of the release artifacts to upload."
target_repo:
type: string
required: true
description: "The target repository owner and name (e.g. `servo/servo`) where the release will be created."
github_release_id:
type: string
required: true
description: "The ID of the GitHub release to which assets will be added."
artifact_ids:
required: true
type: string
description: "A comma-separated list of artifact IDs to upload."
secrets:
github_upload_token:
required: false
description: "A GitHub token with permission to upload release assets. If omitted github.token will be used instead."
s3_upload_token:
required: true
description: "A token with permission to upload release artifacts to our S3 bucket."
jobs:
upload-artifact:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: |
.github
etc/ci
fetch-depth: '1'
- name: Setup Python
uses: ./.github/actions/setup-python
- uses: actions/download-artifact@v7
with:
artifact-ids: ${{ inputs.artifact_ids }}
merge-multiple: true
path: release-artifacts
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
with:
subject-path: 'release-artifacts/*'
- name: Upload release artifacts
run: |
./etc/ci/upload_nightly.py ${{ inputs.artifact_platform}} \
--secret-from-environment \
--github-release-id ${{ inputs.github_release_id }} \
release-artifacts/*
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.s3_upload_token }}
RELEASE_REPO_TOKEN: ${{ secrets.github_upload_token || github.token }}
RELEASE_REPO: ${{ inputs.target_repo }}

View File

@@ -19,13 +19,10 @@ on:
required: false
default: false
type: boolean
upload:
upload_zip:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
force-github-hosted-runner:
required: false
type: boolean
@@ -34,6 +31,11 @@ on:
required: false
default: false
type: boolean
outputs:
artifact_ids:
value: ${{ jobs.build.outputs.release_artifact_ids }}
description: Comma-separated list of artifact IDs for the release artifacts
workflow_dispatch:
inputs:
profile:
@@ -49,10 +51,6 @@ on:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
force-github-hosted-runner:
required: false
type: boolean
@@ -102,6 +100,8 @@ jobs:
- runner-select
name: Windows Build [${{ needs.runner-select.outputs.unique-id }}]
runs-on: ${{ needs.runner-select.outputs.selected-runner-label }}
outputs:
release_artifact_ids: ${{ steps.artifact_ids.outputs.release_artifact_ids }}
steps:
- uses: servo/ci-runners/actions/checkout@44317e3cd86c5ff2ef0b08878b90da246bc237da
@@ -195,24 +195,33 @@ jobs:
path: C:\\a\\servo\\servo\\target\\cargo-timings-*
- name: Build mach package
run: .\mach package --profile ${{ inputs.profile }}
# These files are available
# MSI Installer: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Installer.msi
# Bundle: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Servo.exe
# Zip: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Servo.zip
- name: Upload artifact for mach package
uses: actions/upload-artifact@v4
id: upload-exe
with:
name: ${{ inputs.profile }}-binary-windows
# These files are available
# MSI Installer: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Installer.msi
# Bundle: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Servo.exe
# Zip: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Servo.zip
path: C:\\a\\servo\\servo\\target\\${{ inputs.profile }}\\msi\\Servo.exe
- name: Upload nightly
if: ${{ inputs.upload }}
- name: Upload artifact for mach package (zip)
uses: actions/upload-artifact@v4
id: upload-zip
if: ${{ inputs.upload_zip }}
with:
name: ${{ inputs.profile }}-binary-windows-zip
path: C:\\a\\servo\\servo\\target\\${{ inputs.profile }}\\msi\\Servo.zip
- name: Collect artifact IDs needed by the release workflow
id: artifact_ids
shell: bash
run: |
.\mach upload-nightly windows-msvc --secret-from-environment `
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds
ARTIFACT_IDS="${{steps.upload-exe.outputs.artifact-id}}"
if [[ -n "${{steps.upload-zip.outputs.artifact-id}}" ]]; then
ARTIFACT_IDS="${ARTIFACT_IDS},${{steps.upload-zip.outputs.artifact-id}}"
fi
echo "release_artifact_ids=${ARTIFACT_IDS}" >> $GITHUB_OUTPUT
bencher:
needs: ["build"]
if: ${{ inputs.bencher && inputs.profile != 'debug' && github.event_name != 'workflow_dispatch' && github.event_name != 'merge_group' }}

68
Cargo.lock generated
View File

@@ -1662,7 +1662,7 @@ dependencies = [
[[package]]
name = "content-security-policy"
version = "0.5.4"
source = "git+https://github.com/servo/rust-content-security-policy?branch=servo-csp#22b2831433f306ad22420dca4f5ff685b7fe4cc5"
source = "git+https://github.com/servo/rust-content-security-policy?rev=22b2831433f306ad22420dca4f5ff685b7fe4cc5#22b2831433f306ad22420dca4f5ff685b7fe4cc5"
dependencies = [
"base64 0.22.1",
"bitflags 2.10.0",
@@ -2931,7 +2931,7 @@ dependencies = [
[[package]]
name = "fontsan"
version = "0.6.1"
source = "git+https://github.com/servo/fontsan#ec58e75f566648a256a4fe7264ea6ffa2b83f201"
source = "git+https://github.com/servo/fontsan?rev=ec58e75f566648a256a4fe7264ea6ffa2b83f201#ec58e75f566648a256a4fe7264ea6ffa2b83f201"
dependencies = [
"cc",
"fontsan-woff2",
@@ -2943,7 +2943,7 @@ dependencies = [
[[package]]
name = "fontsan-woff2"
version = "0.1.1"
source = "git+https://github.com/servo/fontsan#ec58e75f566648a256a4fe7264ea6ffa2b83f201"
source = "git+https://github.com/servo/fontsan?rev=ec58e75f566648a256a4fe7264ea6ffa2b83f201#ec58e75f566648a256a4fe7264ea6ffa2b83f201"
dependencies = [
"brotli-decompressor",
"cc",
@@ -6632,7 +6632,7 @@ checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec"
[[package]]
name = "peek-poke"
version = "0.3.0"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"euclid",
"peek-poke-derive",
@@ -6641,7 +6641,7 @@ dependencies = [
[[package]]
name = "peek-poke-derive"
version = "0.3.0"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"proc-macro2",
"quote",
@@ -8010,7 +8010,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.33.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"bitflags 2.10.0",
"cssparser",
@@ -8130,7 +8130,7 @@ dependencies = [
[[package]]
name = "servo-media"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"once_cell",
"servo-media-audio",
@@ -8143,7 +8143,7 @@ dependencies = [
[[package]]
name = "servo-media-audio"
version = "0.2.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"byte-slice-cast",
"euclid",
@@ -8164,7 +8164,7 @@ dependencies = [
[[package]]
name = "servo-media-derive"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"proc-macro2",
"quote",
@@ -8174,7 +8174,7 @@ dependencies = [
[[package]]
name = "servo-media-dummy"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"ipc-channel",
"servo-media",
@@ -8188,7 +8188,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"byte-slice-cast",
"glib",
@@ -8221,7 +8221,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer-render"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"gstreamer",
"gstreamer-video",
@@ -8231,7 +8231,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer-render-android"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"glib",
"gstreamer",
@@ -8245,7 +8245,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer-render-unix"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"glib",
"gstreamer",
@@ -8259,7 +8259,7 @@ dependencies = [
[[package]]
name = "servo-media-player"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"ipc-channel",
"serde",
@@ -8271,7 +8271,7 @@ dependencies = [
[[package]]
name = "servo-media-streams"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"uuid",
]
@@ -8279,12 +8279,12 @@ dependencies = [
[[package]]
name = "servo-media-traits"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
[[package]]
name = "servo-media-webrtc"
version = "0.1.0"
source = "git+https://github.com/servo/media#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
source = "git+https://github.com/servo/media?rev=0c7edc70be3141067bdaf38e8dc8e1bf6180e644#0c7edc70be3141067bdaf38e8dc8e1bf6180e644"
dependencies = [
"log",
"servo-media-streams",
@@ -8317,7 +8317,7 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.4.3"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"serde",
"stable_deref_trait",
@@ -8863,7 +8863,7 @@ dependencies = [
[[package]]
name = "stylo"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"app_units",
"arrayvec",
@@ -8920,7 +8920,7 @@ dependencies = [
[[package]]
name = "stylo_atoms"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"string_cache",
"string_cache_codegen",
@@ -8929,12 +8929,12 @@ dependencies = [
[[package]]
name = "stylo_config"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
[[package]]
name = "stylo_derive"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"darling",
"proc-macro2",
@@ -8946,7 +8946,7 @@ dependencies = [
[[package]]
name = "stylo_dom"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"bitflags 2.10.0",
"stylo_malloc_size_of",
@@ -8955,7 +8955,7 @@ dependencies = [
[[package]]
name = "stylo_malloc_size_of"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"app_units",
"cssparser",
@@ -8972,12 +8972,12 @@ dependencies = [
[[package]]
name = "stylo_static_prefs"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
[[package]]
name = "stylo_traits"
version = "0.9.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"app_units",
"bitflags 2.10.0",
@@ -9390,7 +9390,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "to_shmem"
version = "0.3.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"cssparser",
"servo_arc",
@@ -9403,7 +9403,7 @@ dependencies = [
[[package]]
name = "to_shmem_derive"
version = "0.1.0"
source = "git+https://github.com/servo/stylo?branch=2025-11-01#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
source = "git+https://github.com/servo/stylo?rev=ee0b989dbe43a8e2e98fdb1be791cf22d1bac045#ee0b989dbe43a8e2e98fdb1be791cf22d1bac045"
dependencies = [
"darling",
"proc-macro2",
@@ -10434,7 +10434,7 @@ dependencies = [
[[package]]
name = "webrender"
version = "0.68.0"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"allocator-api2",
"bincode",
@@ -10470,7 +10470,7 @@ dependencies = [
[[package]]
name = "webrender_api"
version = "0.68.0"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"app_units",
"bitflags 2.10.0",
@@ -10489,7 +10489,7 @@ dependencies = [
[[package]]
name = "webrender_build"
version = "0.0.2"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"bitflags 2.10.0",
"lazy_static",
@@ -11203,7 +11203,7 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "wr_glyph_rasterizer"
version = "0.1.0"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"core-foundation 0.9.4",
"core-graphics",
@@ -11228,7 +11228,7 @@ dependencies = [
[[package]]
name = "wr_malloc_size_of"
version = "0.2.2"
source = "git+https://github.com/servo/webrender?branch=0.68#6cafc606096db4715a6119a6e16391aed9af47a5"
source = "git+https://github.com/servo/webrender?rev=6cafc606096db4715a6119a6e16391aed9af47a5#6cafc606096db4715a6119a6e16391aed9af47a5"
dependencies = [
"app_units",
"euclid",

View File

@@ -59,7 +59,7 @@ chrono = { version = "0.4", features = ["serde"] }
cipher = { version = "0.4.4", features = ["alloc"] }
compositing_traits = { path = "components/shared/compositing" }
constellation_traits = { path = "components/shared/constellation" }
content-security-policy = { git = "https://github.com/servo/rust-content-security-policy", branch = "servo-csp", features = ["serde"] }
content-security-policy = { git = "https://github.com/servo/rust-content-security-policy", rev = "22b2831433f306ad22420dca4f5ff685b7fe4cc5", features = ["serde"] }
cookie = { package = "cookie", version = "0.18" }
crossbeam-channel = "0.5"
cssparser = { version = "0.36", features = ["serde"] }
@@ -153,16 +153,16 @@ script_traits = { path = "components/shared/script" }
sea-query = { version = "1.0.0-rc.29", default-features = false, features = ["backend-sqlite", "derive"] }
sea-query-rusqlite = { version = "0.8.0-rc.15" }
sec1 = "0.7"
selectors = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
selectors = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
serde = "1.0.228"
serde_bytes = "0.11"
serde_core = "1.0.226"
serde_json = "1.0"
servo-media = { git = "https://github.com/servo/media" }
servo-media-dummy = { git = "https://github.com/servo/media" }
servo-media-gstreamer = { git = "https://github.com/servo/media" }
servo-media = { git = "https://github.com/servo/media", rev = "0c7edc70be3141067bdaf38e8dc8e1bf6180e644" }
servo-media-dummy = { git = "https://github.com/servo/media", rev = "0c7edc70be3141067bdaf38e8dc8e1bf6180e644" }
servo-media-gstreamer = { git = "https://github.com/servo/media", rev = "0c7edc70be3141067bdaf38e8dc8e1bf6180e644" }
servo-tracing = { path = "components/servo_tracing" }
servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
servo_arc = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
sha1 = "0.10"
sha2 = "0.10"
sha3 = "0.10"
@@ -171,12 +171,12 @@ smallvec = { version = "1.15", features = ["serde", "union"] }
storage_traits = { path = "components/shared/storage" }
string_cache = "0.9"
strum = { version = "0.27", features = ["derive"] }
stylo = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-11-01" }
stylo = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
stylo_atoms = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
stylo_config = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
stylo_dom = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
stylo_traits = { git = "https://github.com/servo/stylo", rev = "ee0b989dbe43a8e2e98fdb1be791cf22d1bac045" }
surfman = { version = "0.11.0", features = ["chains"] }
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
synstructure = "0.13"
@@ -205,8 +205,8 @@ vello_cpu = "0.0.4"
webdriver = "0.53.0"
webgpu_traits = { path = "components/shared/webgpu" }
webpki-roots = "1.0"
webrender = { git = "https://github.com/servo/webrender", branch = "0.68", features = ["capture"] }
webrender_api = { git = "https://github.com/servo/webrender", branch = "0.68" }
webrender = { git = "https://github.com/servo/webrender", rev = "6cafc606096db4715a6119a6e16391aed9af47a5", features = ["capture"] }
webrender_api = { git = "https://github.com/servo/webrender", rev = "6cafc606096db4715a6119a6e16391aed9af47a5" }
webxr-api = { path = "components/shared/webxr" }
wgpu-core = "26"
wgpu-types = "26"
@@ -214,7 +214,7 @@ winapi = "0.3"
windows-sys = "0.61"
winit = "0.30.12"
wio = "0.2"
wr_malloc_size_of = { git = "https://github.com/servo/webrender", branch = "0.68" }
wr_malloc_size_of = { git = "https://github.com/servo/webrender", rev = "6cafc606096db4715a6119a6e16391aed9af47a5" }
x25519-dalek = { version = "2.0.1", features = ["static_secrets"] }
xi-unicode = "0.3.0"
xml5ever = "0.36.1"

View File

@@ -24,7 +24,7 @@ compositing_traits = { workspace = true }
content-security-policy = { workspace = true }
euclid = { workspace = true }
fonts_traits = { workspace = true }
fontsan = { git = "https://github.com/servo/fontsan" }
fontsan = { git = "https://github.com/servo/fontsan", rev = "ec58e75f566648a256a4fe7264ea6ffa2b83f201" }
# FIXME (#34517): macOS only needs this when building libservo without `--features media-gstreamer`
harfbuzz-sys = { workspace = true, features = ["bundled"] }
ipc-channel = { workspace = true }

179
etc/ci/upload_nightly.py Executable file
View File

@@ -0,0 +1,179 @@
#!/usr/bin/env -S uv run --script
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Note: We use `uv lock --script upload_nightly.py` to lock dependency versions in a lockfile.
# /// script
# requires-python = ">=3.11"
# dependencies = ["boto3", "PyGithub"]
# ///
import argparse
import hashlib
import io
import json
import os
from os import path
from datetime import datetime
from typing import List, Optional
import sys
from github import Github, Auth
import boto3
def get_s3_secret(secret_from_environment: bool) -> tuple:
aws_access_key = None
aws_secret_access_key = None
if secret_from_environment:
secret = json.loads(os.environ["S3_UPLOAD_CREDENTIALS"])
aws_access_key = secret["aws_access_key_id"]
aws_secret_access_key = secret["aws_secret_access_key"]
return (aws_access_key, aws_secret_access_key)
def nightly_filename(package: str, timestamp: datetime) -> str:
return "{}-{}".format(
timestamp.isoformat() + "Z", # The `Z` denotes UTC
path.basename(package),
)
# Map the default platform shorthand to a name containing architecture.
def map_platform(platform: str) -> str:
if platform == "android":
return "aarch64-android"
elif platform == "linux":
return "x86_64-linux-gnu"
elif platform == "windows-msvc":
return "x86_64-windows-msvc"
elif platform == "mac":
return "x86_64-apple-darwin"
elif platform == "mac-arm64":
return "aarch64-apple-darwin"
elif platform == "ohos":
return "aarch64-linux-ohos"
raise Exception("Unknown platform: {}".format(platform))
def upload_to_github_release(platform: str, package: str, package_hash: str, github_release_id: Optional[int]) -> None:
if not github_release_id:
return
extension = path.basename(package).partition(".")[2]
auth = Auth.Token(os.environ["RELEASE_REPO_TOKEN"])
g = Github(auth=auth)
nightly_repo = g.get_repo(os.environ["RELEASE_REPO"])
release = nightly_repo.get_release(github_release_id)
if platform != "mac-arm64":
# Legacy assetname. Will be removed after a period with duplicate assets.
asset_name = f"servo-latest.{extension}"
package_hash_fileobj = io.BytesIO(f"{package_hash} {asset_name}".encode("utf-8"))
release.upload_asset(package, name=asset_name)
# pyrefly: ignore[missing-attribute]
release.upload_asset_from_memory(
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
)
asset_platform = map_platform(platform)
asset_name = f"servo-{asset_platform}.{extension}"
package_hash_fileobj = io.BytesIO(f"{package_hash} {asset_name}".encode("utf-8"))
release.upload_asset(package, name=asset_name)
# pyrefly: ignore[missing-attribute]
release.upload_asset_from_memory(
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
)
def upload_to_s3(
platform: str, package: str, package_hash: str, timestamp: datetime, secret_from_environment: bool
) -> None:
(aws_access_key, aws_secret_access_key) = get_s3_secret(secret_from_environment)
s3 = boto3.client("s3", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key)
cloudfront = boto3.client(
"cloudfront", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key
)
BUCKET = "servo-builds2"
DISTRIBUTION_ID = "EJ8ZWSJKFCJS2"
nightly_dir = f"nightly/{platform}"
filename = nightly_filename(package, timestamp)
package_upload_key = "{}/{}".format(nightly_dir, filename)
extension = path.basename(package).partition(".")[2]
latest_upload_key = "{}/servo-latest.{}".format(nightly_dir, extension)
package_hash_fileobj = io.BytesIO(f"{package_hash} {filename}".encode("utf-8"))
latest_hash_upload_key = f"{latest_upload_key}.sha256"
s3.upload_file(package, BUCKET, package_upload_key)
copy_source = {
"Bucket": BUCKET,
"Key": package_upload_key,
}
s3.copy(copy_source, BUCKET, latest_upload_key)
s3.upload_fileobj(package_hash_fileobj, BUCKET, latest_hash_upload_key, ExtraArgs={"ContentType": "text/plain"})
# Invalidate previous "latest" nightly files from
# CloudFront edge caches
cloudfront.create_invalidation(
DistributionId=DISTRIBUTION_ID,
InvalidationBatch={
"CallerReference": f"{latest_upload_key}-{timestamp}",
"Paths": {"Quantity": 1, "Items": [f"/{latest_upload_key}*"]},
},
)
def upload_nightly(
platform: str, secret_from_environment: bool, github_release_id: int | None, packages: List[str]
) -> int:
timestamp = datetime.utcnow().replace(microsecond=0)
for package in packages:
# TODO: This if feels like it should not be necessary. Let's add a warning, and make this an
# error later.
if path.isdir(package):
print("Warning: Skipping directory: {}".format(package), file=sys.stderr)
continue
if not path.isfile(package):
print("Could not find package for {} at {}".format(platform, package), file=sys.stderr)
return 1
# Compute the hash
SHA_BUF_SIZE = 1048576 # read in 1 MiB chunks
sha256_digest = hashlib.sha256()
with open(package, "rb") as package_file:
while True:
data = package_file.read(SHA_BUF_SIZE)
if not data:
break
sha256_digest.update(data)
package_hash = sha256_digest.hexdigest()
upload_to_s3(platform, package, package_hash, timestamp, secret_from_environment)
upload_to_github_release(platform, package, package_hash, github_release_id)
return 0
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Upload Servo nightly to Github Releases and S3")
parser.add_argument("platform", help="Package platform type to upload")
parser.add_argument(
"--secret-from-environment", action="store_true", help="Retrieve the appropriate secrets from the environment."
)
parser.add_argument(
"--github-release-id", default=None, type=int, help="The github release to upload the nightly builds."
)
parser.add_argument("packages", nargs="+", help="The packages to upload.")
args = parser.parse_args()
upload_nightly(
platform=args.platform,
secret_from_environment=args.secret_from_environment,
github_release_id=args.github_release_id,
packages=args.packages,
)

View File

@@ -0,0 +1,409 @@
version = 1
revision = 3
requires-python = ">=3.11"
[manifest]
requirements = [
{ name = "boto3" },
{ name = "pygithub" },
]
[[package]]
name = "boto3"
version = "1.42.28"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
{ name = "jmespath" },
{ name = "s3transfer" },
]
sdist = { url = "https://files.pythonhosted.org/packages/83/aa/a44ea8c8ee8239f3f7c32cce966512c846297df5fe48b56db6882f3b7ca0/boto3-1.42.28.tar.gz", hash = "sha256:7d56c298b8d98f5e9b04cf5d6627f68e7792e25614533aef17f815681b5e1096", size = 112846, upload-time = "2026-01-14T20:37:21.448Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/35/5d95169ed145f0c49ebfeb6a5228ab63d54e95a2c7a43f0f0eb893540660/boto3-1.42.28-py3-none-any.whl", hash = "sha256:7994bc2a094c1894f6a4221a1696c5d18af6c9c888191051866f1d05c4fba431", size = 140575, upload-time = "2026-01-14T20:37:20.098Z" },
]
[[package]]
name = "botocore"
version = "1.42.28"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "python-dateutil" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/85/8d/e0828726aa568e5ab0ec477c7a47a82aa37f00951858d9ad892b6b1d5e32/botocore-1.42.28.tar.gz", hash = "sha256:0c15e78d1accf97df691083331f682e97b1bef73ef12dcdaadcf652abf9c182c", size = 14886029, upload-time = "2026-01-14T20:37:11.137Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/ff/72470b92ba96868be1936b8b3c7a70f902b60d36268bdeddb732317bef7a/botocore-1.42.28-py3-none-any.whl", hash = "sha256:d26c7a0851489ce1a18279f9802fe434bd736ea861d4888cc2c7d83fb1f6af8f", size = 14559264, upload-time = "2026-01-14T20:37:08.184Z" },
]
[[package]]
name = "certifi"
version = "2026.1.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
]
[[package]]
name = "cffi"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
{ url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
{ url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
{ url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
{ url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
{ url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
{ url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
{ url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
{ url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
{ url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
{ url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
{ url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
{ url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
{ url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
{ url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
{ url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
{ url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
{ url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
{ url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
{ url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
{ url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
{ url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
{ url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
{ url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
{ url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
{ url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
{ url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
{ url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
{ url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
{ url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
{ url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
{ url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
{ url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
{ url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
{ url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
{ url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
{ url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
{ url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
{ url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
{ url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
{ url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
]
[[package]]
name = "cryptography"
version = "46.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
{ url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
{ url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
{ url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
{ url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
{ url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
{ url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
{ url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
{ url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
{ url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
{ url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
{ url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
{ url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" },
{ url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" },
{ url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" },
{ url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
{ url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
{ url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
{ url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
{ url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
{ url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
{ url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
{ url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
{ url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
{ url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
{ url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
{ url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
{ url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" },
{ url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" },
{ url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" },
{ url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
{ url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
{ url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
{ url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
{ url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
{ url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
{ url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
{ url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
{ url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
{ url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
{ url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
{ url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
{ url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" },
{ url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" },
{ url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
{ url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" },
{ url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" },
{ url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" },
{ url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" },
{ url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" },
{ url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" },
]
[[package]]
name = "idna"
version = "3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
name = "jmespath"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" },
]
[[package]]
name = "pycparser"
version = "2.23"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
]
[[package]]
name = "pygithub"
version = "2.8.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyjwt", extra = ["crypto"] },
{ name = "pynacl" },
{ name = "requests" },
{ name = "typing-extensions" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c1/74/e560bdeffea72ecb26cff27f0fad548bbff5ecc51d6a155311ea7f9e4c4c/pygithub-2.8.1.tar.gz", hash = "sha256:341b7c78521cb07324ff670afd1baa2bf5c286f8d9fd302c1798ba594a5400c9", size = 2246994, upload-time = "2025-09-02T17:41:54.674Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/ba/7049ce39f653f6140aac4beb53a5aaf08b4407b6a3019aae394c1c5244ff/pygithub-2.8.1-py3-none-any.whl", hash = "sha256:23a0a5bca93baef082e03411bf0ce27204c32be8bfa7abc92fe4a3e132936df0", size = 432709, upload-time = "2025-09-02T17:41:52.947Z" },
]
[[package]]
name = "pyjwt"
version = "2.10.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
]
[package.optional-dependencies]
crypto = [
{ name = "cryptography" },
]
[[package]]
name = "pynacl"
version = "1.6.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4b/79/0e3c34dc3c4671f67d251c07aa8eb100916f250ee470df230b0ab89551b4/pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl", hash = "sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594", size = 390064, upload-time = "2026-01-01T17:31:57.264Z" },
{ url = "https://files.pythonhosted.org/packages/eb/1c/23a26e931736e13b16483795c8a6b2f641bf6a3d5238c22b070a5112722c/pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0", size = 809370, upload-time = "2026-01-01T17:31:59.198Z" },
{ url = "https://files.pythonhosted.org/packages/87/74/8d4b718f8a22aea9e8dcc8b95deb76d4aae380e2f5b570cc70b5fd0a852d/pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9", size = 1408304, upload-time = "2026-01-01T17:32:01.162Z" },
{ url = "https://files.pythonhosted.org/packages/fd/73/be4fdd3a6a87fe8a4553380c2b47fbd1f7f58292eb820902f5c8ac7de7b0/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574", size = 844871, upload-time = "2026-01-01T17:32:02.824Z" },
{ url = "https://files.pythonhosted.org/packages/55/ad/6efc57ab75ee4422e96b5f2697d51bbcf6cdcc091e66310df91fbdc144a8/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634", size = 1446356, upload-time = "2026-01-01T17:32:04.452Z" },
{ url = "https://files.pythonhosted.org/packages/78/b7/928ee9c4779caa0a915844311ab9fb5f99585621c5d6e4574538a17dca07/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88", size = 826814, upload-time = "2026-01-01T17:32:06.078Z" },
{ url = "https://files.pythonhosted.org/packages/f7/a9/1bdba746a2be20f8809fee75c10e3159d75864ef69c6b0dd168fc60e485d/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14", size = 1411742, upload-time = "2026-01-01T17:32:07.651Z" },
{ url = "https://files.pythonhosted.org/packages/f3/2f/5e7ea8d85f9f3ea5b6b87db1d8388daa3587eed181bdeb0306816fdbbe79/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444", size = 801714, upload-time = "2026-01-01T17:32:09.558Z" },
{ url = "https://files.pythonhosted.org/packages/06/ea/43fe2f7eab5f200e40fb10d305bf6f87ea31b3bbc83443eac37cd34a9e1e/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b", size = 1372257, upload-time = "2026-01-01T17:32:11.026Z" },
{ url = "https://files.pythonhosted.org/packages/4d/54/c9ea116412788629b1347e415f72195c25eb2f3809b2d3e7b25f5c79f13a/pynacl-1.6.2-cp314-cp314t-win32.whl", hash = "sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145", size = 231319, upload-time = "2026-01-01T17:32:12.46Z" },
{ url = "https://files.pythonhosted.org/packages/ce/04/64e9d76646abac2dccf904fccba352a86e7d172647557f35b9fe2a5ee4a1/pynacl-1.6.2-cp314-cp314t-win_amd64.whl", hash = "sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590", size = 244044, upload-time = "2026-01-01T17:32:13.781Z" },
{ url = "https://files.pythonhosted.org/packages/33/33/7873dc161c6a06f43cda13dec67b6fe152cb2f982581151956fa5e5cdb47/pynacl-1.6.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2", size = 188740, upload-time = "2026-01-01T17:32:15.083Z" },
{ url = "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", size = 388458, upload-time = "2026-01-01T17:32:16.829Z" },
{ url = "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", size = 800020, upload-time = "2026-01-01T17:32:18.34Z" },
{ url = "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", size = 1399174, upload-time = "2026-01-01T17:32:20.239Z" },
{ url = "https://files.pythonhosted.org/packages/68/f7/322f2f9915c4ef27d140101dd0ed26b479f7e6f5f183590fd32dfc48c4d3/pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87", size = 835085, upload-time = "2026-01-01T17:32:22.24Z" },
{ url = "https://files.pythonhosted.org/packages/3e/d0/f301f83ac8dbe53442c5a43f6a39016f94f754d7a9815a875b65e218a307/pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c", size = 1437614, upload-time = "2026-01-01T17:32:23.766Z" },
{ url = "https://files.pythonhosted.org/packages/c4/58/fc6e649762b029315325ace1a8c6be66125e42f67416d3dbd47b69563d61/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130", size = 818251, upload-time = "2026-01-01T17:32:25.69Z" },
{ url = "https://files.pythonhosted.org/packages/c9/a8/b917096b1accc9acd878819a49d3d84875731a41eb665f6ebc826b1af99e/pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6", size = 1402859, upload-time = "2026-01-01T17:32:27.215Z" },
{ url = "https://files.pythonhosted.org/packages/85/42/fe60b5f4473e12c72f977548e4028156f4d340b884c635ec6b063fe7e9a5/pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e", size = 791926, upload-time = "2026-01-01T17:32:29.314Z" },
{ url = "https://files.pythonhosted.org/packages/fa/f9/e40e318c604259301cc091a2a63f237d9e7b424c4851cafaea4ea7c4834e/pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577", size = 1363101, upload-time = "2026-01-01T17:32:31.263Z" },
{ url = "https://files.pythonhosted.org/packages/48/47/e761c254f410c023a469284a9bc210933e18588ca87706ae93002c05114c/pynacl-1.6.2-cp38-abi3-win32.whl", hash = "sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa", size = 227421, upload-time = "2026-01-01T17:32:33.076Z" },
{ url = "https://files.pythonhosted.org/packages/41/ad/334600e8cacc7d86587fe5f565480fde569dfb487389c8e1be56ac21d8ac/pynacl-1.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0", size = 239754, upload-time = "2026-01-01T17:32:34.557Z" },
{ url = "https://files.pythonhosted.org/packages/29/7d/5945b5af29534641820d3bd7b00962abbbdfee84ec7e19f0d5b3175f9a31/pynacl-1.6.2-cp38-abi3-win_arm64.whl", hash = "sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c", size = 184801, upload-time = "2026-01-01T17:32:36.309Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "requests"
version = "2.32.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
name = "s3transfer"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
]
sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "urllib3"
version = "2.6.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
]

View File

@@ -17,11 +17,6 @@ ply == 3.8
# For Cross-platform colored terminal text
colorama == 0.3.7
# For package uploading
boto3 == 1.34.95
pyOpenSSL == 24.0.0
PyGithub == 1.58.1
# For Python3 compatibility
six == 1.16

View File

@@ -7,15 +7,10 @@
# option. This file may not be copied, modified, or distributed
# except according to those terms.
from datetime import datetime
import random
import time
from collections.abc import Generator
from github import Github
from typing import List
import hashlib
import io
import json
import time
import re
import os
import os.path as path
@@ -40,43 +35,11 @@ from servo.command_base import (
CommandBase,
is_windows,
)
from servo.util import delete, get_target_dir
from servo.util import delete
from python.servo.platform.build_target import SanitizerKind
from servo.platform.build_target import is_android, is_openharmony
PACKAGES = {
"android": [
"android/aarch64-linux-android/release/servoapp.apk",
"android/aarch64-linux-android/release/servoview.aar",
],
"linux": [
"production/servo-tech-demo.tar.gz",
],
"mac": [
"production/servo-tech-demo.dmg",
],
"mac-arm64": [
"production/servo-tech-demo.dmg",
],
"windows-msvc": [
r"production\msi\Servo.exe",
r"production\msi\Servo.zip",
],
"ohos": [
"aarch64-unknown-linux-ohos/production/libservoshell.so",
"openharmony/aarch64-unknown-linux-ohos/production/entry/build/"
+ "default/outputs/default/servoshell-default-signed.hap",
],
}
def packages_for_platform(platform: str) -> Generator[str]:
target_dir = get_target_dir()
for package in PACKAGES[platform]:
yield path.join(target_dir, package)
def listfiles(directory: str) -> list[str]:
return [f for f in os.listdir(directory) if path.isfile(path.join(directory, f))]
@@ -455,138 +418,19 @@ class PackageCommands(CommandBase):
return subprocess.call(exec_command, env=env)
@Command("upload-nightly", description="Upload Servo nightly to S3", category="package")
@CommandArgument("platform", choices=PACKAGES.keys(), help="Package platform type to upload")
@CommandArgument("platform", help="Package platform type to upload")
@CommandArgument(
"--secret-from-environment", action="store_true", help="Retrieve the appropriate secrets from the environment."
)
@CommandArgument(
"--github-release-id", default=None, type=int, help="The github release to upload the nightly builds."
)
def upload_nightly(self, platform: str, secret_from_environment: bool, github_release_id: int | None) -> int:
import boto3
def get_s3_secret() -> tuple:
aws_access_key = None
aws_secret_access_key = None
if secret_from_environment:
secret = json.loads(os.environ["S3_UPLOAD_CREDENTIALS"])
aws_access_key = secret["aws_access_key_id"]
aws_secret_access_key = secret["aws_secret_access_key"]
return (aws_access_key, aws_secret_access_key)
def nightly_filename(package: str, timestamp: datetime) -> str:
return "{}-{}".format(
timestamp.isoformat() + "Z", # The `Z` denotes UTC
path.basename(package),
)
# Map the default platform shorthand to a name containing architecture.
def map_platform(platform: str) -> str:
if platform == "android":
return "aarch64-android"
elif platform == "linux":
return "x86_64-linux-gnu"
elif platform == "windows-msvc":
return "x86_64-windows-msvc"
elif platform == "mac":
return "x86_64-apple-darwin"
elif platform == "mac-arm64":
return "aarch64-apple-darwin"
elif platform == "ohos":
return "aarch64-linux-ohos"
raise Exception("Unknown platform: {}".format(platform))
def upload_to_github_release(platform: str, package: str, package_hash: str) -> None:
if not github_release_id:
return
extension = path.basename(package).partition(".")[2]
g = Github(os.environ["NIGHTLY_REPO_TOKEN"])
nightly_repo = g.get_repo(os.environ["NIGHTLY_REPO"])
release = nightly_repo.get_release(github_release_id)
if platform != "mac-arm64":
# Legacy assetname. Will be removed after a period with duplicate assets.
asset_name = f"servo-latest.{extension}"
package_hash_fileobj = io.BytesIO(f"{package_hash} {asset_name}".encode("utf-8"))
release.upload_asset(package, name=asset_name)
# pyrefly: ignore[missing-attribute]
release.upload_asset_from_memory(
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
)
asset_platform = map_platform(platform)
asset_name = f"servo-{asset_platform}.{extension}"
package_hash_fileobj = io.BytesIO(f"{package_hash} {asset_name}".encode("utf-8"))
release.upload_asset(package, name=asset_name)
# pyrefly: ignore[missing-attribute]
release.upload_asset_from_memory(
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
)
def upload_to_s3(platform: str, package: str, package_hash: str, timestamp: datetime) -> None:
(aws_access_key, aws_secret_access_key) = get_s3_secret()
s3 = boto3.client("s3", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key)
cloudfront = boto3.client(
"cloudfront", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key
)
BUCKET = "servo-builds2"
DISTRIBUTION_ID = "EJ8ZWSJKFCJS2"
nightly_dir = f"nightly/{platform}"
filename = nightly_filename(package, timestamp)
package_upload_key = "{}/{}".format(nightly_dir, filename)
extension = path.basename(package).partition(".")[2]
latest_upload_key = "{}/servo-latest.{}".format(nightly_dir, extension)
package_hash_fileobj = io.BytesIO(f"{package_hash} {filename}".encode("utf-8"))
latest_hash_upload_key = f"{latest_upload_key}.sha256"
s3.upload_file(package, BUCKET, package_upload_key)
copy_source = {
"Bucket": BUCKET,
"Key": package_upload_key,
}
s3.copy(copy_source, BUCKET, latest_upload_key)
s3.upload_fileobj(
package_hash_fileobj, BUCKET, latest_hash_upload_key, ExtraArgs={"ContentType": "text/plain"}
)
# Invalidate previous "latest" nightly files from
# CloudFront edge caches
cloudfront.create_invalidation(
DistributionId=DISTRIBUTION_ID,
InvalidationBatch={
"CallerReference": f"{latest_upload_key}-{timestamp}",
"Paths": {"Quantity": 1, "Items": [f"/{latest_upload_key}*"]},
},
)
timestamp = datetime.utcnow().replace(microsecond=0)
for package in packages_for_platform(platform):
if path.isdir(package):
continue
if not path.isfile(package):
print("Could not find package for {} at {}".format(platform, package), file=sys.stderr)
return 1
# Compute the hash
SHA_BUF_SIZE = 1048576 # read in 1 MiB chunks
sha256_digest = hashlib.sha256()
with open(package, "rb") as package_file:
while True:
data = package_file.read(SHA_BUF_SIZE)
if not data:
break
sha256_digest.update(data)
package_hash = sha256_digest.hexdigest()
upload_to_s3(platform, package, package_hash, timestamp)
upload_to_github_release(platform, package, package_hash)
return 0
@CommandArgument("packages", nargs="+", help="The packages to upload.")
def upload_nightly(
self, platform: str, secret_from_environment: bool, github_release_id: int | None, packages: List[str]
) -> int:
print("Error: This command was moved to a dedicated script: etc/ci/upload_nightly.py", file=sys.stderr)
return 1
@Command(
"release", description="Perform necessary updates before release a new servoshell version", category="package"

View File

@@ -62,8 +62,8 @@ public class JNIServo {
public native void click(float x, float y);
public native void pauseCompositor();
public native void resumeCompositor(Surface surface, ServoCoordinates coords);
public native void pausePainting();
public native void resumePainting(Surface surface, ServoCoordinates coords);
public native void mediaSessionAction(int action);

View File

@@ -121,11 +121,11 @@ public class Servo {
mRunCallback.inGLThread(() -> mJNI.click(x, y));
}
public void pauseCompositor() {
mRunCallback.inGLThread(() -> mJNI.pauseCompositor());
public void pausePainting() {
mRunCallback.inGLThread(() -> mJNI.pausePainting());
}
public void resumeCompositor(Surface surface, ServoCoordinates coords) {
mRunCallback.inGLThread(() -> mJNI.resumeCompositor(surface, coords));
public void resumePainting(Surface surface, ServoCoordinates coords) {
mRunCallback.inGLThread(() -> mJNI.resumePainting(surface, coords));
}
public void suspend(boolean suspended) {

View File

@@ -241,7 +241,7 @@ public class ServoView extends SurfaceView
options, mServoView, mServoView, mClient, mActivity, surface);
} else {
mPaused = false;
mServoView.mServo.resumeCompositor(surface, coords);
mServoView.mServo.resumePainting(surface, coords);
}
Choreographer.getInstance().postFrameCallback(mServoView);
@@ -259,7 +259,7 @@ public class ServoView extends SurfaceView
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(LOGTAG, "GLThread::surfaceDestroyed");
mPaused = true;
mServoView.mServo.pauseCompositor();
mServoView.mServo.pausePainting();
}
public void shutdown() {