Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 264 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,281 @@
name: Publish to pub.dev

on:
pull_request:
paths:
- '.github/workflows/publish.yml'
- 'doc/prebuilt-native-assets.md'
- 'hook/build.dart'
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+*'
workflow_dispatch:

permissions:
contents: read

jobs:
build-prebuilt:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- target: linux-x64
os: ubuntu-22.04
library: libwebcrypto.so
configure_args: ''
- target: macos-arm64
os: macos-15
library: libwebcrypto.dylib
configure_args: '-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DCMAKE_OSX_ARCHITECTURES=arm64'
- target: macos-x64
os: macos-15-intel
library: libwebcrypto.dylib
configure_args: '-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_OSX_ARCHITECTURES=x86_64'
- target: windows-x64
os: windows-2022
library: webcrypto.dll
configure_args: '-A x64'

steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

- uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2
if: runner.os == 'Windows'

- name: Build Linux prebuilt asset
if: runner.os == 'Linux'
shell: bash
run: |
docker run --rm \
--env HOST_GID="$(id -g)" \
--env HOST_UID="$(id -u)" \
--volume "$GITHUB_WORKSPACE:/workspace" \
--workdir /workspace \
ubuntu@sha256:8feb4d8ca5354def3d8fce243717141ce31e2c428701f6682bd2fafe15388214 \
bash -euxo pipefail -c '
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install --no-install-recommends --yes \
build-essential ca-certificates cmake ninja-build perl
cmake \
-S src \
-B build/prebuilt \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/workspace/build/prebuilt-install
cmake --build build/prebuilt --target install
mkdir -p artifact/prebuilt/linux-x64
cp build/prebuilt-install/libwebcrypto.so \
artifact/prebuilt/linux-x64/libwebcrypto.so
chown -R "$HOST_UID:$HOST_GID" build artifact
'

- name: Build prebuilt asset
if: runner.os != 'Linux'
shell: pwsh
run: |
$install = Join-Path $env:GITHUB_WORKSPACE 'build/prebuilt-install'
$artifact = Join-Path $env:GITHUB_WORKSPACE 'artifact/prebuilt/${{ matrix.target }}'
$configureArgs = @(
'-S', 'src',
'-B', 'build/prebuilt',
'-DCMAKE_BUILD_TYPE=Release',
"-DCMAKE_INSTALL_PREFIX=$install"
)
if ('${{ matrix.configure_args }}' -ne '') {
$configureArgs += '${{ matrix.configure_args }}'.Split(' ')
}

cmake @configureArgs
cmake --build build/prebuilt --config Release --target install

New-Item -ItemType Directory -Force -Path $artifact | Out-Null
Copy-Item -LiteralPath (Join-Path $install '${{ matrix.library }}') -Destination $artifact

- name: Verify Linux architecture
if: runner.os == 'Linux'
shell: pwsh
run: |
$library = 'artifact/prebuilt/${{ matrix.target }}/${{ matrix.library }}'
$description = & file $library
$description
if ($description -notmatch 'x86-64') {
throw 'Expected an x86-64 ELF library.'
}
$versionInfo = (& readelf --version-info $library) -join "`n"
$glibcVersions = [regex]::Matches(
$versionInfo,
'GLIBC_([0-9]+\.[0-9]+)'
) | ForEach-Object { [version]$_.Groups[1].Value }
$maxGlibc = $glibcVersions | Sort-Object -Descending | Select-Object -First 1
if ($null -eq $maxGlibc) {
throw 'Expected at least one GLIBC version requirement.'
}
if ($maxGlibc -gt [version]'2.31') {
throw "Expected GLIBC 2.31 or older, got $maxGlibc."
}
"Maximum required GLIBC version: $maxGlibc"

- name: Verify macOS architecture
if: runner.os == 'macOS'
shell: pwsh
run: |
$library = 'artifact/prebuilt/${{ matrix.target }}/${{ matrix.library }}'
$architecture = (& lipo -archs $library).Trim()
$expected = if ('${{ matrix.target }}' -eq 'macos-arm64') { 'arm64' } else { 'x86_64' }
$expectedMinimumVersion = if ('${{ matrix.target }}' -eq 'macos-arm64') {
[version]'11.0'
} else {
[version]'10.15'
}
if ($architecture -ne $expected) {
throw "Expected $expected, got $architecture."
}
$buildInfo = (& vtool -show-build $library) -join "`n"
$minimumVersion = [regex]::Match(
$buildInfo,
'(?m)^\s*minos\s+([0-9.]+)\s*$'
)
if (-not $minimumVersion.Success) {
throw 'Failed to read the macOS deployment target.'
}
$minimumVersion = [version]$minimumVersion.Groups[1].Value
if ($minimumVersion -gt $expectedMinimumVersion) {
throw "Expected macOS $expectedMinimumVersion or older, got $minimumVersion."
}
"Minimum macOS version: $minimumVersion"

