diff --git a/.github/workflows/build-archive.yml b/.github/workflows/build-archive.yml index 76e1e79e..f79650da 100644 --- a/.github/workflows/build-archive.yml +++ b/.github/workflows/build-archive.yml @@ -33,10 +33,10 @@ jobs: fw-password: ${{ secrets.TEST_FW_PASSWORD }} fw-url: ${{ secrets.TEST_FW_URL }} - - name: Download tools artifact from test job + - name: Download magiskboot-rs artifact uses: actions/download-artifact@v8 with: - name: tools-exe + name: magiskboot-rs-windows path: bin/tools/ - name: Download qdl-rs artifact @@ -62,7 +62,6 @@ jobs: $requiredFiles = @( 'bin/tools/qdl-rs.exe', 'bin/tools/magiskboot.exe', - 'bin/tools/magiskboot_xz_helper.exe', 'bin/tools/adb.exe', 'bin/tools/fastboot.exe', 'bin/tools/avbtool-rs.exe', diff --git a/.github/workflows/build-magiskboot-rs.yml b/.github/workflows/build-magiskboot-rs.yml new file mode 100644 index 00000000..7f2cb302 --- /dev/null +++ b/.github/workflows/build-magiskboot-rs.yml @@ -0,0 +1,47 @@ +name: Build magiskboot-rs + +on: + push: + paths: + - 'vendor/magiskboot-rs/**' + - '.github/workflows/build-magiskboot-rs.yml' + pull_request: + paths: + - 'vendor/magiskboot-rs/**' + - '.github/workflows/build-magiskboot-rs.yml' + workflow_call: + +jobs: + build-windows: + runs-on: windows-latest + timeout-minutes: 20 + defaults: + run: + shell: pwsh + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-pc-windows-msvc + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: vendor/magiskboot-rs + cache-on-failure: true + + - name: Build magiskboot-rs + working-directory: vendor/magiskboot-rs + run: cargo build --release + + - name: Smoke test + run: vendor/magiskboot-rs/target/release/magiskboot.exe sha1 vendor/magiskboot-rs/LICENSE + + - name: Upload artifact + uses: actions/upload-artifact@v7 + with: + name: magiskboot-rs-windows + path: vendor/magiskboot-rs/target/release/magiskboot.exe + if-no-files-found: error diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a6dc3914..489a923a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,9 @@ jobs: build-avbtool-rs: uses: ./.github/workflows/build-avbtool-rs.yml + build-magiskboot-rs: + uses: ./.github/workflows/build-magiskboot-rs.yml + preflight: needs: test runs-on: windows-latest @@ -50,7 +53,7 @@ jobs: "- Config version matches tag ?" >> $env:GITHUB_STEP_SUMMARY build: - needs: [preflight, build-qdlrs, build-avbtool-rs] + needs: [preflight, build-qdlrs, build-avbtool-rs, build-magiskboot-rs] uses: ./.github/workflows/build-archive.yml with: tag-name: ${{ needs.preflight.outputs.tag_name }} @@ -71,6 +74,7 @@ jobs: uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.preflight.outputs.tag_name }} + draft: true prerelease: true generate_release_notes: true files: | diff --git a/.github/workflows/windows_test.yml b/.github/workflows/windows_test.yml index dc27c90e..a2cb34d7 100644 --- a/.github/workflows/windows_test.yml +++ b/.github/workflows/windows_test.yml @@ -21,51 +21,11 @@ jobs: build-avbtool-rs: uses: ./.github/workflows/build-avbtool-rs.yml - build-tools: - runs-on: windows-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v6 - with: - submodules: true - - - name: Setup Python & Dependencies - uses: ./.github/actions/setup-python-deps - with: - python-version: '3.14' - - - name: Cache and Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: MSYS - install: gcc cmake make zlib-devel git - cache: true - - - name: Cache Compiled Tools - uses: actions/cache@v5 - with: - path: bin/tools - key: tools-bin-v4-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('vendor/MagiskbootAlone/**', 'tests/scripts/build_tools.py', 'tests/scripts/cache_fw.py', 'bin/ltbox/config.json', '.github/ci-tools.json', '.github/actions/setup-fw-env/action.yml', '.github/workflows/windows_test.yml') }} - restore-keys: | - tools-bin-v4-${{ runner.os }}-${{ runner.arch }}- - - - name: Build magiskboot - run: python tests/scripts/build_tools.py - - - name: Bundle CI tools - run: python .github/scripts/release/bundle-tools.py - - - name: Upload tools artifact - uses: actions/upload-artifact@v7 - with: - name: tools-exe - path: | - bin/tools/magiskboot.exe - bin/tools/magiskboot_xz_helper.exe - bin/tools/*.dll + build-magiskboot-rs: + uses: ./.github/workflows/build-magiskboot-rs.yml integration-test: - needs: [unit-test, build-tools, build-avbtool-rs] + needs: [unit-test, build-magiskboot-rs, build-avbtool-rs] runs-on: windows-latest timeout-minutes: 30 env: @@ -92,10 +52,10 @@ jobs: fw-password: ${{ secrets.TEST_FW_PASSWORD }} fw-url: ${{ secrets.TEST_FW_URL }} - - name: Download tools artifact + - name: Download magiskboot-rs artifact uses: actions/download-artifact@v8 with: - name: tools-exe + name: magiskboot-rs-windows path: bin/tools - name: Download avbtool-rs artifact diff --git a/.gitmodules b/.gitmodules index c825f7f1..de645111 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,13 +1,9 @@ [submodule "vendor/qdlrs"] path = vendor/qdlrs url = https://github.com/miner7222/qdlrs.git -[submodule "vendor/avb"] - path = vendor/avb - url = https://android.googlesource.com/platform/external/avb - branch = refs/heads/main-kernel -[submodule "vendor/MagiskbootAlone"] - path = vendor/MagiskbootAlone - url = https://github.com/miner7222/MagiskbootAlone.git [submodule "vendor/avbtool-rs"] path = vendor/avbtool-rs url = https://github.com/miner7222/avbtool-rs +[submodule "vendor/magiskboot-rs"] + path = vendor/magiskboot-rs + url = https://github.com/miner7222/magiskboot-rs diff --git a/bin/ltbox/utils.py b/bin/ltbox/utils.py index a1ff5f60..e00fd27f 100644 --- a/bin/ltbox/utils.py +++ b/bin/ltbox/utils.py @@ -80,9 +80,6 @@ def _get_tool_env() -> dict: env = os.environ.copy() paths = [str(const.TOOLS_DIR)] env["PATH"] = os.pathsep.join(paths) + os.pathsep + env["PATH"] - magiskboot_xz_helper = const.TOOLS_DIR / "magiskboot_xz_helper.exe" - if magiskboot_xz_helper.exists(): - env["MAGISKBOOT_RUST_XZ_HELPER"] = str(magiskboot_xz_helper) return env diff --git a/tests/actions/integration/fixtures.py b/tests/actions/integration/fixtures.py index 283ba502..b359df6f 100644 --- a/tests/actions/integration/fixtures.py +++ b/tests/actions/integration/fixtures.py @@ -9,7 +9,6 @@ _REAL_TOOLS_DIR = Path(__file__).resolve().parents[3] / "bin" / "tools" MAGISKBOOT_PATH = _REAL_TOOLS_DIR / "magiskboot.exe" -MAGISKBOOT_XZ_HELPER_PATH = _REAL_TOOLS_DIR / "magiskboot_xz_helper.exe" KPTOOLS_PATH = _REAL_TOOLS_DIR / "kptools.exe" @@ -66,10 +65,6 @@ def _copy_bundled_magiskboot(tools_dir: Path) -> Path: magiskboot_exe = tools_dir / "magiskboot.exe" shutil.copy(MAGISKBOOT_PATH, magiskboot_exe) - if MAGISKBOOT_XZ_HELPER_PATH.exists(): - shutil.copy(MAGISKBOOT_XZ_HELPER_PATH, tools_dir / "magiskboot_xz_helper.exe") - for dll_file in MAGISKBOOT_PATH.parent.glob("*.dll"): - shutil.copy(dll_file, tools_dir / dll_file.name) return magiskboot_exe diff --git a/tests/conftest.py b/tests/conftest.py index f78ef709..fa28048e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,10 +9,7 @@ ROOT = Path(__file__).resolve().parents[1] BIN_PATH = ROOT / "bin" -INTEGRATION_TOOL_FILES = ( - "magiskboot.exe", - "magiskboot_xz_helper.exe", -) +INTEGRATION_TOOL_FILES = ("magiskboot.exe",) if str(BIN_PATH) not in sys.path: sys.path.insert(0, str(BIN_PATH)) diff --git a/tests/core/test_conftest.py b/tests/core/test_conftest.py index 6007b40b..e3ff625c 100644 --- a/tests/core/test_conftest.py +++ b/tests/core/test_conftest.py @@ -15,12 +15,9 @@ def test_integration_tools_ready_true_when_all_downloaded_tools_exist( with patch.object(conftest, "ROOT", tmp_path): assert conftest._integration_tools_ready() is True - def test_integration_tools_ready_false_when_any_required_tool_missing( - self, tmp_path - ): + def test_integration_tools_ready_false_when_no_tools_exist(self, tmp_path): tools_dir = tmp_path / "bin" / "tools" tools_dir.mkdir(parents=True) - (tools_dir / "magiskboot.exe").write_text("stub", encoding="utf-8") with patch.object(conftest, "ROOT", tmp_path): assert conftest._integration_tools_ready() is False diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 4df421f4..00b78f7a 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -12,11 +12,9 @@ class TestUtils: - def test_get_tool_env_sets_magiskboot_helper_when_present(self, tmp_path): + def test_get_tool_env_prepends_tools_dir_to_path(self, tmp_path): tools_dir = tmp_path / "tools" tools_dir.mkdir() - helper = tools_dir / "magiskboot_xz_helper.exe" - helper.write_text("stub", encoding="utf-8") utils._get_tool_env.cache_clear() with ( @@ -25,22 +23,6 @@ def test_get_tool_env_sets_magiskboot_helper_when_present(self, tmp_path): ): env = utils._get_tool_env() - assert env["MAGISKBOOT_RUST_XZ_HELPER"] == str(helper) - assert env["PATH"].split(";")[0] == str(tools_dir) - utils._get_tool_env.cache_clear() - - def test_get_tool_env_skips_magiskboot_helper_when_missing(self, tmp_path): - tools_dir = tmp_path / "tools" - tools_dir.mkdir() - - utils._get_tool_env.cache_clear() - with ( - patch("ltbox.utils.const.TOOLS_DIR", tools_dir), - patch.dict("ltbox.utils.os.environ", {"PATH": "C:\\base"}, clear=True), - ): - env = utils._get_tool_env() - - assert "MAGISKBOOT_RUST_XZ_HELPER" not in env assert env["PATH"].split(";")[0] == str(tools_dir) utils._get_tool_env.cache_clear() diff --git a/tests/patch/test_root.py b/tests/patch/test_root.py index 7596fb27..ba108241 100644 --- a/tests/patch/test_root.py +++ b/tests/patch/test_root.py @@ -464,7 +464,7 @@ def run(self, *args, **kwargs): assert result == tmp_path / "init_boot_patched.img" assert not any(call and call[0] == "dtb" for call in run_calls) - def test_magisk_cpio_patch_keeps_helper_env(self, tmp_path): + def test_magisk_cpio_patch_passes_env_vars(self, tmp_path): work_dir = tmp_path / "work" work_dir.mkdir() for name in [ @@ -497,16 +497,15 @@ def run(self, *args, **kwargs): (work_dir / "new-boot.img").write_bytes(b"\x00" * 64) return result - helper_path = tmp_path / "bin" / "tools" / "magiskboot_xz_helper.exe" - helper_path.parent.mkdir(parents=True) - helper_path.write_text("stub", encoding="utf-8") + tools_dir = tmp_path / "bin" / "tools" + tools_dir.mkdir(parents=True) with ( patch("ltbox.patch.root.const.BASE_DIR", tmp_path), patch("ltbox.patch.root.const.FN_INIT_BOOT", "init_boot.img"), patch("ltbox.patch.root.const.FN_INIT_BOOT_ROOT", "init_boot_patched.img"), patch("ltbox.patch.root.utils.MagiskBootWrapper", FakeWrapper), - patch("ltbox.patch.root.utils.const.TOOLS_DIR", helper_path.parent), + patch("ltbox.patch.root.utils.const.TOOLS_DIR", tools_dir), ): from ltbox import utils @@ -520,7 +519,6 @@ def run(self, *args, **kwargs): assert result == tmp_path / "init_boot_patched.img" assert captured_env is not None - assert captured_env["MAGISKBOOT_RUST_XZ_HELPER"] == str(helper_path) assert captured_env["KEEPVERITY"] == "true" assert captured_env["KEEPFORCEENCRYPT"] == "true" diff --git a/tests/scripts/build_tools.py b/tests/scripts/build_tools.py index 9e292834..f18f04bb 100644 --- a/tests/scripts/build_tools.py +++ b/tests/scripts/build_tools.py @@ -5,9 +5,8 @@ REPO_ROOT = Path(__file__).resolve().parents[2] TOOLS_DIR = REPO_ROOT / "bin" / "tools" -SUBMODULE_DIR = REPO_ROOT / "vendor" / "MagiskbootAlone" +SUBMODULE_DIR = REPO_ROOT / "vendor" / "magiskboot-rs" MAGISKBOOT_EXE = TOOLS_DIR / "magiskboot.exe" -MAGISKBOOT_XZ_HELPER_EXE = TOOLS_DIR / "magiskboot_xz_helper.exe" VERSION_FILE = TOOLS_DIR / "magiskboot.version" @@ -38,133 +37,42 @@ def _find_cargo_exe() -> str | None: def build(): TOOLS_DIR.mkdir(parents=True, exist_ok=True) - if not SUBMODULE_DIR.exists() or not (SUBMODULE_DIR / "CMakeLists.txt").exists(): - print("[ERROR] vendor/MagiskbootAlone submodule not initialized.") - print(" Run: git submodule update --init vendor/MagiskbootAlone") + if not SUBMODULE_DIR.exists() or not (SUBMODULE_DIR / "Cargo.toml").exists(): + print("[ERROR] vendor/magiskboot-rs submodule not initialized.") + print(" Run: git submodule update --init vendor/magiskboot-rs") raise SystemExit(1) current_sha = _get_submodule_sha() - helper_ready = MAGISKBOOT_XZ_HELPER_EXE.exists() if os.name == "nt" else True - if MAGISKBOOT_EXE.exists() and VERSION_FILE.exists() and helper_ready: + if MAGISKBOOT_EXE.exists() and VERSION_FILE.exists(): cached_sha = VERSION_FILE.read_text(encoding="utf-8").strip() if cached_sha == current_sha: - print("[INFO] Tools are up-to-date. Skipping build.") + print("[INFO] magiskboot is up-to-date. Skipping build.") return - print("[INFO] Building magiskboot from vendor/MagiskbootAlone...") + cargo_exe = _find_cargo_exe() + if cargo_exe is None: + raise RuntimeError("cargo not found; required to build magiskboot-rs") - # Copy submodule source to a temp build dir (avoid polluting the submodule) - build_dir = TOOLS_DIR / "magiskboot_build" - if build_dir.exists(): - shutil.rmtree(build_dir, ignore_errors=True) - if build_dir.exists() and os.name == "nt": - subprocess.run( - ["cmd", "/c", "rmdir", "/s", "/q", str(build_dir)], check=False - ) + print("[INFO] Building magiskboot from vendor/magiskboot-rs...") - shutil.copytree(SUBMODULE_DIR, build_dir, dirs_exist_ok=True) - - cpio_cpp_path = build_dir / "src" / "cpio.cpp" - if cpio_cpp_path.exists(): - content = cpio_cpp_path.read_text(encoding="utf-8") - content = content.replace( - '"/tmp/magiskboot-cpio-XXXXXX"', '"magiskboot-cpio-XXXXXX"' - ) - cpio_cpp_path.write_text(content, encoding="utf-8") - print("[INFO] Patched hardcoded /tmp/ path in cpio.cpp") + subprocess.run( + [cargo_exe, "build", "--release"], + cwd=str(SUBMODULE_DIR), + check=True, + ) if os.name == "nt": - msys_root = Path("C:/msys64") - bash_exe = msys_root / "usr/bin/bash.exe" - - if not bash_exe.exists(): - print( - "[WARN] MSYS2 not found at C:/msys64. Cannot build POSIX C++ code on Windows." - ) - return - - print("[INFO] Installing MSYS2 dependencies (gcc, cmake, make, zlib-devel)...") - subprocess.run( - [ - str(bash_exe), - "-lc", - "pacman -S --noconfirm --overwrite '*' --needed gcc cmake make zlib-devel", - ], - check=True, - ) - - src_dir_msys = build_dir.as_posix() - - build_cmd = ( - f"cd '{src_dir_msys}' && " - f"cmake -S . -B build -G 'Unix Makefiles' " - f"-DMBEDTLS_FATAL_WARNINGS=OFF " - f"-DCMAKE_EXE_LINKER_FLAGS='-static-libgcc -static-libstdc++' " - f"-DCMAKE_CXX_FLAGS='-D_GNU_SOURCE -Wno-error' -DCMAKE_C_FLAGS='-D_GNU_SOURCE -Wno-error' && " - f"cmake --build build --config Release" - ) - subprocess.run([str(bash_exe), "-lc", build_cmd], check=True) - - cargo_exe = _find_cargo_exe() - if cargo_exe is None: - raise RuntimeError( - "cargo not found; required to build magiskboot_xz_helper.exe" - ) - helper_manifest = build_dir / "rust" / "magiskboot_xz_helper" / "Cargo.toml" - subprocess.run( - [ - cargo_exe, - "build", - "--manifest-path", - str(helper_manifest), - "--release", - ], - check=True, - ) - - compiled_exe = build_dir / "build" / "magiskboot.exe" - compiled_helper = ( - build_dir - / "rust" - / "magiskboot_xz_helper" - / "target" - / "release" - / "magiskboot_xz_helper.exe" - ) - if compiled_exe.exists(): - shutil.copy(compiled_exe, MAGISKBOOT_EXE) - if not compiled_helper.exists(): - raise RuntimeError("magiskboot_xz_helper.exe was not produced") - shutil.copy(compiled_helper, MAGISKBOOT_XZ_HELPER_EXE) - - dlls_to_copy = ["msys-2.0.dll", "msys-z.dll"] - usr_bin = msys_root / "usr/bin" - - for dll in list(set(dlls_to_copy)): - src_dll = usr_bin / dll - if src_dll.exists(): - shutil.copy(src_dll, TOOLS_DIR / dll) - print(f"[INFO] Copied {dll} for standalone execution.") - else: - print("[WARN] Build failed to produce magiskboot.exe.") + compiled_exe = SUBMODULE_DIR / "target" / "release" / "magiskboot.exe" else: - subprocess.run( - ["cmake", "-S", str(build_dir), "-B", str(build_dir / "build")], check=True - ) - subprocess.run( - ["cmake", "--build", str(build_dir / "build"), "--config", "Release"], - check=True, - ) - - compiled_exe = build_dir / "build" / "magiskboot" - if compiled_exe.exists(): - shutil.copy(compiled_exe, MAGISKBOOT_EXE) + compiled_exe = SUBMODULE_DIR / "target" / "release" / "magiskboot" - VERSION_FILE.write_text(current_sha, encoding="utf-8") + if not compiled_exe.exists(): + raise RuntimeError(f"Build did not produce {compiled_exe}") - shutil.rmtree(build_dir, ignore_errors=True) - print("[INFO] Successfully built and cached tools.") + shutil.copy(compiled_exe, MAGISKBOOT_EXE) + VERSION_FILE.write_text(current_sha, encoding="utf-8") + print("[INFO] Successfully built and cached magiskboot.") if __name__ == "__main__": diff --git a/vendor/MagiskbootAlone b/vendor/MagiskbootAlone deleted file mode 160000 index 3220998c..00000000 --- a/vendor/MagiskbootAlone +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3220998c9b6ba4e951f1db887e576e95c209afc4 diff --git a/vendor/magiskboot-rs b/vendor/magiskboot-rs new file mode 160000 index 00000000..e245e119 --- /dev/null +++ b/vendor/magiskboot-rs @@ -0,0 +1 @@ +Subproject commit e245e119ea3a87fac195d2d5f0db14e0b4c92e37