Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .github/scripts/release_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ lla-linux-i686
lla-macos-amd64
lla-macos-arm64
lla-netbsd-amd64
lla-windows-amd64.exe
lla-windows-arm64.exe
plugins-linux-amd64.tar.gz
plugins-linux-amd64.zip
plugins-linux-arm64.tar.gz
Expand All @@ -218,6 +220,8 @@ plugins-macos-amd64.tar.gz
plugins-macos-amd64.zip
plugins-macos-arm64.tar.gz
plugins-macos-arm64.zip
plugins-windows-amd64.zip
plugins-windows-arm64.zip
lla_${version}_amd64.deb
lla-${version}-1.x86_64.rpm
lla-${version}-r0.x86_64.apk
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ The canonical maintainer checklist is in
## CI (`ci.yml`)

- Triggered on pushes and pull requests to `main` when Rust sources, manifests, proto files, scripts, workflow files, package metadata, or toolchain files change.
- Runs formatting, Clippy, tests, and release-mode build checks across Linux and macOS.
- Runs formatting, Clippy, tests, and release-mode build checks across Linux,
macOS, Windows AMD64, and Windows ARM64.
- Builds the default-feature CLI natively on NetBSD, verifies that Wasmtime is
absent from its dependency graph, smoke-tests it, and uploads
`lla-netbsd-amd64`.
Expand Down Expand Up @@ -50,10 +51,14 @@ The canonical maintainer checklist is in
- `cargo test --workspace`
- `cargo test -p lla --features wasm-plugins`
- Builds and verifies all release assets before publishing:
- full-featured CLI binaries built with `wasm-plugins`: `lla-linux-{amd64,arm64,i686}`, `lla-macos-*`
- full-featured CLI binaries built with `wasm-plugins`:
`lla-linux-{amd64,arm64,i686}`, `lla-macos-*`, and
`lla-windows-{amd64,arm64}.exe`
- full-featured static musl binaries with Wasmtime: `lla-linux-{amd64,arm64}-musl`
- native `lla-netbsd-amd64`, built and smoke-tested inside NetBSD 10.1 with
default features and no Wasmtime dependency
- Windows AMD64/ARM64 plugin `.zip` archives, built and verified on native
GitHub-hosted runners
- plugin archives: `plugins-*.tar.gz` and `plugins-*.zip`
- Linux packages: `.deb`, `.rpm`, `.apk`, `.pkg.tar.zst`
- `themes.zip`
Expand Down Expand Up @@ -88,6 +93,7 @@ The canonical maintainer checklist is in

- `.github/scripts/prepare_release.sh` updates release versions and changelog content for the generated PR.
- `.github/scripts/release_helpers.sh` contains shared validation, expected asset, checksum, crates.io, and GitHub release helpers.
- `scripts/build_plugins.sh` builds plugin dynamic libraries and produces both `.tar.gz` and `.zip` archives for each release target.
- `scripts/build_plugins.sh` builds plugin dynamic libraries and produces
`.tar.gz` plus `.zip` archives on Unix and `.zip` archives on Windows.
- `.github/scripts/check_glibc_baseline.sh` enforces the documented GNU/Linux compatibility baseline.
- `.github/scripts/smoke_test_musl.sh` exercises core features and the explicit plugin error inside Alpine.
129 changes: 127 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- "**/Cargo.lock"
- "**/*.proto"
- "**/*.sh"
- "**/*.ps1"
- "plugins/*/plugin.toml"
- "scripts/**"
- ".github/workflows/ci.yml"
Expand All @@ -22,6 +23,7 @@ on:
- "**/Cargo.lock"
- "**/*.proto"
- "**/*.sh"
- "**/*.ps1"
- "plugins/*/plugin.toml"
- "scripts/**"
- ".github/workflows/ci.yml"
Expand All @@ -40,7 +42,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest, windows-11-arm]
rust: [stable]

steps:
Expand Down Expand Up @@ -156,6 +158,14 @@ jobs:
target: aarch64-apple-darwin
artifact_name: lla-macos-arm64

# Windows builds
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: lla-windows-amd64.exe
- os: windows-11-arm
target: aarch64-pc-windows-msvc
artifact_name: lla-windows-arm64.exe

steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -186,7 +196,8 @@ jobs:
with:
key: ${{ matrix.target }}

- name: Build release
- name: Build release (Unix)
if: runner.os != 'Windows'
env:
RUSTUP_TOOLCHAIN: stable
run: |
Expand All @@ -198,6 +209,14 @@ jobs:
cargo build --release --target "${{ matrix.target }}" -p lla --features wasm-plugins
fi

- name: Build release (Windows)
if: runner.os == 'Windows'
shell: pwsh
env:
RUSTUP_TOOLCHAIN: stable
RUSTFLAGS: -C target-feature=+crt-static
run: cargo build --release --target "${{ matrix.target }}" -p lla --features wasm-plugins

