build(release): add release packaging scripts, rename guest kernel to kuyase-kernel-arm64.img, and generate SHA256SUMS

This commit is contained in:
Georgiy
2026-11-01 01:53:53 +03:00
Unverified
parent 737da8980c
commit bc7e0660d5
7 changed files with 186 additions and 8 deletions
+2
View File
@@ -11,7 +11,9 @@ backups/*
# Built binaries (compiled from source via cargo & build-kernel.sh)
Releases/
kuyase-bins.tar.gz
kuyase-kernel-arm64.img
kernel/Image
kernel/kuyase-kernel-arm64.img
guest-init/bin/qemu-hw-bridge
# Rust build artifacts
+2 -2
View File
@@ -15,7 +15,7 @@
## Быстрый старт (готовая портативная сборка)
Для запуска готовой сборки из [**GitHub Releases**](https://github.com/vypivshiy/kuyase/releases) (`kuyase-win64-portable.zip` для Windows x64 или `kuyase-linux64-portable.tar.gz` для Linux x64) не требуется устанавливать Rust, WSL2 или `adb` — ядро `Image`, гостевой инициализатор и демон `qemu-hw-bridge` уже включены в архив.
Для запуска готовой сборки из [**GitHub Releases**](https://github.com/vypivshiy/kuyase/releases) (`kuyase-win64-portable.zip` для Windows x64 или `kuyase-linux64-portable.tar.gz` для Linux x64) не требуется устанавливать Rust, WSL2 или `adb` — пропатченное гостевое ядро `kuyase-kernel-arm64.img`, гостевой инициализатор и демон `qemu-hw-bridge` уже включены в архив.
### 1. Установка QEMU
@@ -50,7 +50,7 @@
rustup target add armv7-unknown-linux-musleabihf
```
* **QEMU** (`qemu-system-aarch64` в `./bin/`, `./Releases/` или системном `PATH`).
* **Гостевое ядро Linux** (`./kernel/Image` или `./Releases/Image`).
* **Гостевое ядро Linux** (`./kuyase-kernel-arm64.img`, `./kernel/kuyase-kernel-arm64.img` или `./Releases/kuyase-kernel-arm64.img` — можно скачать из [**GitHub Releases**](https://github.com/vypivshiy/kuyase/releases)).
### 2. Сборка гостевого моста и десктопного приложения
+4 -1
View File
@@ -345,7 +345,10 @@ pub fn render_launcher_pane(ui: &mut egui::Ui, app: &mut GuiAppState) {
};
(true, format!("{}{}", p.display(), suffix))
}
None => (false, "Не найдено (выберите файл Image)".to_string()),
None => (
false,
"Не найдено (выберите kuyase-kernel-arm64.img)".to_string(),
),
};
egui::Grid::new("vm_paths_settings_grid")
+2 -2
View File
@@ -743,8 +743,8 @@ impl GuiAppState {
thread::spawn(move || {
if let Some(p) = run_os_custom_file_dialog(
&start_dir,
"Выберите образ гостевого ядра Linux (Image)",
"Ядро Linux (Image*)|Image*;*vmlinuz*|Все файлы (*.*)|*.*",
"Выберите образ гостевого ядра Linux (kuyase-kernel-arm64.img / Image)",
"Ядро Linux (kuyase-kernel-arm64.img; Image*)|*.img;Image*;*vmlinuz*|Все файлы (*.*)|*.*",
) {
*slot.lock().unwrap() = Some(p);
}
+10 -3
View File
@@ -22,8 +22,11 @@ pub fn resolve_repo_root() -> PathBuf {
}
}
if let Ok(exe) = env::current_exe() {
let has_local_kernel = |dir: &Path| {
dir.join("kuyase-kernel-arm64.img").is_file() || dir.join("Image").is_file()
};
if let Some(exe_dir) = exe.parent() {
if exe_dir.join("Image").is_file() && !exe_dir.join("..").join("Cargo.toml").is_file() {
if has_local_kernel(exe_dir) && !exe_dir.join("..").join("Cargo.toml").is_file() {
return exe_dir.to_path_buf();
}
}
@@ -36,7 +39,7 @@ pub fn resolve_repo_root() -> PathBuf {
}
}
if let Some(exe_dir) = exe.parent() {
if exe_dir.join("Image").is_file() {
if has_local_kernel(exe_dir) {
return exe_dir.to_path_buf();
}
}
@@ -95,6 +98,10 @@ pub fn resolve_qemu_binary(root: &Path) -> Result<PathBuf, String> {
pub fn resolve_kernel_image(root: &Path) -> Result<PathBuf, String> {
let candidates = [
root.join("kuyase-kernel-arm64.img"),
root.join("kernel").join("kuyase-kernel-arm64.img"),
root.join("Releases").join("kuyase-kernel-arm64.img"),
root.join("bin").join("kuyase-kernel-arm64.img"),
root.join("Image"),
root.join("kernel").join("Image"),
root.join("Releases").join("Image"),
@@ -106,7 +113,7 @@ pub fn resolve_kernel_image(root: &Path) -> Result<PathBuf, String> {
}
}
Err(format!(
"Guest Linux kernel Image not found in {}/Image, {}/kernel/Image, {}/Releases/Image, or {}/bin/Image",
"Guest Linux kernel not found in {}/kuyase-kernel-arm64.img, {}/kernel/kuyase-kernel-arm64.img, {}/Releases/kuyase-kernel-arm64.img, or {}/kernel/Image",
root.display(),
root.display(),
root.display(),
+95
View File
@@ -0,0 +1,95 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Сборка всех релизных артефактов KUYASE для GitHub Releases (Windows x64 + ARMv7 guest bridge + Linux x64 через WSL2 + SHA256SUMS.txt).
.EXAMPLE
pwsh -File scripts/build-releases.ps1
#>
param(
[switch]$SkipLinux
)
$ErrorActionPreference = "Stop"
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
Set-Location $RepoRoot
New-Item -ItemType Directory -Force -Path "guest-init/bin", "Releases", "kernel" | Out-Null
Write-Host "=== [1/4] Кросс-компиляция гостевого моста qemu-hw-bridge (armv7-unknown-linux-musleabihf) ===" -ForegroundColor Cyan
cargo build --release -p qemu-hw-bridge --target armv7-unknown-linux-musleabihf
if ($LASTEXITCODE -ne 0) { throw "Ошибка сборки qemu-hw-bridge" }
Copy-Item -Force "target/armv7-unknown-linux-musleabihf/release/qemu-hw-bridge" "guest-init/bin/qemu-hw-bridge"
Copy-Item -Force "target/armv7-unknown-linux-musleabihf/release/qemu-hw-bridge" "Releases/qemu-hw-bridge"
Write-Host "=== [2/4] Сборка десктопного приложения и CLI для Windows x64 (kuyase.exe) ===" -ForegroundColor Cyan
cargo build --release -p kuyase
if ($LASTEXITCODE -ne 0) { throw "Ошибка сборки kuyase.exe" }
Copy-Item -Force "target/release/kuyase.exe" "Releases/kuyase.exe"
if (Test-Path "kernel/kuyase-kernel-arm64.img") {
Copy-Item -Force "kernel/kuyase-kernel-arm64.img" "Releases/kuyase-kernel-arm64.img"
} elseif (Test-Path "kernel/Image") {
Copy-Item -Force "kernel/Image" "Releases/kuyase-kernel-arm64.img"
} elseif ((Test-Path "Releases/Image") -and -not (Test-Path "Releases/kuyase-kernel-arm64.img")) {
Copy-Item -Force "Releases/Image" "Releases/kuyase-kernel-arm64.img"
}
if (-not (Test-Path "Releases/kuyase-kernel-arm64.img")) {
throw "Не найден образ гостевого ядра Releases/kuyase-kernel-arm64.img (или kernel/Image)"
}
Copy-Item -Force "Releases/kuyase-kernel-arm64.img" "kernel/kuyase-kernel-arm64.img"
Copy-Item -Force "Releases/kuyase-kernel-arm64.img" "kernel/Image"
Write-Host "=== [3/4] Упаковка Releases/kuyase-win64-portable.zip ===" -ForegroundColor Cyan
$StageWin = Join-Path ([System.IO.Path]::GetTempPath()) "kuyase-win64-stage-$PID"
if (Test-Path $StageWin) { Remove-Item -Recurse -Force $StageWin }
New-Item -ItemType Directory -Force -Path $StageWin, (Join-Path $StageWin "examples"), (Join-Path $StageWin "scripts") | Out-Null
try {
Copy-Item -Force "Releases/kuyase.exe" (Join-Path $StageWin "kuyase.exe")
Copy-Item -Force "Releases/kuyase-kernel-arm64.img" (Join-Path $StageWin "kuyase-kernel-arm64.img")
Copy-Item -Force "Releases/qemu-hw-bridge" (Join-Path $StageWin "qemu-hw-bridge")
Copy-Item -Force "README.md" (Join-Path $StageWin "README.md")
Copy-Item -Force "DUMP_GUIDE.md" (Join-Path $StageWin "DUMP_GUIDE.md")
Copy-Item -Force "examples/README.md" (Join-Path $StageWin "examples/README.md")
Copy-Item -Force "examples/test_yam_lineup.py" (Join-Path $StageWin "examples/test_yam_lineup.py")
Copy-Item -Force "scripts/dump-from-adb.ps1", "scripts/dump-from-adb.sh", "scripts/sanitize-rootfs.ps1", "scripts/sanitize-rootfs.sh" (Join-Path $StageWin "scripts/")
$WinZip = Join-Path $RepoRoot "Releases/kuyase-win64-portable.zip"
if (Test-Path $WinZip) { Remove-Item -Force $WinZip }
Compress-Archive -Path (Join-Path $StageWin "*") -DestinationPath $WinZip -Force
} finally {
if (Test-Path $StageWin) { Remove-Item -Recurse -Force $StageWin }
}
if (-not $SkipLinux) {
Write-Host "=== [4/4] Сборка Linux x86_64 бинарника и архива через WSL2 ===" -ForegroundColor Cyan
$WslRepo = (wsl -e wslpath -u ($RepoRoot -replace '\\', '/')).Trim()
wsl -e bash -lc "cd '$WslRepo' && SKIP_BRIDGE=1 CARGO_TARGET_DIR=/tmp/kuyase-linux-target bash scripts/build-releases.sh"
if ($LASTEXITCODE -ne 0) { throw "Ошибка сборки Linux-релиза в WSL2" }
}
$ReleaseAssets = @(
"kuyase-win64-portable.zip",
"kuyase-linux64-portable.tar.gz",
"kuyase-kernel-arm64.img"
)
$SumLines = @()
foreach ($fname in $ReleaseAssets) {
$fpath = Join-Path $RepoRoot "Releases/$fname"
if (Test-Path $fpath) {
$hash = (Get-FileHash -Path $fpath -Algorithm SHA256).Hash.ToLowerInvariant()
$SumLines += "$hash $fname"
}
}
$ShaFile = Join-Path $RepoRoot "Releases/SHA256SUMS.txt"
[System.IO.File]::WriteAllLines($ShaFile, $SumLines, (New-Object System.Text.UTF8Encoding($false)))
Write-Host "`n=== Готовые файлы для загрузки в GitHub Releases (Releases/): ===" -ForegroundColor Green
Get-ChildItem "Releases" | Where-Object { $_.Name -in ($ReleaseAssets + @("SHA256SUMS.txt")) } |
Select-Object Name, @{Name="SizeMB";Expression={[math]::Round($_.Length / 1MB, 2)}}, LastWriteTime |
Format-Table -AutoSize
Write-Host "=== SHA256SUMS.txt ===" -ForegroundColor Yellow
Get-Content $ShaFile
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# KUYASE — Сборка релизных бинарников и портативных архивов для GitHub Releases (Linux / WSL2)
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${REPO_ROOT}"
export PATH="${HOME}/.cargo/bin:${PATH}"
if [ "${SKIP_BRIDGE:-0}" != "1" ]; then
echo "=== [1/4] Кросс-компиляция гостевого моста qemu-hw-bridge (armv7-unknown-linux-musleabihf) ==="
if command -v rustup >/dev/null 2>&1; then
rustup target list --installed | grep -q '^armv7-unknown-linux-musleabihf$' || rustup target add armv7-unknown-linux-musleabihf
fi
cargo build --release -p qemu-hw-bridge --target armv7-unknown-linux-musleabihf
mkdir -p guest-init/bin Releases
cp -f target/armv7-unknown-linux-musleabihf/release/qemu-hw-bridge guest-init/bin/qemu-hw-bridge
cp -f target/armv7-unknown-linux-musleabihf/release/qemu-hw-bridge Releases/qemu-hw-bridge
chmod 0755 guest-init/bin/qemu-hw-bridge Releases/qemu-hw-bridge
else
echo "=== [1/4] Использование готового гостевого моста Releases/qemu-hw-bridge ==="
fi
echo "=== [2/4] Сборка нативного бинарника kuyase (Linux x86_64) ==="
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target-linux}" cargo build --release -p kuyase
LINUX_BIN="${CARGO_TARGET_DIR:-target-linux}/release/kuyase"
cp -f "${LINUX_BIN}" Releases/kuyase
chmod 0755 Releases/kuyase
if [ -f kernel/kuyase-kernel-arm64.img ]; then
cp -f kernel/kuyase-kernel-arm64.img Releases/kuyase-kernel-arm64.img
elif [ -f kernel/Image ]; then
cp -f kernel/Image Releases/kuyase-kernel-arm64.img
elif [ -f Releases/Image ] && [ ! -f Releases/kuyase-kernel-arm64.img ]; then
cp -f Releases/Image Releases/kuyase-kernel-arm64.img
fi
if [ ! -f Releases/kuyase-kernel-arm64.img ]; then
echo "[kuyase] Ошибка: не найден образ ядра Releases/kuyase-kernel-arm64.img (или kernel/Image)" >&2
exit 1
fi
mkdir -p kernel
cp -f Releases/kuyase-kernel-arm64.img kernel/kuyase-kernel-arm64.img
cp -f Releases/kuyase-kernel-arm64.img kernel/Image
echo "=== [3/4] Упаковка Releases/kuyase-linux64-portable.tar.gz ==="
STAGE_LINUX="$(mktemp -d)"
trap 'rm -rf "${STAGE_LINUX}"' EXIT
mkdir -p "${STAGE_LINUX}/examples" "${STAGE_LINUX}/scripts"
cp -f Releases/kuyase "${STAGE_LINUX}/kuyase"
cp -f Releases/kuyase-kernel-arm64.img "${STAGE_LINUX}/kuyase-kernel-arm64.img"
cp -f Releases/qemu-hw-bridge "${STAGE_LINUX}/qemu-hw-bridge"
chmod 0755 "${STAGE_LINUX}/kuyase" "${STAGE_LINUX}/qemu-hw-bridge"
cp -f README.md DUMP_GUIDE.md "${STAGE_LINUX}/"
cp -f examples/README.md examples/test_yam_lineup.py "${STAGE_LINUX}/examples/"
cp -f scripts/dump-from-adb.sh scripts/dump-from-adb.ps1 scripts/sanitize-rootfs.sh scripts/sanitize-rootfs.ps1 "${STAGE_LINUX}/scripts/"
chmod 0755 "${STAGE_LINUX}/scripts/"*.sh "${STAGE_LINUX}/examples/test_yam_lineup.py" 2>/dev/null || true
tar -czf Releases/kuyase-linux64-portable.tar.gz -C "${STAGE_LINUX}" \
kuyase kuyase-kernel-arm64.img qemu-hw-bridge README.md DUMP_GUIDE.md examples scripts
echo "=== [4/4] Генерация контрольных сумм Releases/SHA256SUMS.txt ==="
(
cd Releases
FILES=()
[ -f kuyase-win64-portable.zip ] && FILES+=(kuyase-win64-portable.zip)
[ -f kuyase-linux64-portable.tar.gz ] && FILES+=(kuyase-linux64-portable.tar.gz)
[ -f kuyase-kernel-arm64.img ] && FILES+=(kuyase-kernel-arm64.img)
sha256sum "${FILES[@]}" > SHA256SUMS.txt
)
cat Releases/SHA256SUMS.txt