diff --git a/README.md b/README.md index fb7e4990..868d8ff6 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,10 @@ Root changes the device security model and can introduce compatibility breakage ModOS defaults to the official `topjohnwu/Magisk` repository. The source remains configurable through `MAGISK[REPOSITORY]` in `env.toml`, but alternate Magisk forks are opt-in rather than part of the GrapheneOS build contract. If you use Zygisk Next for GrapheneOS compatibility, install and manage it separately from the OTA build. Magisk/Zygisk behavior can change across releases, so rooted builds should be revalidated after ROM, Magisk, or Zygisk changes. +ModOS currently pins **official Magisk v30.7** as its avbroot compatibility baseline. This mirrors the current rooted-GrapheneOS reference pairing with avbroot 3.34.1. avbroot 3.34.0 and newer explicitly recognize Magisk 31000, but parser/patch-format support is not the same as target-device runtime validation, so ModOS does not automatically advance to the highest Magisk tag. Override `MAGISK_VERSION` only for an intentional compatibility test. + +With avbroot, Magisk is not updated using the app's normal **Direct install** path. Magisk version changes must be made by repatching/re-signing the OTA and installing that OTA. The manager's **Additional setup / Environment fix** is a separate runtime-environment step and does not replace OTA repatching. + A Magisk-patched OTA is only the boot-image half of a working Magisk installation. On a clean install, after a data wipe, or whenever Magisk reports that additional setup is required, install/open the **matching Magisk manager APK**, accept **Additional setup / Environment fix**, and allow the device to reboot. That step provisions the runtime binaries (including `su`) under `/data/adb/magisk`; those files are device state and cannot be embedded in or verified from an OTA image. After that reboot, verify runtime root on the actual device: diff --git a/env.toml b/env.toml index 7fc53059..e576b6e6 100644 --- a/env.toml +++ b/env.toml @@ -12,6 +12,9 @@ ROM_FAMILY = "grapheneos" # 'MAGISK[REPOSITORY]' = "topjohnwu/Magisk" [build] +# Magisk is pinned deliberately for avbroot compatibility. Bump only after +# reviewing avbroot support and validating the target device/runtime path. +# MAGISK_VERSION = "v30.7" # Legacy single-output switch: false = rootless, true = Magisk. # ROOT = true # Optional override: "rootless", "magisk", or "both". diff --git a/src/ci/selection_variant.sh b/src/ci/selection_variant.sh index df710817..5debe9f2 100755 --- a/src/ci/selection_variant.sh +++ b/src/ci/selection_variant.sh @@ -17,6 +17,7 @@ function selection_variant_manifest() { SELECTION_ROOT SELECTION_MAGISK_PREINIT SELECTION_MAGISK_REPOSITORY + SELECTION_MAGISK_VERSION SELECTION_DEBUG SELECTION_COMPATIBLE_SEPOLICY SELECTION_CLEAR_VBMETA_FLAGS @@ -103,7 +104,13 @@ function selection_variant_manifest() { echo "Error: rooted selection identity has an invalid Magisk repository." >&2 return 1 fi - printf '%s\n' "magisk_repository=${SELECTION_MAGISK_REPOSITORY}" + if [[ ! "${SELECTION_MAGISK_VERSION}" =~ ^v[0-9]+([.][0-9A-Za-z_-]+)*$ ]]; then + echo "Error: rooted selection identity has an invalid Magisk version." >&2 + return 1 + fi + printf '%s\n' \ + "magisk_repository=${SELECTION_MAGISK_REPOSITORY}" \ + "magisk_version=${SELECTION_MAGISK_VERSION}" fi if [[ "${SELECTION_BOOT_ANIMATION}" == 'true' ]]; then diff --git a/src/config_schema.sh b/src/config_schema.sh index 10584759..bbfee73d 100644 --- a/src/config_schema.sh +++ b/src/config_schema.sh @@ -78,6 +78,8 @@ _config_schema_define update_channel 'GRAPHENEOS[UPDATE_CHANNEL]' device string GRAPHENEOS array UPDATE_CHANNEL GRAPHENEOS_UPDATE_CHANNEL '' true existing-or-default _config_schema_define magisk_repository 'MAGISK[REPOSITORY]' device string \ MAGISK array REPOSITORY '' topjohnwu/Magisk false caller-or-default +_config_schema_define magisk_version MAGISK_VERSION build string \ + VERSION array MAGISK MAGISK_VERSION v30.7 true caller-or-existing-or-default _config_schema_define afsr 'ADDITIONALS[AFSR]' build boolean \ ADDITIONALS array AFSR ADDITIONALS_AFSR true true caller-or-existing-or-default _config_schema_define alterinstaller 'ADDITIONALS[ALTERINSTALLER]' build boolean \ diff --git a/src/fetcher.sh b/src/fetcher.sh index 1b3c5f8c..aad429ff 100755 --- a/src/fetcher.sh +++ b/src/fetcher.sh @@ -8,17 +8,21 @@ source src/declarations.sh source src/rom_profiles.sh source src/ota_providers.sh -# Fetch the latest version of GrapheneOS and Magisk and sets up the OTA URL +# Resolve the ROM version plus the configured Magisk compatibility version function get_latest_version() { - local latest_magisk_version=$( - git ls-remote --tags "${DOMAIN}/${MAGISK[REPOSITORY]}.git" | - awk -F'\t' '{print $2}' | - grep -E 'refs/tags/' | - grep -v '\^{}$' | - sed 's/refs\/tags\///' | - sort -V | - tail -n1 - ) + local selected_magisk_version="${VERSION[MAGISK]:-}" + + if [[ -z "${selected_magisk_version}" ]]; then + selected_magisk_version=$( + git ls-remote --tags "${DOMAIN}/${MAGISK[REPOSITORY]}.git" | + awk -F'\t' '{print $2}' | + grep -E 'refs/tags/' | + grep -v '\^{}$' | + sed 's/refs\/tags\///' | + sort -V | + tail -n1 + ) + fi resolve_rom_profile || return 1 @@ -30,12 +34,16 @@ function get_latest_version() { fetch_rom_ota_metadata || return 1 echo -e "${ROM_FAMILY} OTA target: \`${GRAPHENEOS[OTA_TARGET]}\`\nOTA URL: ${GRAPHENEOS[OTA_URL]}\n" - if [[ -z "${latest_magisk_version}" ]]; then - echo -e "Failed to get the latest Magisk version." + if [[ -z "${selected_magisk_version}" ]]; then + echo -e "Failed to resolve the Magisk version." exit 1 - else - VERSION[MAGISK]="${latest_magisk_version}" fi + if [[ ! "${selected_magisk_version}" =~ ^v[0-9]+([.][0-9A-Za-z_-]+)*$ ]]; then + echo -e "Invalid Magisk version tag: ${selected_magisk_version}" + exit 1 + fi + + VERSION[MAGISK]="${selected_magisk_version}" } # Getter function to download the magisk, modules, signatures and tools diff --git a/src/rom_profiles.sh b/src/rom_profiles.sh index 3ed3d523..ea555680 100644 --- a/src/rom_profiles.sh +++ b/src/rom_profiles.sh @@ -151,6 +151,7 @@ function module_selection_fingerprint() { local profile_digest="disabled" local magisk_preinit="disabled" local magisk_repository="disabled" + local magisk_version="disabled" local boot_animation_digest="disabled" local entry local -a module_entries=( @@ -186,6 +187,7 @@ function module_selection_fingerprint() { if [[ "${ADDITIONALS[ROOT]}" == 'true' ]]; then magisk_preinit="${MAGISK[PREINIT]}" magisk_repository="${MAGISK[REPOSITORY]}" + magisk_version="${VERSION[MAGISK]}" if [[ ! "${magisk_preinit}" =~ ^[A-Za-z0-9._-]+$ ]]; then echo "Error: rooted profiles require a canonical Magisk preinit device." >&2 return 1 @@ -194,6 +196,10 @@ function module_selection_fingerprint() { echo "Error: rooted profiles require a canonical Magisk repository." >&2 return 1 fi + if [[ ! "${magisk_version}" =~ ^v[0-9]+([.][0-9A-Za-z_-]+)*$ ]]; then + echo "Error: rooted profiles require a canonical Magisk version tag." >&2 + return 1 + fi fi if [[ "${ADDITIONALS[FDROID_PRIVILEGED_EXTENSION]}" == 'true' ]]; then @@ -214,6 +220,7 @@ function module_selection_fingerprint() { SELECTION_ROOT="${ADDITIONALS[ROOT]}" SELECTION_MAGISK_PREINIT="${magisk_preinit}" SELECTION_MAGISK_REPOSITORY="${magisk_repository}" + SELECTION_MAGISK_VERSION="${magisk_version}" SELECTION_DEBUG="${ADDITIONALS[DEBUG]}" SELECTION_COMPATIBLE_SEPOLICY="${ADDITIONALS[MAS_COMPATIBLE_SEPOLICY]}" SELECTION_CLEAR_VBMETA_FLAGS="${ROM_PROFILE[CLEAR_VBMETA_FLAGS]}" diff --git a/tests/provider_acquisition_test.sh b/tests/provider_acquisition_test.sh index bd32516c..ca2dda47 100644 --- a/tests/provider_acquisition_test.sh +++ b/tests/provider_acquisition_test.sh @@ -43,6 +43,24 @@ reset_acquisition_fixture() { ADDITIONALS_MAS_COMPATIBLE_SEPOLICY="" } +test_pinned_magisk_version_skips_tag_discovery() ( + reset_acquisition_fixture + ROM_FAMILY="grapheneos" + DEVICE_NAME="shiba" + VERSION[MAGISK]="v30.7" + + git() { fail "pinned Magisk version unexpectedly queried git tags"; } + curl() { + local url="${!#}" + [[ "${url}" == "https://releases.grapheneos.org/shiba-stable" ]] || + fail "unexpected GrapheneOS metadata URL: ${url}" + printf '%s\n' '2026071700 1784260800' + } + + get_latest_version >/dev/null + assert_equals "v30.7" "${VERSION[MAGISK]}" "pinned Magisk version" +) + test_grapheneos_text_metadata() ( reset_acquisition_fixture ROM_FAMILY="grapheneos" @@ -200,6 +218,7 @@ test_unsafe_lineageos_metadata_fails_closed() ( fi ) +test_pinned_magisk_version_skips_tag_discovery test_grapheneos_text_metadata test_lineageos_v2_metadata test_lineageos_digest_verification diff --git a/tests/rom_contract_test.sh b/tests/rom_contract_test.sh index dd97d3e4..a9522290 100644 --- a/tests/rom_contract_test.sh +++ b/tests/rom_contract_test.sh @@ -77,10 +77,12 @@ test_profiles_are_stable() { https://download.lineageos.org/api/v2 } -test_magisk_repository_default() ( +test_magisk_compatibility_defaults() ( load_contract assert_equals "topjohnwu/Magisk" "${MAGISK[REPOSITORY]}" \ "default Magisk repository" + assert_equals "v30.7" "${VERSION[MAGISK]}" \ + "default Magisk compatibility version" ) test_unknown_rom_fails_closed() ( @@ -132,7 +134,7 @@ fingerprint() { test_selection_fingerprint() ( local baseline repeated afsr_on afsr_off root_changed rom_changed module_changed local boot_changed second_boot_changed fingerprint_input - local rootless_repo_changed rooted_repo_changed + local rootless_repo_changed rooted_repo_changed rooted_version_changed load_contract set_selection_fixture @@ -180,6 +182,12 @@ test_selection_fingerprint() ( fail "Magisk repository selection did not change the rooted fingerprint" MAGISK[REPOSITORY]="topjohnwu/Magisk" + VERSION[MAGISK]="v30.6" + rooted_version_changed="$(fingerprint)" + [[ "${rooted_version_changed}" != "${root_changed}" ]] || + fail "Magisk version selection did not change the rooted fingerprint" + VERSION[MAGISK]="v30.7" + MAGISK[PREINIT]="sda47" local preinit_changed preinit_changed="$(fingerprint)" @@ -282,7 +290,7 @@ test_output_policy() ( ) test_profiles_are_stable -test_magisk_repository_default +test_magisk_compatibility_defaults test_unknown_rom_fails_closed test_invalid_compatible_sepolicy_fails_closed test_selection_fingerprint diff --git a/tests/upstream_integration_test.sh b/tests/upstream_integration_test.sh index 68250339..eb6d905b 100644 --- a/tests/upstream_integration_test.sh +++ b/tests/upstream_integration_test.sh @@ -13,6 +13,7 @@ grep -Fq 'VERSION[CUSTOTA]="${VERSION[CUSTOTA]:-6.5}"' src/declarations.sh || fa grep -Fq 'VERSION[AVBROOT_SETUP]="634e6185cf70ea3ec9229ae957ddf2304f52e9e8"' src/declarations.sh || fail "maintained helper pin is unexpected" grep -Fq '"version": "2.0.0"' locks/executable-tools-v1.json || fail "AFSR 2.0 lock entry is missing" grep -Fq "topjohnwu/Magisk" src/config_schema.sh || fail "official Magisk default is missing" +grep -Fq "VERSION array MAGISK MAGISK_VERSION v30.7" src/config_schema.sh || fail "Magisk v30.7 compatibility pin is missing" if grep -Fq "pixincreate/Magisk" src/config_schema.sh; then fail "pixincreate Magisk must remain opt-in rather than the default" fi