- name: Verify glibc baseline
if: matrix.libc == 'gnu'
run: |
Expand Down Expand Up @@ -229,6 +248,112 @@ jobs:
run: |
cp target/${{ matrix.target }}/release/lla ${{ matrix.artifact_name }}

- name: Prepare and smoke-test binary (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$binary = "target/${{ matrix.target }}/release/lla.exe"
Copy-Item $binary "${{ matrix.artifact_name }}"
& $binary --help | Out-Null
& $binary --version | Out-Null
$fixture = Join-Path $env:RUNNER_TEMP "lla-windows-smoke"
New-Item -ItemType Directory -Force $fixture | Out-Null
$fixtureFile = Join-Path $fixture "fixture.txt"
[System.IO.File]::WriteAllBytes($fixtureFile, [byte[]](97, 98, 99))
if (-not (Test-Path -LiteralPath $fixtureFile -PathType Leaf)) {
throw "Windows smoke fixture was not created: $fixtureFile"
}
if ((Get-Item -LiteralPath $fixtureFile).Length -ne 3) {
throw "Windows smoke fixture has an unexpected size"
}
$rawJson = (& $binary --json $fixture) -join "`n"
if ($LASTEXITCODE -ne 0) { throw "Windows JSON smoke test failed: $rawJson" }
$json = $rawJson | ConvertFrom-Json
if (@($json).Count -ne 1) { throw "Windows JSON smoke test returned an unexpected entry count: $rawJson" }
$entry = @($json)[0]
foreach ($field in @('owner_user', 'owner_group', 'inode', 'hard_links', 'allocated_size_bytes', 'security_context', 'mount_point', 'mount_source', 'filesystem')) {
if ($null -ne $entry.$field) { throw "Windows JSON field '$field' must be null" }
}
$imports = (& llvm-readobj --coff-imports $binary) -join "`n"
if ($LASTEXITCODE -ne 0) { throw 'Failed to inspect Windows PE imports' }
if ($imports -match '(?i)(VCRUNTIME|MSVCP)[0-9_]*\.dll') {
throw "Windows binary imports the redistributable MSVC runtime"
}

- name: Build and verify Windows plugin archive
if: runner.os == 'Windows'
shell: bash
run: |
./scripts/build_plugins.sh --target "${{ matrix.target }}"
./scripts/verify_plugins_v3.sh "dist/plugins-windows-${{ matrix.target == 'x86_64-pc-windows-msvc' && 'amd64' || 'arm64' }}"

- name: Verify Windows plugin PE imports
if: runner.os == 'Windows'
shell: pwsh
run: |
$pluginDirectory = "dist/plugins-windows-${{ matrix.target == 'x86_64-pc-windows-msvc' && 'amd64' || 'arm64' }}"
Get-ChildItem $pluginDirectory -Recurse -Filter *.dll | ForEach-Object {
$imports = (& llvm-readobj --coff-imports $_.FullName) -join "`n"
if ($LASTEXITCODE -ne 0) { throw "Failed to inspect PE imports for '$($_.FullName)'" }
if ($imports -match '(?i)(VCRUNTIME|MSVCP)[0-9_]*\.dll') {
throw "Plugin '$($_.Name)' imports the redistributable MSVC runtime"
}
}

- name: Test PowerShell installer helpers
if: runner.os == 'Windows'
shell: pwsh
run: |
. ./install.ps1
if ((Resolve-LlaArchitecture 'AMD64' '') -ne 'amd64') { throw 'AMD64 mapping failed' }
if ((Resolve-LlaArchitecture 'AMD64' 'ARM64') -ne 'arm64') { throw 'ARM64 native mapping failed' }
$asset = "lla-windows-amd64.exe"
$hash = 'a' * 64
if ((Get-LlaChecksum "$hash $asset`n" $asset) -ne $hash) { throw 'Checksum parsing failed' }
try { Get-LlaChecksum "$hash $asset.old`n" $asset | Out-Null; throw 'Inexact checksum entry was accepted' } catch {
if ($_.Exception.Message -eq 'Inexact checksum entry was accepted') { throw }
}
try { Resolve-LlaArchitecture 'x86' '' | Out-Null; throw 'x86 was accepted' } catch {
if ($_.Exception.Message -eq 'x86 was accepted') { throw }
}

$binary = (Resolve-Path "target/${{ matrix.target }}/release/lla.exe").Path
$expectedAsset = "lla-windows-$(Resolve-LlaArchitecture).exe"
$expectedHash = (Get-FileHash -Algorithm SHA256 $binary).Hash.ToLowerInvariant()
function Invoke-WebRequest {
param(
[Parameter(Position = 0)][string]$Uri,
[string]$OutFile
)
if ($Uri.EndsWith('.exe')) {
Copy-Item $binary $OutFile
} else {
[PSCustomObject]@{ Content = "$expectedHash $expectedAsset`n" }
}
}

$originalUserPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$originalProcessPath = $env:Path
$customInstall = Join-Path $env:RUNNER_TEMP 'lla-custom-install'
try {
$script:Version = 'v0.0.0-test'
$script:InstallDir = $customInstall
$script:NoPathUpdate = $true
Invoke-LlaInstall
if (-not (Test-Path (Join-Path $customInstall 'lla.exe'))) { throw 'Custom install directory was ignored' }
if ([Environment]::GetEnvironmentVariable('Path', 'User') -ne $originalUserPath) { throw '-NoPathUpdate changed the user PATH' }

Add-LlaToUserPath $customInstall
$userEntries = @([Environment]::GetEnvironmentVariable('Path', 'User') -split ';')
if (-not ($userEntries -contains $customInstall)) { throw 'User PATH was not updated' }
Add-LlaToUserPath $customInstall
$matches = @([Environment]::GetEnvironmentVariable('Path', 'User') -split ';' | Where-Object { $_ -eq $customInstall })
if ($matches.Count -ne 1) { throw 'User PATH update was not idempotent' }
} finally {
[Environment]::SetEnvironmentVariable('Path', $originalUserPath, 'User')
$env:Path = $originalProcessPath
}

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
Expand Down
37 changes: 34 additions & 3 deletions .github/workflows/rebuild-release-plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ jobs:
target: aarch64-apple-darwin
os_label: macos
arch_label: arm64
- runner: windows-latest
target: x86_64-pc-windows-msvc
os_label: windows
arch_label: amd64
- runner: windows-11-arm
target: aarch64-pc-windows-msvc
os_label: windows
arch_label: arm64
steps:
- uses: actions/checkout@v4

Expand All @@ -96,6 +104,7 @@ jobs:
cargo install cargo-zigbuild --version "$CARGO_ZIGBUILD_VERSION" --locked

- name: Build plugin archives
shell: bash
run: |
if [[ "${{ runner.os }}" == "Linux" ]]; then
./scripts/build_plugins.sh --target "${{ matrix.target }}" --glibc-version "$GLIBC_BASELINE"
Expand All @@ -110,10 +119,24 @@ jobs:
.github/scripts/check_glibc_baseline.sh "$GLIBC_BASELINE" "${plugins[@]}"

- name: Verify Plugin Platform v3
if: matrix.target == 'x86_64-unknown-linux-gnu' || matrix.target == 'x86_64-apple-darwin' || matrix.target == 'aarch64-apple-darwin'
if: matrix.target == 'x86_64-unknown-linux-gnu' || matrix.target == 'x86_64-apple-darwin' || matrix.target == 'aarch64-apple-darwin' || matrix.target == 'x86_64-pc-windows-msvc' || matrix.target == 'aarch64-pc-windows-msvc'
shell: bash
run: ./scripts/verify_plugins_v3.sh "dist/plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}"

- name: Verify Windows plugin PE imports
if: matrix.os_label == 'windows'
shell: pwsh
run: |
Get-ChildItem "dist/plugins-windows-${{ matrix.arch_label }}" -Recurse -Filter *.dll | ForEach-Object {
$imports = (& llvm-readobj --coff-imports $_.FullName) -join "`n"
if ($LASTEXITCODE -ne 0) { throw "Failed to inspect PE imports for '$($_.FullName)'" }
if ($imports -match '(?i)(VCRUNTIME|MSVCP)[0-9_]*\.dll') {
throw "Plugin '$($_.Name)' imports the redistributable MSVC runtime"
}
}

- name: Confirm file_hash is native
shell: bash
run: |
manifest="dist/plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}/file_hash/plugin.toml"
grep -Eq '^runtime = "native"$' "$manifest"
Expand All @@ -122,14 +145,22 @@ jobs:
exit 1
fi

- name: Upload plugin artifacts
- name: Upload plugin artifacts (Unix)
if: matrix.os_label != 'windows'
uses: actions/upload-artifact@v4
with:
name: plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}
path: |
dist/plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}.tar.gz
dist/plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}.zip

- name: Upload plugin artifact (Windows)
if: matrix.os_label == 'windows'
uses: actions/upload-artifact@v4
with:
name: plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}
path: dist/plugins-${{ matrix.os_label }}-${{ matrix.arch_label }}.zip

replace_assets:
name: Replace Release Plugin Assets
needs: [validate, build_plugins]
Expand All @@ -151,7 +182,7 @@ jobs:
- name: Replace plugin archives
run: |
set -euo pipefail
expected=10
expected=12
actual="$(find rebuilt_plugins -maxdepth 1 -type f \( -name 'plugins-*.tar.gz' -o -name 'plugins-*.zip' \) | wc -l | tr -d ' ')"
if [[ "$actual" != "$expected" ]]; then
echo "Expected $expected rebuilt plugin archives, found $actual" 1>&2
Expand Down
Loading
Loading