ci: parallelize GitHub releases and fix WSL path resolution
This commit is contained in:
6 files changed
+288
-59
No files matched your search
+131
-48
@@ -8,20 +8,64 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
SQLX_OFFLINE: "true"
|
||||
RELEASE_VERSION: ${{ github.ref_name }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
router:
|
||||
name: Router (ARMv7 musl)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
targets: x86_64-unknown-linux-gnu,aarch64-unknown-linux-gnu
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: release-router-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: release-router-${{ runner.os }}-${{ runner.arch }}-
|
||||
|
||||
- name: Cache cross executable
|
||||
id: cross
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cargo/bin/cross
|
||||
key: cross-${{ runner.os }}-${{ runner.arch }}-0.2.5
|
||||
|
||||
- name: Install cross
|
||||
if: steps.cross.outputs.cache-hit != 'true'
|
||||
run: cargo install cross --version 0.2.5 --locked
|
||||
|
||||
- name: Build router binaries
|
||||
run: cross build --release --locked --target armv7-unknown-linux-musleabihf -p wakey -p wakey-agent
|
||||
|
||||
- name: Package rootfs tarball
|
||||
shell: pwsh
|
||||
run: ./scripts/package_rootfs.ps1 -Version $env:RELEASE_VERSION -Target armv7-unknown-linux-musleabihf
|
||||
|
||||
- name: Upload rootfs
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wakey-rootfs
|
||||
path: dist/wakey-rootfs-*armv7-unknown-linux-musleabihf.tgz
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
|
||||
ui:
|
||||
name: UI
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -33,8 +77,36 @@ jobs:
|
||||
cache: pnpm
|
||||
cache-dependency-path: ui/pnpm-lock.yaml
|
||||
|
||||
- name: Install cross
|
||||
run: cargo install cross --locked
|
||||
- name: Build UI dist
|
||||
working-directory: ui
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
- name: Upload UI
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wakey-ui
|
||||
path: ui/dist/
|
||||
if-no-files-found: error
|
||||
|
||||
cc:
|
||||
name: CC (${{ matrix.target }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
runner: ubuntu-24.04
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v4
|
||||
@@ -43,57 +115,67 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: linux-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
key: release-cc-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: release-cc-${{ matrix.target }}-
|
||||
|
||||
- name: Build (armv7-musl with cross)
|
||||
run: |
|
||||
cross build --release --target armv7-unknown-linux-musleabihf -p wakey -p wakey-agent
|
||||
# Native GNU builds use the runner's glibc. Gitea remains the separate
|
||||
# older-glibc release path for deployment hosts that need it.
|
||||
- name: Build control-plane natively
|
||||
run: cargo build --release --locked --target ${{ matrix.target }} -p wakey-control-plane
|
||||
|
||||
- name: Build UI dist
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd ui
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
- name: Build control-plane (linux-gnu)
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# x86_64 builds natively; aarch64 needs cross
|
||||
echo "Building control-plane x86_64-unknown-linux-gnu (native)"
|
||||
cargo build --release --target x86_64-unknown-linux-gnu -p wakey-control-plane
|
||||
|
||||
echo "Building control-plane aarch64-unknown-linux-gnu (cross)"
|
||||
cross build --release --target aarch64-unknown-linux-gnu -p wakey-control-plane
|
||||
|
||||
- name: Package rootfs tarball
|
||||
shell: pwsh
|
||||
run: |
|
||||
./scripts/package_rootfs.ps1 -Version "${{ github.ref_name }}" -Target armv7-unknown-linux-musleabihf
|
||||
|
||||
- name: Package control-plane bundles
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x scripts/update_wakey_cc.sh scripts/package_wakey_cc_bundle.sh
|
||||
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
|
||||
./scripts/package_wakey_cc_bundle.sh --version "${{ github.ref_name }}" --target "$target"
|
||||
done
|
||||
|
||||
- name: Upload artifact
|
||||
- name: Upload control-plane binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wakey-cc-bin-${{ matrix.target }}
|
||||
path: target/${{ matrix.target }}/release/wakey-control-plane
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
name: Package and publish
|
||||
needs: [router, ui, cc]
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download rootfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: wakey-rootfs
|
||||
path: dist/wakey-rootfs-*armv7-unknown-linux-musleabihf.tgz
|
||||
path: dist
|
||||
|
||||
- name: Download UI
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: wakey-ui
|
||||
path: ui/dist
|
||||
|
||||
- name: Download x86-64 CC
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: wakey-cc-bin-x86_64-unknown-linux-gnu
|
||||
path: target/x86_64-unknown-linux-gnu/release
|
||||
|
||||
- name: Download ARM64 CC
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: wakey-cc-bin-aarch64-unknown-linux-gnu
|
||||
path: target/aarch64-unknown-linux-gnu/release
|
||||
|
||||
- name: Package control-plane bundles
|
||||
run: |
|
||||
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
|
||||
sh ./scripts/package_wakey_cc_bundle.sh --version "$RELEASE_VERSION" --target "$target"
|
||||
done
|
||||
|
||||
- name: Upload control-plane bundles
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wakey-cc-bundles
|
||||
path: dist/wakey-cc-*unknown-linux-gnu.tgz
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
|
||||
- name: Publish Release to GitHub
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -101,6 +183,7 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
fail_on_unmatched_files: true
|
||||
files: |
|
||||
dist/wakey-rootfs-*armv7-unknown-linux-musleabihf.tgz
|
||||
dist/wakey-cc-*unknown-linux-gnu.tgz
|
||||
@@ -144,6 +144,14 @@ Implementation notes:
|
||||
|
||||
## CI (GitHub)
|
||||
|
||||
- `.github/workflows/release.yml` builds router binaries, UI, x86-64 CC and
|
||||
ARM64 CC in parallel. CC uses matching Ubuntu 24.04 runners and native Cargo;
|
||||
Cross is used only for the ARMv7-musl router binaries. A final job downloads
|
||||
their artifacts, packages CC bundles and publishes after every build succeeds.
|
||||
- GitHub's native GNU binaries inherit its runner's glibc requirements. Use the
|
||||
Gitea bundles for older deployment hosts, checking that its build actually
|
||||
used Zig's configured baseline. Gitea retains one job for `--once` runners.
|
||||
|
||||
- Defined in `.github/workflows/ci.yml` on pushes/PRs to `main` or `master`:
|
||||
- **`rust` job:** `cargo fmt --check`, `clippy -D warnings`, `cargo test --workspace` on `ubuntu-latest`.
|
||||
- **`ui` job:** under `ui/`, `pnpm install --frozen-lockfile`, then `pnpm run typecheck`, `format:check`, and `build` (mirrors what you need before packaging the control-plane bundle). GitHub sets `CI=true` for the runner; pnpm uses lockfile `ui/pnpm-lock.yaml` for cache.
|
||||
|
||||
@@ -67,6 +67,12 @@ From Windows PowerShell, using the WSL wrapper:
|
||||
|
||||
Path control (binary and config are independent):
|
||||
|
||||
When called from PowerShell inside WSL, absolute POSIX paths pass through
|
||||
unchanged. Windows paths are converted using the selected distro's `wslpath`.
|
||||
Relative runner home, binary and config paths resolve from the current PowerShell
|
||||
directory. Conversion also supports paths that have not been created yet.
|
||||
The helper, runner home and config paths must be accessible in that distro.
|
||||
|
||||
```powershell
|
||||
./scripts/act_runner_wsl.ps1 -Action status `
|
||||
-RunnerBin /home/lda/gitea-runner/act_runner `
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
param(
|
||||
[ValidateSet("update", "register", "start", "stop", "status")]
|
||||
[string]$Action = "start",
|
||||
[string]$Distro = $(if ($env:WAKEY_WSL_DISTRO) { $env:WAKEY_WSL_DISTRO } else { "FedoraLinux-43" }),
|
||||
[string]$Distro = $( $env:WAKEY_WSL_DISTRO ?? "FedoraLinux-43" ),
|
||||
[string]$RunnerHome,
|
||||
[string]$RunnerBin = "/home/lda/gitea-runner/act_runner",
|
||||
[string]$ConfigPath,
|
||||
@@ -22,7 +22,7 @@ if (-not (Test-Path $scriptWin)) {
|
||||
throw "Missing Fedora runner helper: $scriptWin"
|
||||
}
|
||||
|
||||
$scriptLinux = (& wsl.exe -d $Distro -e wslpath -a "$scriptWin").Trim()
|
||||
$scriptLinux = ConvertTo-WslPath -WindowsPath $scriptWin -Distro $Distro
|
||||
if (-not $scriptLinux) {
|
||||
throw "Failed to resolve WSL path for $scriptWin"
|
||||
}
|
||||
@@ -30,21 +30,19 @@ if (-not $scriptLinux) {
|
||||
if (-not $RunnerHome) {
|
||||
$repoRootWin = Split-Path -Parent $PSScriptRoot
|
||||
$defaultRunnerHomeWin = Join-Path $repoRootWin "ar_data/wsl_runner"
|
||||
$RunnerHome = (& wsl.exe -d $Distro -e wslpath -a "$defaultRunnerHomeWin").Trim()
|
||||
if (-not $RunnerHome) {
|
||||
throw "Failed to resolve WSL runner home for $defaultRunnerHomeWin"
|
||||
}
|
||||
$RunnerHome = $defaultRunnerHomeWin
|
||||
}
|
||||
|
||||
if (-not $ConfigPath) {
|
||||
$repoRootWin = Split-Path -Parent $PSScriptRoot
|
||||
$defaultConfigWin = Join-Path $repoRootWin "ar_data/wsl_runner/config.yaml"
|
||||
$ConfigPath = (& wsl.exe -d $Distro -e wslpath -a "$defaultConfigWin").Trim()
|
||||
if (-not $ConfigPath) {
|
||||
throw "Failed to resolve WSL config path for $defaultConfigWin"
|
||||
}
|
||||
$ConfigPath = $defaultConfigWin
|
||||
}
|
||||
|
||||
$RunnerHome = ConvertTo-WslPath -Path $RunnerHome -Distro $Distro
|
||||
$RunnerBin = ConvertTo-WslPath -Path $RunnerBin -Distro $Distro
|
||||
$ConfigPath = ConvertTo-WslPath -Path $ConfigPath -Distro $Distro
|
||||
|
||||
$parts = @("bash", (Quote-ShArg $scriptLinux), (Quote-ShArg $Action), "--runner-home", (Quote-ShArg $RunnerHome))
|
||||
$parts += @("--runner-bin", (Quote-ShArg $RunnerBin), "--config-path", (Quote-ShArg $ConfigPath))
|
||||
if ($ServerUrl) { $parts += @("--server-url", (Quote-ShArg $ServerUrl)) }
|
||||
|
||||
+38
-1
@@ -27,8 +27,15 @@ function Test-IsWsl {
|
||||
return $false
|
||||
}
|
||||
|
||||
# With -Distro, resolve local relative paths from the PowerShell working
|
||||
# directory and convert Windows paths with the distro's mount configuration.
|
||||
# Without it, keep the existing mapping used by SCP callers, including remote destinations.
|
||||
function ConvertTo-WslPath {
|
||||
param([string]$WindowsPath)
|
||||
param(
|
||||
[Alias('Path')]
|
||||
[string]$WindowsPath,
|
||||
[string]$Distro
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($WindowsPath)) {
|
||||
return $WindowsPath
|
||||
@@ -39,6 +46,36 @@ function ConvertTo-WslPath {
|
||||
return $WindowsPath
|
||||
}
|
||||
|
||||
# wslpath expects a Windows input here. Use the selected distro's mount
|
||||
# configuration when requested, while keeping existing POSIX paths intact.
|
||||
if ($Distro) {
|
||||
# Absolute Windows paths can be supplied even from Linux PowerShell.
|
||||
# Relative inputs use the filesystem provider without requiring a file
|
||||
# to exist, which is important for a new runner home/config.
|
||||
if ($WindowsPath -notmatch '^[A-Za-z]:[\\/]|^\\\\') {
|
||||
$provider = $null
|
||||
$drive = $null
|
||||
$WindowsPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
|
||||
$WindowsPath, [ref]$provider, [ref]$drive)
|
||||
if ($provider.Name -ne 'FileSystem') {
|
||||
throw "WSL paths must use the filesystem provider"
|
||||
}
|
||||
if ($WindowsPath.StartsWith('/')) {
|
||||
return $WindowsPath
|
||||
}
|
||||
}
|
||||
$output = & wsl.exe -d $Distro -e wslpath -a "$WindowsPath"
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
throw "wslpath failed for '$WindowsPath' in '$Distro' (exit $exitCode)"
|
||||
}
|
||||
$converted = ($output -join "`n").Trim()
|
||||
if (-not $converted) {
|
||||
throw "wslpath returned no path for '$WindowsPath' in '$Distro'"
|
||||
}
|
||||
return $converted
|
||||
}
|
||||
|
||||
# Handle Windows path (e.g., C:\path\to\file -> /mnt/c/path/to/file)
|
||||
if ($WindowsPath -match '^([A-Z]):(.*)$') {
|
||||
$drive = $matches[1].ToLower()
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# Run with pwsh -NoProfile -File scripts/tests/wsl_paths.ps1.
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. "$PSScriptRoot/../lib.ps1"
|
||||
|
||||
# Intercept WSL so these tests never start or register a real runner.
|
||||
$global:WakeyWslTest = @{
|
||||
Calls = [System.Collections.Generic.List[object]]::new()
|
||||
ExitCode = 0
|
||||
# Fake conversion response, chosen to exercise a path containing spaces.
|
||||
Output = '/mnt/c/runner home/config.yaml'
|
||||
}
|
||||
function global:wsl.exe {
|
||||
$global:WakeyWslTest.Calls.Add(@($args))
|
||||
$global:LASTEXITCODE = $global:WakeyWslTest.ExitCode
|
||||
if ($args[3] -eq 'wslpath') { $global:WakeyWslTest.Output }
|
||||
}
|
||||
|
||||
foreach ($path in @('/home/lda/repo/scripts/helper.sh', '/mnt/c/runner home/config.yaml')) {
|
||||
$result = ConvertTo-WslPath -WindowsPath $path -Distro TestDistro
|
||||
if ($result -cne $path) { throw "POSIX path changed: $result" }
|
||||
}
|
||||
if ($global:WakeyWslTest.Calls.Count -ne 0) { throw 'POSIX paths invoked wslpath' }
|
||||
|
||||
foreach ($path in @('C:\runner home\config.yaml', 'C:/runner home/config.yaml', '\\server\share\runner home\config.yaml')) {
|
||||
$global:WakeyWslTest.Calls.Clear()
|
||||
$result = ConvertTo-WslPath -WindowsPath $path -Distro TestDistro
|
||||
if ($result -cne '/mnt/c/runner home/config.yaml') { throw 'Mock Windows conversion failed' }
|
||||
$call = $global:WakeyWslTest.Calls[0]
|
||||
if (($call[0..4] -join ' ') -cne '-d TestDistro -e wslpath -a' -or $call[5] -cne $path) {
|
||||
throw 'Distro or Windows path was not passed intact'
|
||||
}
|
||||
}
|
||||
if ((ConvertTo-WslPath 'D:\runner\config.yaml') -cne '/mnt/d/runner/config.yaml') {
|
||||
throw 'Existing drive conversion changed'
|
||||
}
|
||||
foreach ($path in @('root@router:/root/.bin/', 'relative/file')) {
|
||||
if ((ConvertTo-WslPath $path) -cne $path) { throw 'Existing SCP path handling changed' }
|
||||
}
|
||||
|
||||
# Resolve missing relative paths from the PS location, including dot segments.
|
||||
$temp = Join-Path ([System.IO.Path]::GetTempPath()) "wakey-wsl-paths-$([guid]::NewGuid())"
|
||||
New-Item -ItemType Directory -Path $temp | Out-Null
|
||||
Push-Location $temp
|
||||
try {
|
||||
foreach ($relative in @('new folder/config.yaml', '.\new folder\config.yaml', 'nested/../new folder/config.yaml')) {
|
||||
$expected = Join-Path $temp 'new folder/config.yaml'
|
||||
$global:WakeyWslTest.Calls.Clear()
|
||||
$result = ConvertTo-WslPath -Path $relative -Distro TestDistro
|
||||
if ($IsWindows) {
|
||||
if ($global:WakeyWslTest.Calls[0][5] -cne $expected) { throw 'Relative Windows path was not resolved before conversion' }
|
||||
} elseif ($result -cne $expected -or $global:WakeyWslTest.Calls.Count -ne 0) {
|
||||
throw "Relative POSIX path was not resolved: $result"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Pop-Location
|
||||
Remove-Item -Recurse -Force $temp
|
||||
}
|
||||
|
||||
foreach ($case in @(
|
||||
@{ ExitCode = 1; Output = '' },
|
||||
@{ ExitCode = 1; Output = '/mnt/c/bad' },
|
||||
@{ ExitCode = 0; Output = '' }
|
||||
)) {
|
||||
$global:WakeyWslTest.ExitCode = $case.ExitCode
|
||||
$global:WakeyWslTest.Output = $case.Output
|
||||
$caught = $false
|
||||
try { ConvertTo-WslPath -WindowsPath 'C:\bad' -Distro TestDistro | Out-Null }
|
||||
catch { $caught = $_.Exception.Message -like 'wslpath *' }
|
||||
if (-not $caught) { throw 'Failed/empty wslpath result was accepted' }
|
||||
}
|
||||
|
||||
if (-not $IsWindows) {
|
||||
$global:WakeyWslTest.Calls.Clear()
|
||||
$global:WakeyWslTest.ExitCode = 0
|
||||
& "$PSScriptRoot/../act_runner_wsl.ps1" -Action status -Distro TestDistro
|
||||
if ($global:WakeyWslTest.Calls.Count -ne 1 -or $global:WakeyWslTest.Calls[0][3] -ne 'bash') {
|
||||
throw 'POSIX wrapper paths should invoke only the runner command'
|
||||
}
|
||||
$command = $global:WakeyWslTest.Calls[0][5]
|
||||
if ($command -notlike '*act_runner_fedora.sh*' -or $command -notlike '*ar_data/wsl_runner/config.yaml*') {
|
||||
throw 'Wrapper did not resolve default paths'
|
||||
}
|
||||
$global:WakeyWslTest.Calls.Clear()
|
||||
$global:WakeyWslTest.Output = '/mnt/c/runner home/config.yaml'
|
||||
& "$PSScriptRoot/../act_runner_wsl.ps1" -Action status -Distro TestDistro `
|
||||
-RunnerHome 'new runner home' -RunnerBin 'bin/act_runner' -ConfigPath 'C:\runner home\config.yaml'
|
||||
if ($global:WakeyWslTest.Calls.Count -ne 2 -or $global:WakeyWslTest.Calls[0][3] -ne 'wslpath') {
|
||||
throw 'Explicit Windows config should be converted once before running bash'
|
||||
}
|
||||
$command = $global:WakeyWslTest.Calls[1][5]
|
||||
foreach ($path in @((Join-Path (Get-Location).Path 'new runner home'), (Join-Path (Get-Location).Path 'bin/act_runner'), '/mnt/c/runner home/config.yaml')) {
|
||||
if (-not $command.Contains((Quote-ShArg $path))) { throw "Explicit path missing from runner command: $path" }
|
||||
}
|
||||
}
|
||||
Write-Host 'WSL path tests passed'
|
||||
Reference in new issue
Block a user