- name: Verify Windows architecture
if: runner.os == 'Windows'
shell: pwsh
run: |
$library = 'artifact/prebuilt/${{ matrix.target }}/${{ matrix.library }}'
$stream = [System.IO.File]::OpenRead($library)
$reader = [System.IO.BinaryReader]::new($stream)
try {
$stream.Position = 0x3c
$peHeaderOffset = $reader.ReadInt32()
$stream.Position = $peHeaderOffset
if ($reader.ReadUInt32() -ne 0x00004550) {
throw 'Expected a PE library.'
}
$machine = $reader.ReadUInt16()
if ($machine -ne 0x8664) {
throw ('Expected an x64 PE library, got machine type 0x{0:x4}.' -f $machine)
}
} finally {
$reader.Dispose()
$stream.Dispose()
}

- name: Upload prebuilt asset
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: webcrypto-${{ matrix.target }}
path: artifact/
if-no-files-found: error

validate-prebuilt:
name: Validate prebuilt package
needs: build-prebuilt
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: webcrypto-*
path: .
merge-multiple: true
- uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: 'stable'
- name: Verify prebuilt layout
shell: pwsh
run: |
@(
'prebuilt/linux-x64/libwebcrypto.so',
'prebuilt/macos-arm64/libwebcrypto.dylib',
'prebuilt/macos-x64/libwebcrypto.dylib',
'prebuilt/windows-x64/webcrypto.dll'
) | ForEach-Object {
if (-not (Test-Path -LiteralPath $_ -PathType Leaf)) {
throw "Missing prebuilt asset: $_"
}
}
- run: flutter pub get
- name: Test prebuilt asset
run: dart test -p vm test/aes_ctr_counter_wrap_test.dart
- name: Test source-build override
shell: pwsh
run: |
$library = 'prebuilt/linux-x64/libwebcrypto.so'
$libraryBackup = Join-Path $env:RUNNER_TEMP 'libwebcrypto.so'
$pubspecBackup = Join-Path $env:RUNNER_TEMP 'pubspec.yaml'
Copy-Item -LiteralPath $library -Destination $libraryBackup
Copy-Item -LiteralPath 'pubspec.yaml' -Destination $pubspecBackup
try {
Set-Content -LiteralPath $library -Value 'invalid prebuilt'
Add-Content -LiteralPath 'pubspec.yaml' -Value @(
'',
'hooks:',
' user_defines:',
' webcrypto:',
' build_from_source: true'
)
Remove-Item -Recurse -Force '.dart_tool/hooks_runner' -ErrorAction SilentlyContinue
flutter pub get
dart test -p vm test/aes_ctr_counter_wrap_test.dart
} finally {
Copy-Item -LiteralPath $libraryBackup -Destination $library -Force
Copy-Item -LiteralPath $pubspecBackup -Destination 'pubspec.yaml' -Force
}
- name: Validate and measure package contents
shell: pwsh
run: |
$archive = Join-Path $env:RUNNER_TEMP 'webcrypto.tar.gz'
flutter pub publish --to-archive $archive
$archiveSize = (Get-Item -LiteralPath $archive).Length
$prebuiltSize = (
Get-ChildItem -Path prebuilt -Recurse -File |
Measure-Object -Property Length -Sum
).Sum
$archiveMiB = [math]::Round($archiveSize / 1MB, 2)
$prebuiltMiB = [math]::Round($prebuiltSize / 1MB, 2)
"Compressed package archive: $archiveMiB MiB ($archiveSize bytes)"
"Uncompressed prebuilt matrix: $prebuiltMiB MiB ($prebuiltSize bytes)"
@"
### Package size

- Compressed package archive: $archiveMiB MiB ($archiveSize bytes)
- Uncompressed prebuilt matrix: $prebuiltMiB MiB ($prebuiltSize bytes)
"@ | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY

publish:
name: Publish to pub.dev
runs-on: ubuntu-latest
needs:
- build-prebuilt
- validate-prebuilt
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-24.04
environment: pub.dev
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- uses: subosito/flutter-action@v2
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: webcrypto-*
path: .
merge-multiple: true
- uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: 'stable'
- run: flutter pub get
Expand Down
47 changes: 47 additions & 0 deletions doc/prebuilt-native-assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Prebuilt Native Assets

Published versions of `package:webcrypto` include trusted prebuilt native
libraries for the initial desktop target matrix:

```text
prebuilt/
linux-x64/
libwebcrypto.so
macos-arm64/
libwebcrypto.dylib
macos-x64/
libwebcrypto.dylib
windows-x64/
webcrypto.dll
```

The build hook uses a matching prebuilt library when one is available. Targets
without a packaged prebuilt continue to build the native library from source
with CMake.

The Linux artifact is built on Ubuntu 20.04 and must not require a glibc
version newer than 2.31. The macOS x64 artifact uses a 10.15 deployment target,
while the arm64 artifact uses 11.0, the first macOS release supporting Apple
Silicon. The publishing workflow verifies these compatibility baselines before
it assembles the package.

## Building From Source

Consumers and CI can bypass a packaged prebuilt explicitly:

```yaml
hooks:
user_defines:
webcrypto:
build_from_source: true
```

## Publishing

The prebuilt libraries are not committed to the repository. The
`.github/workflows/publish.yml` workflow builds them from the tagged source,
validates the assembled package, and includes them in the package published to
pub.dev.

Manually dispatching the workflow builds and validates the same artifacts but
does not publish a package.
Loading
Loading