diff --git a/.github/workflows/cmake-linux-amd64-appimage.yml b/.github/workflows/cmake-linux-amd64-appimage.yml index 40b891f..1ea88ed 100644 --- a/.github/workflows/cmake-linux-amd64-appimage.yml +++ b/.github/workflows/cmake-linux-amd64-appimage.yml @@ -22,19 +22,9 @@ jobs: - os: ubuntu-22.04 qt-ver: 5 qt-pkg: qtbase5-dev qttools5-dev - use-pkexec-launcher: OFF - - os: ubuntu-22.04 - qt-ver: 5 - qt-pkg: qtbase5-dev qttools5-dev - use-pkexec-launcher: ON - - os: ubuntu-24.04 - qt-ver: 6 - qt-pkg: qt6-base-dev qt6-tools-dev - use-pkexec-launcher: OFF - os: ubuntu-24.04 qt-ver: 6 qt-pkg: qt6-base-dev qt6-tools-dev - use-pkexec-launcher: ON runs-on: ${{matrix.os}} steps: @@ -56,7 +46,7 @@ jobs: run: | cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \ -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_PACKAGING=ON \ - -DUSE_PKEXEC_LAUNCHER=${{matrix.use-pkexec-launcher}} \ + -DUSE_PKEXEC_LAUNCHER=ON \ -DBUILD_CLI_UTILITY=ON - name: Build @@ -89,17 +79,15 @@ jobs: uses: actions/upload-artifact@v4 with: # Artifact name - name: QEFI Entry Manager x86_64 AppImage Qt${{matrix.qt-ver}} pkexec-launcher-${{matrix.use-pkexec-launcher}} + name: QEFI Entry Manager x86_64 AppImage Qt${{matrix.qt-ver}} # A file, directory or wildcard pattern that describes what to upload path: ${{github.workspace}}/build/EFI_Entry_Manager-x86_64-Qt${{matrix.qt-ver}}.AppImage - name: Packaging Debian - if: ${{ matrix.use-pkexec-launcher == 'ON' }} working-directory: ${{github.workspace}}/build run: cpack -G DEB - name: Upload a Build Artifact - if: ${{ matrix.use-pkexec-launcher == 'ON' }} uses: actions/upload-artifact@v4 with: # Artifact name diff --git a/.github/workflows/uefi-linux-vm-smoke.yml b/.github/workflows/uefi-linux-vm-smoke.yml new file mode 100644 index 0000000..705192c --- /dev/null +++ b/.github/workflows/uefi-linux-vm-smoke.yml @@ -0,0 +1,218 @@ +name: UEFI Linux VM smoke tests + +on: + push: + branches: [master] + pull_request: + branches: [master] + +env: + BUILD_TYPE: Release + +jobs: + build-appimage: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Install Qt and build dependencies + run: | + sudo apt update -y + sudo apt install -y qt6-base-dev qt6-tools-dev fuse + + - name: Configure CMake + run: | + cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \ + -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_PACKAGING=ON \ + -DUSE_PKEXEC_LAUNCHER=ON \ + -DBUILD_CLI_UTILITY=ON + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Package AppImage + working-directory: ${{github.workspace}}/build + run: | + export QMAKE=/usr/lib/qt6/bin/qmake + make install DESTDIR=AppDir + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + chmod +x linuxdeploy*.AppImage + ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage + mv EFI_Entry_Manager-x86_64.AppImage EFI_Entry_Manager-x86_64-Qt6.AppImage + + - name: Upload AppImage artifact + uses: actions/upload-artifact@v4 + with: + name: qefi-appimage-qt6 + path: ${{github.workspace}}/build/EFI_Entry_Manager-x86_64-Qt6.AppImage + + uefi-vm-test: + needs: build-appimage + runs-on: ubuntu-24.04 + strategy: + matrix: + include: + - distro: fedora + image_url: https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-UEFI-UKI-42-1.1.x86_64.qcow2 + - distro: arch + image_url: https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2 + steps: + - name: Install QEMU and cloud tooling + run: | + sudo apt update -y + sudo apt install -y qemu-system-x86 qemu-utils ovmf cloud-image-utils genisoimage + + - name: Download AppImage artifact + uses: actions/download-artifact@v4 + with: + name: qefi-appimage-qt6 + path: artifacts + + - name: Boot UEFI VM and smoke test AppImage + env: + DISTRO: ${{matrix.distro}} + IMAGE_URL: ${{matrix.image_url}} + run: | + set -euo pipefail + APPIMAGE_PATH="${GITHUB_WORKSPACE}/artifacts/EFI_Entry_Manager-x86_64-Qt6.AppImage" + chmod +x "${APPIMAGE_PATH}" + VM_DIR="${RUNNER_TEMP}/uefi-${DISTRO}" + mkdir -p "${VM_DIR}" + curl -fsSL "${IMAGE_URL}" -o "${VM_DIR}/disk.qcow2" + qemu-img resize "${VM_DIR}/disk.qcow2" 10G + + ssh-keygen -t ed25519 -N "" -f "${VM_DIR}/id_ed25519" + PUB_KEY="$(cat "${VM_DIR}/id_ed25519.pub")" + + cat > "${VM_DIR}/user-data" < /var/tmp/ci-ready" ] + EOF + + cat > "${VM_DIR}/meta-data" </dev/null 2>&1; then + sudo dnf install -y polkit fuse fuse3 mesa-libEGL mesa-dri-drivers libglvnd libglvnd-opengl mesa-libGL fontconfig freetype libX11 libXext libXrender libXfixes libXcursor libXi libXrandr harfbuzz xorg-x11-server-Xvfb + elif command -v pacman >/dev/null 2>&1; then + sudo pacman -Sy --noconfirm polkit fuse2 mesa libglvnd fontconfig freetype2 libx11 libxext libxrender libxfixes libxcursor libxi libxrandr harfbuzz xorg-server-xvfb + fi + + sudo tee /etc/polkit-1/rules.d/49-ci-pkexec.rules >/dev/null <<'POLKIT' + polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.policykit.exec" && + subject.user == "tester") { + return polkit.Result.YES; + } + }); + POLKIT + sudo chmod 0644 /etc/polkit-1/rules.d/49-ci-pkexec.rules + + chmod +x /home/tester/QEFI.AppImage + export QT_QPA_PLATFORM=xcb + export APPIMAGE_EXTRACT_AND_RUN=1 + + set +e + timeout 10s xvfb-run -a /home/tester/QEFI.AppImage > /tmp/qefi.log 2>&1 + status=$? + set -e + + if [ "${status}" -ne 0 ] && [ "${status}" -ne 124 ]; then + echo "AppImage exited with status ${status}" + cat /tmp/qefi.log + exit 1 + fi + + if grep -q "No such file or directory" /tmp/qefi.log; then + cat /tmp/qefi.log + exit 1 + fi + EOSSH diff --git a/qefientrymanager-launcher b/qefientrymanager-launcher index 32364ef..8aa4087 100755 --- a/qefientrymanager-launcher +++ b/qefientrymanager-launcher @@ -1,16 +1,52 @@ #!/usr/bin/env bash -env_array=( - "XDG_CURRENT_DESKTOP=${XDG_CURRENT_DESKTOP}" - "QT_QPA_PLATFORM=${QT_QPA_PLATFORM}" - "QT_QPA_PLATFORMTHEME=${QT_QPA_PLATFORMTHEME}" - "QT_STYLE_OVERRIDE=${QT_STYLE_OVERRIDE}" -) +# Resolve the bundled binary path first; avoid relying on PATH after pkexec +launcher_path="$(readlink -f "${BASH_SOURCE[0]}")" +launcher_dir="$(dirname "${launcher_path}")" +target="${launcher_dir}/QEFIEntryManager" +if [[ ! -x "${target}" ]]; then + target="QEFIEntryManager" +fi + +env_array=() +append_env() { + local key="$1" + local value="${!key-}" + if [[ -n "${value}" ]]; then + env_array+=("${key}=${value}") + fi +} -if [[ -z "${WAYLAND_DISPLAY}" ]]; then - env_array+=("DISPLAY=${DISPLAY}" "XAUTHORITY=${XAUTHORITY}") +# Preserve the minimum GUI/session vars that pkexec strips +append_env XDG_CURRENT_DESKTOP +append_env XDG_SESSION_TYPE +append_env QT_QPA_PLATFORM +append_env QT_QPA_PLATFORMTHEME +append_env QT_STYLE_OVERRIDE +append_env DBUS_SESSION_BUS_ADDRESS + +if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then + # Wayland: pass both runtime dir and display socket + append_env XDG_RUNTIME_DIR + append_env WAYLAND_DISPLAY else - env_array+=("WAYLAND_DISPLAY=${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}") + # X11: pass display and auth cookie + append_env DISPLAY + append_env XAUTHORITY +fi + +if [[ "${EUID}" -eq 0 ]]; then + # If already root, run directly without re-exec + exec "${target}" "$@" +fi + +if [[ -n "${APPIMAGE:-}" && -x "${APPIMAGE}" ]]; then + # AppImages mount their SquashFS via FUSE under the calling user. + # After pkexec, root often cannot read that user FUSE mount unless + # allow_root/allow_other is set, so we re-exec the AppImage as root + # and force extract+run to bypass the mount. + exec pkexec env "${env_array[@]}" APPIMAGE_EXTRACT_AND_RUN=1 "${APPIMAGE}" "$@" fi -exec pkexec env "${env_array[@]}" QEFIEntryManager "$@" +# Fallback for non-AppImage installs +exec pkexec env "${env_array[@]}" "${target}" "$@"