Compare commits

...

8 Commits

Author SHA1 Message Date
Jonathan Schwender
8032867a0d backport: ci: Secure release workflow using environment (#44108) (#44118)
This applies changes from #44108 to the release/v0.1 branch. To secure
our workflow secrets, we will be removing the global secrets, hence
backporting this is required in order to support future release workflow
usage.

-------------------------------

The publish environment enforces additional restrictions, mainly that
the workflow can only be run from protected branches and additional
prevents access of the publish secrets from workflows which do not use
this environment.

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-04-11 14:08:22 +02:00
Jonathan Schwender
4e9182a05a backport: CI: Simplify release workflow (#44085) (#44112)
This applies #44085 to the release/v0.1 branch, which is required to
backport #44108, which is necessary in order to run future release
workflows, after we have removed the secrets from the regular
environment.

Using the new yaml anchor feature, we can inline the upload_release
workflow file without duplicating code.
In combination with the matrix feature this allows us to remove quite a
bit of duplication. I'm not sure why we didn't use matrix to begin with.

This is also a preparation for a follow-up PR, that uses github
environments to improve secret protection, since reusable workflows
don't support environments.

Testing: [manually triggered nightly
release](https://github.com/servo/servo/actions/runs/24236862409)


(cherry picked from commit f59491d36f)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-04-11 11:58:15 +02:00
Jonathan Schwender
0026e003ad backport: deps: Fix sea-query pre-release versions (#44093) (#44104)
This applies #44093 to the release/v0.1 branch. 

This prevents issues for users that don't already have a lockfile, since
prerelease versions can have breaking changes.

Testing: No changes, covered by existing tests.
Fixes: #44089


(cherry picked from commit 34337273a3)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-04-11 08:12:27 +02:00
Jonathan Schwender
b87c7501de backport: release: Add job to publish to crates.io (#43972) (#44096)
This applies #43972 to the release/v0.1 branch in preparation for the
release.

When triggering the release action on a non-protected branch in this
repo, the job is rejected (as intended):
<img width="1484" height="304" alt="image"

src="https://github.com/user-attachments/assets/236d3a41-2765-4652-8709-93110e03c77b"
/>

When triggering the action on a protected branch in this repository, the
publish-crates-io job will be pending, until explicitly approved by one
of the required approvers (thanks to the `environment` settings). This
allows us to publish all of our packages in one go.

Testing: Tested by manually

[triggering](https://github.com/servo/servo/actions/runs/24119955943/job/70371705395)
a release for `0.1.0-rc2`, which got successfully published to
crates.io. This was also a resume-after-cancellation test, since the
first ~30 crates of the release had already been published via `cargo
publish --workspace`, before running into the issue that `cargo publish
--workspace` can't resume after intermediate failures. The last commit
"Fix buffering issue in CI" is untested, and was added after observing
the stdout log messages only appearing at the end of the script. That
commit is trivial though, and probably does not justify using crates.io
resources for another test release.

---------




(cherry picked from commit c3d2df22c0)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Mukilan Thiyagarajan <mukilanthiagarajan@gmail.com>
2026-04-10 20:47:50 +02:00
Jonathan Schwender
52507c53a3 backport: release: Fix result check for cancelled workflows (#44017) (#44019)
This applies the changes from #44017 to the release/v0.1 branch, to
improve the action reliability before the upcoming release.

-------

If the entire workflow was cancelled we also need to check for
`cancelled()`. Simply checking needs.*.result is not sufficient - it was
observed that the success branch was still entered when only checking
needs.

Testing: Tested manually, by cancelling [this

workflow](https://github.com/servo/servo/actions/runs/24119740924/job/70371050119)
which resulted in a draft release publish (failure branch)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Mukilan Thiyagarajan <mukilanthiagarajan@gmail.com>
2026-04-08 10:08:49 +02:00
Jonathan Schwender
75fee3c0ac servo-hyper-serde: Use the workspace version (#43869)
This is a backport of #43866 to the release/v0.1 branch.

`hyper_serde` currently has no dependants on crates.io. We give up the
independant versioning and simplify our setup by using the same version
as our workspace. The new crate is servo-hyper-serde.

Testing: Not required, policy change.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-04-02 16:53:53 +02:00
Jonathan Schwender
94b9fa4988 stylo: Use published crates.io versions
This is necessary to publish to crates.io.

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-04-01 18:03:08 +02:00
Jonathan Schwender
38513c8f2d release: Allow publishing
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2026-04-01 14:38:52 +02:00
7 changed files with 389 additions and 337 deletions

View File

@@ -13,6 +13,10 @@ on:
description: '`true` to create a release on this repo, false to release to the nightly-releases repo'
type: boolean
default: false
crates_io:
description: '`true` to publish to crates.io'
type: boolean
default: false
release_tag:
required: true
type: string
@@ -28,6 +32,9 @@ jobs:
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Create Draft GH Release
runs-on: ubuntu-latest
environment: &publish-environment
name: publish
deployment: false
steps:
- id: create-release
run: |
@@ -66,9 +73,10 @@ jobs:
&& (inputs.regular_release || false) == false
name: Publish GH Release
runs-on: ubuntu-latest
environment: *publish-environment
steps:
- name: Publish as latest (success)
if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
if: ${{ !contains(needs.*.result, 'failure') && (!contains(needs.*.result, 'cancelled') && !cancelled()) }}
run: |
gh api \
--method PATCH \
@@ -77,7 +85,7 @@ jobs:
/repos/${RELEASE_REPO}/releases/${RELEASE_ID} \
-F draft=false
- name: Publish as latest (failure)
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
if: ${{ contains(needs.*.result, 'failure') || (contains(needs.*.result, 'cancelled') || cancelled()) }}
run: |
gh api \
--method PATCH \
@@ -91,12 +99,33 @@ jobs:
needs:
- create-draft-release
- upload-vendored-source
- upload-linux-nightly
- upload-win-nightly
- upload-mac-nightly
- upload-mac-arm64-nightly
- upload-android-nightly
- upload-ohos-nightly
- upload-artifacts-nightly
- upload-artifacts-release
publish-crates-io:
name: 'Publish to crates.io'
if: github.repository == 'servo/servo' && (inputs.crates_io || false)
environment:
name: publish_crates_io
deployment: false
runs-on: ubuntu-22.04
permissions:
id-token: write
steps:
- uses: actions/checkout@v6
- uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
SERVO_CRATES_IO_SLEEP_AFTER_PUBLISH_SECONDS: "30"
SERVO_CRATES_IO_VERIFY_PUBLISHED_TIMEOUT_SECONDS: "300"
SERVO_CRATES_IO_VERIFY_PUBLISHED_INTERVAL_SECONDS: "10"
# Verification requires building, which is incredibly slow and also increases our attack surface.
# If we decide for an extra verification, we should add a seperate job before this one, which
# does a `dry-run` publish without any elevated permissions.
run: |
python3 etc/ci/publish_crates_io.py --no-verify
build-win:
# This job is only useful when run on upstream servo.
@@ -113,6 +142,7 @@ jobs:
if: github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch'
name: Upload vendored source archive
runs-on: ubuntu-latest
environment: *publish-environment
permissions:
contents: write
id-token: write
@@ -141,45 +171,100 @@ jobs:
env:
GITHUB_TOKEN: ${{ inputs.regular_release && github.token || secrets.NIGHTLY_REPO_TOKEN }}
upload-win-nightly:
upload-artifacts-nightly:
# Only run scheduled nightly builds on upstream servo.
if: |
(github.repository == 'servo/servo' || github.event_name == 'workflow_dispatch')
&& (inputs.regular_release || false) == false
runs-on: ubuntu-latest
permissions:
id-token: write
attestations: write
needs:
environment: *publish-environment
needs: &upload-artifacts-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' }}
- build-mac
- build-mac-arm64
- build-linux
- build-android
- build-ohos
strategy: &upload-artifacts-strategy
fail-fast: false
matrix:
include:
- artifact_ids: ${{ needs.build-win.outputs.artifact_ids }}
artifact_platform: windows-msvc
- artifact_ids: ${{ needs.build-mac.outputs.artifact_ids }}
artifact_platform: mac
- artifact_ids: ${{ needs.build-mac-arm64.outputs.artifact_ids }}
artifact_platform: mac-arm64
- artifact_ids: ${{ needs.build-linux.outputs.artifact_ids }}
artifact_platform: linux
- artifact_ids: ${{ needs.build-android.outputs.artifact_ids }}
artifact_platform: android
- artifact_ids: ${{ needs.build-ohos.outputs.artifact_ids }}
artifact_platform: ohos
env:
ARTIFACT_IDS: ${{ matrix.artifact_ids }}
ARTIFACT_PLATFORM: ${{ matrix.artifact_platform }}
GITHUB_RELEASE_ID: ${{ needs.create-draft-release.outputs.release-id }}
RELEASE_REPO: ${{ github.repository_owner }}/servo-nightly-builds
RELEASE_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
steps: &upload-artifacts-steps
- uses: actions/checkout@v6
with:
sparse-checkout: |
.github
etc/ci
fetch-depth: '1'
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Validate artifact IDs
run: |
if [[ -z "${ARTIFACT_IDS}" ]]; then
echo "Error: No artifact IDs provided."
echo "Help: Check the build job's outputs.artifact_ids value."
echo "If you recently renamed the build job without updating the corresponding output reference,"
echo "that is likely the cause of this error."
exit 1
fi
- uses: actions/download-artifact@v8
with:
artifact-ids: ${{ env.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 "${ARTIFACT_PLATFORM}" \
--secret-from-environment \
--github-release-id "${GITHUB_RELEASE_ID}" \
release-artifacts/*
upload-win-release:
upload-artifacts-release:
if: github.event_name == 'workflow_dispatch' && inputs.regular_release
runs-on: ubuntu-latest
environment: *publish-environment
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' }}
needs: *upload-artifacts-needs
strategy: *upload-artifacts-strategy
env:
ARTIFACT_IDS: ${{ matrix.artifact_ids }}
ARTIFACT_PLATFORM: ${{ matrix.artifact_platform }}
GITHUB_RELEASE_ID: ${{ needs.create-draft-release.outputs.release-id }}
RELEASE_REPO: ${{ github.repository_owner }}/servo
RELEASE_REPO_TOKEN: ${{ github.token }}
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
steps: *upload-artifacts-steps
build-mac:
@@ -191,46 +276,6 @@ jobs:
profile: "production"
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
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'
@@ -240,46 +285,6 @@ jobs:
profile: "production"
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
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'
@@ -289,46 +294,6 @@ jobs:
profile: "production"
force-github-hosted-runner: true # <https://github.com/servo/servo/issues/33296>
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'
@@ -338,47 +303,6 @@ jobs:
profile: "release"
secrets: inherit
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'
@@ -388,43 +312,3 @@ jobs:
profile: "production"
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

@@ -1,69 +0,0 @@
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
- name: Validate artifact IDs
run: |
if [[ -z "${{ inputs.artifact_ids }}" ]]; then
echo "Error: No artifact IDs provided."
echo "Help: Check the calling workflow's output.artifact_ids parameter, usually created by a build workflow."
echo "If you recently renamed the build job for the artifacts, without updating the `outputs.artifact_ids` "
echo "parameter then this might be the cause of the error."
exit 1
fi
- uses: actions/download-artifact@v8
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 }}

49
Cargo.lock generated
View File

@@ -7279,7 +7279,8 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.36.1"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c"
dependencies = [
"bitflags 2.11.0",
"cssparser",
@@ -7914,7 +7915,7 @@ dependencies = [
[[package]]
name = "servo-hyper-serde"
version = "0.13.2"
version = "0.1.0"
dependencies = [
"cookie 0.18.1",
"headers 0.4.1",
@@ -8926,7 +8927,8 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.4.3"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930"
dependencies = [
"serde",
"stable_deref_trait",
@@ -9345,8 +9347,9 @@ dependencies = [
[[package]]
name = "stylo"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c032879feb9c437f8eb370b1ddc56c13531267766bd6f3ada99c406d36d60a6"
dependencies = [
"app_units",
"arrayvec",
@@ -9401,8 +9404,9 @@ dependencies = [
[[package]]
name = "stylo_atoms"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb92d87ad2ba792800cc2bf508eb17286044303ebe55b15cd7e541190486c86"
dependencies = [
"string_cache",
"string_cache_codegen",
@@ -9410,8 +9414,9 @@ dependencies = [
[[package]]
name = "stylo_derive"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338d14b4375657058b395afcf11bee2fc22a77e9d80f66ee863b617b38d4319a"
dependencies = [
"darling",
"proc-macro2",
@@ -9422,8 +9427,9 @@ dependencies = [
[[package]]
name = "stylo_dom"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa592a930bd67daa5c9c07bc7b422f23f67b5bd30532e85a6fcfef348cf0fb9"
dependencies = [
"bitflags 2.11.0",
"stylo_malloc_size_of",
@@ -9431,8 +9437,9 @@ dependencies = [
[[package]]
name = "stylo_malloc_size_of"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8cd4ec615ff7bbc849d1d7d95891c39db20dfd1cb79eb8ba29c54107a4101a3"
dependencies = [
"app_units",
"cssparser",
@@ -9448,13 +9455,15 @@ dependencies = [
[[package]]
name = "stylo_static_prefs"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc94d0d425d29db1da9535b12793a0db91e85ca2fde13adc73df33f49ee75af7"
[[package]]
name = "stylo_traits"
version = "0.14.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cc7f5c9b43201f0d5c81ca190ccbf29382cff3af4cc926c9cd5e53beb6c9e5"
dependencies = [
"app_units",
"bitflags 2.11.0",
@@ -9876,7 +9885,8 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "to_shmem"
version = "0.3.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab187810ca1e6aaa4c97a06492aac9ade2ffae6a301fd2aac103656f5a69edb"
dependencies = [
"cssparser",
"servo_arc",
@@ -9889,7 +9899,8 @@ dependencies = [
[[package]]
name = "to_shmem_derive"
version = "0.1.0"
source = "git+https://github.com/servo/stylo?rev=8557228b96c0e343764953e72a62ea503baf01b3#8557228b96c0e343764953e72a62ea503baf01b3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba1f5563024b63bb6acb4558452d9ba737518c1d11fcc1861febe98d1e31cf4"
dependencies = [
"darling",
"proc-macro2",

View File

@@ -17,7 +17,7 @@ repository = "https://github.com/servo/servo"
description = "A component of the servo web-engine."
license = "MPL-2.0"
edition = "2024"
publish = false
publish = true
# We have yet to decide on a policy for updating the minimum supported Rust version.
# Before increasing this, please open a discussion on Zulip explaining the reason
# why we should consider increasing our minimum supported Rust version.
@@ -158,15 +158,15 @@ rustc-hash = "2.1.2"
rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] }
rustls-pki-types = "1.14"
rustls-platform-verifier = "0.6.2"
sea-query = { version = "1.0.0-rc.31", default-features = false, features = ["backend-sqlite", "derive"] }
sea-query-rusqlite = { version = "0.8.0-rc.15" }
sea-query = { version = "=1.0.0-rc.31", 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", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
selectors = "0.36.1"
serde = "1.0.228"
serde_bytes = "0.11"
serde_core = "1.0.226"
serde_json = "1.0"
servo_arc = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
servo_arc = "0.4.3"
sha1 = "0.10"
sha2 = "0.10"
sha3 = "0.10"
@@ -174,12 +174,12 @@ skrifa = "0.37.0"
smallvec = { version = "1.15", features = ["serde", "union"] }
string_cache = "0.9"
strum = { version = "0.28", features = ["derive"] }
stylo = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
stylo_atoms = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
stylo_dom = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
stylo_static_prefs = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
stylo_traits = { git = "https://github.com/servo/stylo", rev = "8557228b96c0e343764953e72a62ea503baf01b3" }
stylo = "0.15.0"
stylo_atoms = "0.15.0"
stylo_dom = "0.15.0"
stylo_malloc_size_of = "0.15.0"
stylo_static_prefs = "0.15.0"
stylo_traits = "0.15.0"
surfman = { version = "0.11.0", features = ["chains"] }
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
synstructure = "0.13"
@@ -237,6 +237,7 @@ dom_struct = { package = "servo-dom-struct", version = "0.1.0", path = "componen
embedder_traits = { package = "servo-embedder-traits", version = "0.1.0", path = "components/shared/embedder" }
fonts = { package = "servo-fonts", version = "0.1.0", path = "components/fonts" }
fonts_traits = { package = "servo-fonts-traits", version = "0.1.0", path = "components/shared/fonts" }
hyper_serde = { package = "servo-hyper-serde", version = "0.1.0", path = "components/hyper_serde" }
jstraceable_derive = { package = "servo-jstraceable-derive", version = "0.1.0", path = "components/jstraceable_derive" }
layout = { package = "servo-layout", version = "0.1.0", path = "components/layout" }
layout_api = { package = "servo-layout-api", version = "0.1.0", path = "components/shared/layout" }
@@ -295,9 +296,6 @@ webxr-api = { package = "servo-webxr-api", version = "0.1.0", path = "components
xpath = { package = "servo-xpath", version = "0.1.0", path = "components/xpath" }
# End workspace-version dependencies - Don't change this comment, we grep for it in scripts!
# Independently versioned workspace local crates. These crates will not take part in auto-bumps.
hyper_serde = { package = "servo-hyper-serde", version = "0.13.2", path = "components/hyper_serde" }
# RSA key generation could be very slow without compilation
# optimizations, in development mode. Without optimizations, WPT might
# consider RSA key generation tests fail due to timeout.

View File

@@ -1,6 +1,6 @@
[package]
name = "servo-hyper-serde"
version = "0.13.2"
version.workspace = true
edition.workspace = true
authors = ["The Servo Project Developers"]
description = "Serde support for hyper types."

View File

@@ -231,8 +231,6 @@ skip = [
# github.com organizations to allow git sources for
[sources.allow-org]
github = [
"servo",
# Temporarily needed by servoshell: see root Cargo.toml
"emilk",
]

230
etc/ci/publish_crates_io.py Normal file
View File

@@ -0,0 +1,230 @@
#!/usr/bin/env python3
# Copyright 2026 The Servo Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
import argparse
import json
import os
import subprocess
import sys
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable
from urllib import error, parse, request
# Allow crates.io team to easily identify us incase this script misbehaves.
USER_AGENT = "servo-publish-crates-io/1 (https://github.com/servo/servo)"
WORKSPACE_ROOT = Path(__file__).resolve().parents[2]
SLEEP_AFTER_PUBLISH_SECONDS = int(os.environ.get("SERVO_CRATES_IO_SLEEP_AFTER_PUBLISH_SECONDS", "30"))
VERIFY_PUBLISHED_TIMEOUT_SECONDS = int(os.environ.get("SERVO_CRATES_IO_VERIFY_PUBLISHED_TIMEOUT_SECONDS", "300"))
VERIFY_PUBLISHED_INTERVAL_SECONDS = int(os.environ.get("SERVO_CRATES_IO_VERIFY_PUBLISHED_INTERVAL_SECONDS", "10"))
API_TIMEOUT_SECONDS = int(os.environ.get("SERVO_CRATES_IO_API_TIMEOUT_SECONDS", "30"))
@dataclass(frozen=True)
class WorkspacePackage:
name: str
version: str
manifest_path: str
dependencies: tuple[str, ...]
def log(message: str) -> None:
print(message, file=sys.stderr, flush=True)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Publish workspace crates to crates.io in dependency order, skipping "
"versions that already exist and waiting for index propagation after each publish."
)
)
parser.add_argument(
"--no-verify",
action="store_true",
help="Pass --no-verify to cargo publish.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print the resolved publish order without querying crates.io or publishing.",
)
return parser.parse_args()
def load_metadata() -> dict:
return json.loads(
subprocess.check_output(
["cargo", "metadata", "--no-deps", "--format-version", "1"],
cwd=WORKSPACE_ROOT,
text=True,
)
)
def publishes_to_crates_io(package: dict) -> bool:
publish = package.get("publish")
# See <https://doc.rust-lang.org/cargo/commands/cargo-metadata.html>
# > List of registries to which this package may be published.
# > Publishing is unrestricted if null, and forbidden if an empty array.
return publish is None or "crates-io" in publish
def collect_packages(metadata: dict) -> dict[str, WorkspacePackage]:
local_packages = {package["name"]: package for package in metadata["packages"] if package.get("source") is None}
publishable_names = {name for name, package in local_packages.items() if publishes_to_crates_io(package)}
packages: dict[str, WorkspacePackage] = {}
for name in sorted(publishable_names):
package = local_packages[name]
dependencies = set()
blocked_dependencies = set()
for dependency in package["dependencies"]:
dependency_name = dependency["name"]
if dependency.get("kind") == "dev":
continue
if dependency.get("path") is None or dependency_name not in local_packages:
continue
if dependency_name in publishable_names:
dependencies.add(dependency_name)
else:
blocked_dependencies.add(dependency_name)
if blocked_dependencies:
blocked = ", ".join(sorted(blocked_dependencies))
raise ValueError(f"{name} depends on local crate(s) that do not publish to crates.io: {blocked}")
packages[name] = WorkspacePackage(
name=name,
version=package["version"],
manifest_path=package["manifest_path"],
dependencies=tuple(sorted(dependencies)),
)
return packages
def topological_publish_order(packages: dict[str, WorkspacePackage]) -> list[WorkspacePackage]:
"""
If there were a command like `cargo publish --workspace --print`, which just prints a
publishable order of crates, then we would use that. Since that doesn't exist we
implement this ourselves by walking the dependency tree.
In principle we have a list of WorkspacePackage objects, where each entry has a list of
in-workspace dependencies. We start from the edges (no dependencies), and work our way
to the top, in each iteration appending to the publish-list, and removing the entries
from the dependency lists of the remaining crates.
"""
remaining = {name: set(package.dependencies) for name, package in packages.items()}
ordered_names: list[str] = []
while remaining:
ready = sorted(name for name, dependencies in remaining.items() if not dependencies)
assert ready is not None and len(ready) > 0, "Unable to resolve publish order"
ordered_names.extend(ready)
for name in ready:
remaining.pop(name)
for dependencies in remaining.values():
dependencies.difference_update(ready)
return [packages[name] for name in ordered_names]
def crates_io_version_exists(crate_name: str, version: str) -> bool:
crate_path = parse.quote(crate_name, safe="")
version_path = parse.quote(version, safe="")
api_url = f"https://crates.io/api/v1/crates/{crate_path}/{version_path}"
req = request.Request(api_url, headers={"User-Agent": USER_AGENT})
try:
with request.urlopen(req, timeout=API_TIMEOUT_SECONDS):
return True
except error.HTTPError as http_error:
if http_error.code == 404:
return False
raise RuntimeError(
f"crates.io returned HTTP {http_error.code} while checking {crate_name} {version}"
) from http_error
except error.URLError as url_error:
raise RuntimeError(f"failed to query crates.io for {crate_name} {version}: {url_error.reason}") from url_error
def wait_until_published(package: WorkspacePackage) -> None:
deadline = time.monotonic() + VERIFY_PUBLISHED_TIMEOUT_SECONDS
while True:
try:
if crates_io_version_exists(package.name, package.version):
log(f"verified {package.name} {package.version} on crates.io")
return
except RuntimeError as runtime_error:
log(str(runtime_error))
remaining = deadline - time.monotonic()
if remaining <= 0:
raise RuntimeError(f"timed out waiting for {package.name} {package.version} to appear on crates.io")
time.sleep(min(float(VERIFY_PUBLISHED_INTERVAL_SECONDS), remaining))
def publish_package(
args: argparse.Namespace,
package: WorkspacePackage,
) -> None:
command = ["cargo", "publish", "--manifest-path", package.manifest_path]
if args.no_verify:
command.append("--no-verify")
log(f"publishing {package.name} {package.version}")
subprocess.run(command, cwd=WORKSPACE_ROOT, check=True)
def publish_packages(args: argparse.Namespace, packages: Iterable[WorkspacePackage]) -> None:
for package in packages:
if crates_io_version_exists(package.name, package.version):
log(f"skipping {package.name} {package.version}; already on crates.io")
continue
publish_package(args, package)
duration_seconds = SLEEP_AFTER_PUBLISH_SECONDS
log(f"published {package.name} {package.version}. Waiting for {duration_seconds}s")
# To distribute load on crates.io, we sleep for a bit after each publish.
time.sleep(duration_seconds)
# And in case crates.io is under heavy load and publishing takes longer than usual,
# try and wait until the new version appears on crates.io.
wait_until_published(package)
def main() -> int:
args = parse_args()
metadata = load_metadata()
packages = collect_packages(metadata)
ordered_packages = topological_publish_order(packages)
if not ordered_packages:
log("no local crates to publish to crates.io")
return 0
log("publish order:")
for package in ordered_packages:
dependency_list = ", ".join(package.dependencies) if package.dependencies else "none"
log(f" {package.name} {package.version} (deps: {dependency_list})")
if args.dry_run:
return 0
publish_packages(args, ordered_packages)
return 0
if __name__ == "__main__":
sys.exit(main())