GKI build #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # .github/workflows/gki-build.yml | |
| # | |
| # Builds ONE GKI kernel (one KMI) with the chosen root solution + SUSFS, | |
| # renames it with your own branding, and uploads an AnyKernel3 zip. | |
| # | |
| # Callable on its own (workflow_dispatch) or from build-all-gki.yml (workflow_call). | |
| # | |
| # Verified while writing this file: | |
| # - AOSP manifest branches common-<android>-<kver>-<patchlevel> exist | |
| # - susfs4ksu branches gki-android12-5.10 / gki-android13-5.10 / gki-android13-5.15 / | |
| # gki-android14-5.15 / gki-android14-6.1 / gki-android15-6.6 / | |
| # gki-android16-6.12 | |
| # - setup.sh reachable for ReSukiSU, SukiSU-Ultra and KernelSU-Next | |
| # - CONFIG_KPM exists ONLY in SukiSU-Ultra. ReSukiSU and KernelSU-Next do not have it. | |
| # - CONFIG_KSU_SUSFS* are built into ReSukiSU itself. | |
| name: GKI build | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| android_version: { description: "android12 / android13 / android14 / android15 / android16", required: true, default: "android13" } | |
| kernel_version: { description: "5.10 / 5.15 / 6.1 / 6.6 / 6.12", required: true, default: "5.10" } | |
| sub_level: { description: "sub level, e.g. 223", required: true, default: "223" } | |
| os_patch_level: { description: "manifest date, e.g. 2024-11 or lts", required: true, default: "2024-11" } | |
| root_solution: | |
| description: "root solution" | |
| required: true | |
| default: "ReSukiSU" | |
| type: choice | |
| options: [ReSukiSU, SukiSU-Ultra, KernelSU-Next] | |
| ksu_branch: { description: "branch/tag passed to setup.sh (blank = default)", required: false, default: "" } | |
| add_susfs: { description: "apply SUSFS", type: boolean, default: true } | |
| add_kpm: { description: "CONFIG_KPM (SukiSU-Ultra only)", type: boolean, default: false } | |
| kernel_name: { description: "kernel name template", required: false, default: "{base}-evokerr" } | |
| builder: { description: "builder shown in Settings", required: false, default: "evokerr@evokeroot" } | |
| link: { description: "link shown in /proc/version and uname -v", required: false, default: "t.me/evokeroot" } | |
| workflow_call: | |
| inputs: | |
| android_version: { type: string, required: true } | |
| kernel_version: { type: string, required: true } | |
| sub_level: { type: string, required: true } | |
| os_patch_level: { type: string, required: true } | |
| root_solution: { type: string, required: true } | |
| ksu_branch: { type: string, required: false, default: "" } | |
| add_susfs: { type: boolean, required: false, default: true } | |
| add_kpm: { type: boolean, required: false, default: false } | |
| kernel_name: { type: string, required: false, default: "{base}-evokerr" } | |
| builder: { type: string, required: false, default: "evokerr@evokeroot" } | |
| link: { type: string, required: false, default: "t.me/evokeroot" } | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 180 | |
| env: | |
| ANDROID_VER: ${{ inputs.android_version }} | |
| KERNEL_VER: ${{ inputs.kernel_version }} | |
| SUB_LEVEL: ${{ inputs.sub_level }} | |
| PATCH_LEVEL: ${{ inputs.os_patch_level }} | |
| ROOT_SOL: ${{ inputs.root_solution }} | |
| steps: | |
| - name: Checkout (for scripts/brand_kernel.py) | |
| uses: actions/checkout@v4 | |
| - name: Free up disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ | |
| /usr/local/share/boost "$AGENT_TOOLSDIRECTORY" | |
| df -h / | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends \ | |
| git curl wget bc bison flex libssl-dev libelf-dev python3 python3-pip \ | |
| build-essential ccache zip unzip rsync cpio lz4 zstd | |
| mkdir -p "$HOME/bin" | |
| curl -sSL https://storage.googleapis.com/git-repo-downloads/repo -o "$HOME/bin/repo" | |
| chmod a+x "$HOME/bin/repo" | |
| echo "$HOME/bin" >> "$GITHUB_PATH" | |
| git config --global user.name "kernel-builder" | |
| git config --global user.email "builder@localhost" | |
| # A wrong os_patch_level otherwise burns ~15 minutes of sync before failing with a | |
| # confusing repo error. Check the branch first and say exactly what is available. | |
| - name: Pre-flight - does the manifest branch exist | |
| run: | | |
| set -eu | |
| BRANCH="common-${ANDROID_VER}-${KERNEL_VER}-${PATCH_LEVEL}" | |
| echo "Looking for $BRANCH ..." | |
| if curl -sfo /dev/null "https://android.googlesource.com/kernel/manifest/+refs/heads/${BRANCH}?format=JSON"; then | |
| echo "OK: $BRANCH exists." | |
| else | |
| echo "::error::manifest branch '$BRANCH' does not exist." | |
| echo "Available os_patch_level values for ${ANDROID_VER}-${KERNEL_VER}:" | |
| curl -s "https://android.googlesource.com/kernel/manifest/+refs/heads/?format=JSON" \ | |
| | tail -c +6 \ | |
| | grep -o "common-${ANDROID_VER}-${KERNEL_VER}-[0-9-]*" \ | |
| | sort -u | sed 's/.*-\([0-9]\{4\}-[0-9]\{2\}\)$/ \1/' | |
| exit 1 | |
| fi | |
| - name: Sync GKI source | |
| run: | | |
| set -eux | |
| BRANCH="common-${ANDROID_VER}-${KERNEL_VER}-${PATCH_LEVEL}" | |
| mkdir -p kernel && cd kernel | |
| repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -b "$BRANCH" | |
| repo sync -c -j"$(nproc)" --no-tags --no-clone-bundle --force-sync | |
| echo "KERNEL_ROOT=$PWD" >> "$GITHUB_ENV" | |
| - name: Add the root solution | |
| working-directory: kernel | |
| run: | | |
| set -eux | |
| # Which branch actually carries SUSFS was checked by reading kernel/Kconfig on | |
| # every branch of each repo (2026-08-17): | |
| # ReSukiSU main -> 12 CONFIG_KSU_SUSFS* symbols, clones into KernelSU/ | |
| # SukiSU-Ultra builtin -> 10 CONFIG_KSU_SUSFS* symbols, clones into KernelSU/ | |
| # KernelSU-Next - -> no branch has SUSFS, clones into KernelSU-Next/ | |
| # "susfs-main" and "next-susfs" do not exist. Passing a missing branch to | |
| # setup.sh does not fail - it quietly leaves you on the default branch, which is | |
| # how the first SukiSU-Ultra build came out with no SUSFS in it at all. | |
| case "$ROOT_SOL" in | |
| ReSukiSU) | |
| URL="https://raw.githubusercontent.com/ReSukiSU/ReSukiSU/main/kernel/setup.sh" | |
| DEF_BRANCH="main"; KSU_DIR="KernelSU" ;; | |
| SukiSU-Ultra) | |
| URL="https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | |
| DEF_BRANCH="builtin"; KSU_DIR="KernelSU" ;; | |
| KernelSU-Next) | |
| URL="https://raw.githubusercontent.com/KernelSU-Next/KernelSU-Next/dev/kernel/setup.sh" | |
| DEF_BRANCH="stable"; KSU_DIR="KernelSU-Next" ;; | |
| *) echo "unknown root solution: $ROOT_SOL"; exit 1 ;; | |
| esac | |
| BR="${{ inputs.ksu_branch }}" | |
| [ -n "$BR" ] || BR="$DEF_BRANCH" | |
| curl -LSs "$URL" | bash -s "$BR" | |
| test -d "$KSU_DIR" || { echo "::error::$KSU_DIR was not created"; exit 1; } | |
| # refuse the silent fallback described above | |
| GOT="$(git -C "$KSU_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)" | |
| echo "asked for '$BR', got '$GOT'" | |
| if [ "$GOT" != "$BR" ] && [ "$GOT" != "HEAD" ]; then | |
| echo "::error::setup.sh landed on '$GOT' instead of '$BR'. The branch was probably renamed upstream." | |
| exit 1 | |
| fi | |
| echo "KSU_DIR=$KSU_DIR" >> "$GITHUB_ENV" | |
| - name: Apply SUSFS | |
| if: inputs.add_susfs | |
| working-directory: kernel | |
| run: | | |
| set -eux | |
| SUSFS_BRANCH="gki-${ANDROID_VER}-${KERNEL_VER}" | |
| git clone --depth=1 https://gitlab.com/simonpunk/susfs4ksu.git -b "$SUSFS_BRANCH" susfs4ksu | |
| cp susfs4ksu/kernel_patches/fs/* common/fs/ | |
| cp susfs4ksu/kernel_patches/include/linux/* common/include/linux/ | |
| cd common | |
| patch -p1 -F3 --no-backup-if-mismatch \ | |
| < "../susfs4ksu/kernel_patches/50_add_susfs_in_gki-${ANDROID_VER}-${KERNEL_VER}.patch" | |
| # Only the fs/ side is patched here, never the driver. | |
| # | |
| # susfs4ksu also ships KernelSU/10_enable_susfs_for_ksu.patch, but that targets | |
| # upstream KernelSU's source layout. All three forks we build have diverged from | |
| # it, and `patch -F3` is happy to apply the hunks in the wrong place: the result | |
| # compiles calls to ksu_late_loaded() and ksu_lsm_hook_exit(), which do not exist | |
| # in these forks, and the build dies in drivers/kernelsu/core/init.c. | |
| # | |
| # Each fork brings its own SUSFS glue on the branch setup.sh checks out, so the | |
| # patch is not just risky, it is redundant. Do not add it back. | |
| # The options go straight into gki_defconfig rather than through | |
| # GKI_DEFCONFIG_FRAGMENT. build.sh *sources* that variable as shell, which turns | |
| # every line into an exported environment variable - CONFIG_KSU=y then reaches make | |
| # during `mrproper`, where CONFIG_ARM64 is not set yet, and ReSukiSU's Kbuild aborts | |
| # with "only support arm64-v8a, armeabi-v7a, x86-64". | |
| - name: Add the options to gki_defconfig | |
| working-directory: kernel | |
| run: | | |
| set -eux | |
| FRAG="$(mktemp)" | |
| { | |
| echo "CONFIG_KSU=y" | |
| echo "CONFIG_KSU_MANUAL_HOOK=y" | |
| echo "CONFIG_TMPFS_XATTR=y" | |
| echo "CONFIG_TMPFS_POSIX_ACL=y" | |
| } > "$FRAG" | |
| if [ "${{ inputs.add_susfs }}" = "true" ]; then | |
| { | |
| echo "CONFIG_KSU_SUSFS=y" | |
| echo "CONFIG_KSU_SUSFS_SUS_PATH=y" | |
| echo "CONFIG_KSU_SUSFS_SUS_MOUNT=y" | |
| echo "CONFIG_KSU_SUSFS_SUS_KSTAT=y" | |
| echo "CONFIG_KSU_SUSFS_TRY_UMOUNT=y" | |
| echo "CONFIG_KSU_SUSFS_SPOOF_UNAME=y" | |
| echo "CONFIG_KSU_SUSFS_ENABLE_LOG=y" | |
| echo "CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS=y" | |
| echo "CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG=y" | |
| echo "CONFIG_KSU_SUSFS_OPEN_REDIRECT=y" | |
| echo "CONFIG_KSU_SUSFS_SUS_SU=n" | |
| } >> "$FRAG" | |
| fi | |
| # KPM only exists in SukiSU-Ultra's Kconfig. Setting it anywhere else is a no-op | |
| # that silently does nothing, so refuse loudly instead. | |
| if [ "${{ inputs.add_kpm }}" = "true" ]; then | |
| if [ "$ROOT_SOL" != "SukiSU-Ultra" ]; then | |
| echo "ERROR: CONFIG_KPM only exists in SukiSU-Ultra, not in $ROOT_SOL." | |
| exit 1 | |
| fi | |
| echo "CONFIG_KPM=y" >> "$FRAG" | |
| fi | |
| # drop symbols the chosen driver does not define, so the build does not warn | |
| for sym in $(grep -o '^CONFIG_[A-Z0-9_]*' "$FRAG"); do | |
| name="${sym#CONFIG_}" | |
| if ! grep -rq "config $name\b" "$KSU_DIR" common/fs 2>/dev/null; then | |
| case "$name" in | |
| TMPFS_XATTR|TMPFS_POSIX_ACL) : ;; # kernel's own symbols | |
| *) echo " dropping unsupported $sym"; sed -i "/^${sym}=/d" "$FRAG" ;; | |
| esac | |
| fi | |
| done | |
| DEFCONFIG="common/arch/arm64/configs/gki_defconfig" | |
| # replace rather than append, so a re-run does not stack duplicates | |
| while read -r line; do | |
| [ -n "$line" ] || continue | |
| key="${line%%=*}" | |
| sed -i "/^${key}=/d;/^# ${key} is not set/d" "$DEFCONFIG" | |
| echo "$line" >> "$DEFCONFIG" | |
| done < "$FRAG" | |
| # build.config.gki runs check_defconfig as a POST_DEFCONFIG_CMD. It re-runs | |
| # savedefconfig and demands the result match gki_defconfig byte for byte, which | |
| # no added option can ever satisfy - savedefconfig reorders and folds implied | |
| # symbols. Every GKI builder drops that check; so do we. | |
| for f in common/build.config.gki common/build.config.gki.aarch64 \ | |
| common/build.config.gki-debug.aarch64; do | |
| [ -f "$f" ] || continue | |
| if grep -q "check_defconfig" "$f"; then | |
| sed -i 's/check_defconfig//g' "$f" | |
| echo " dropped check_defconfig from $f" | |
| fi | |
| done | |
| echo "----- added -----"; cat "$FRAG" | |
| echo "----- tail of gki_defconfig -----"; tail -20 "$DEFCONFIG" | |
| # Say plainly when SUSFS was asked for but the driver cannot provide it, instead | |
| # of shipping a kernel that quietly has none. KernelSU-Next is in that position: | |
| # no branch of it defines CONFIG_KSU_SUSFS. | |
| SUFFIX="" | |
| if [ "${{ inputs.add_susfs }}" = "true" ] && ! grep -q '^CONFIG_KSU_SUSFS=y' "$FRAG"; then | |
| echo "::warning::SUSFS was requested but $ROOT_SOL does not define CONFIG_KSU_SUSFS on this branch. Building without it." | |
| SUFFIX="-noSUSFS" | |
| fi | |
| echo "NAME_SUFFIX=$SUFFIX" >> "$GITHUB_ENV" | |
| - name: Build | |
| working-directory: kernel | |
| run: | | |
| set -eux | |
| if [ -f build/build.sh ]; then | |
| # 5.10 / 5.15 use the legacy build.sh. | |
| # No GKI_DEFCONFIG_FRAGMENT here on purpose - the options are already inside | |
| # gki_defconfig, and build.sh sources that variable as shell. | |
| # No HOSTLD override: the tree ships its own LLVM under prebuilts and build.sh | |
| # puts it on PATH. Pointing at /usr/bin/ld.lld only works if lld happens to be | |
| # installed system-wide, and on a stock runner it is not. | |
| KMI_SYMBOL_LIST_STRICT_MODE=0 \ | |
| LTO=thin \ | |
| BUILD_CONFIG=common/build.config.gki.aarch64 \ | |
| build/build.sh | |
| else | |
| # 6.1+ use kleaf / bazel, which reads the same gki_defconfig | |
| tools/bazel build --config=fast --lto=thin //common:kernel_aarch64_dist | |
| fi | |
| - name: Locate the built Image | |
| working-directory: kernel | |
| run: | | |
| set -eux | |
| for p in \ | |
| "out/${ANDROID_VER}-${KERNEL_VER}/dist/Image" \ | |
| "out/android-${KERNEL_VER}/dist/Image" \ | |
| "bazel-bin/common/kernel_aarch64/Image" ; do | |
| if [ -f "$p" ]; then echo "IMAGE=$PWD/$p" >> "$GITHUB_ENV"; exit 0; fi | |
| done | |
| echo "Image not found:"; find . -name Image -type f | head -20; exit 1 | |
| - name: Rename the kernel | |
| run: | | |
| set -eux | |
| python3 "$GITHUB_WORKSPACE/scripts/brand_kernel.py" \ | |
| --image "$IMAGE" \ | |
| --name '${{ inputs.kernel_name }}' \ | |
| --builder '${{ inputs.builder }}' \ | |
| --link '${{ inputs.link }}' | |
| - name: Package AnyKernel3 | |
| run: | | |
| set -eux | |
| git clone --depth=1 https://github.com/osm0sis/AnyKernel3.git | |
| rm -rf AnyKernel3/.git AnyKernel3/README.md | |
| sed -i "s/do.devicecheck=1/do.devicecheck=0/;s/is_slot_device=0/is_slot_device=auto/" AnyKernel3/anykernel.sh | |
| sed -i "s|kernel.string=.*|kernel.string=${{ inputs.builder }} \| ${{ inputs.link }}|" AnyKernel3/anykernel.sh | |
| cp "$IMAGE" AnyKernel3/Image | |
| NAME="${ROOT_SOL}-${ANDROID_VER}-${KERNEL_VER}.${SUB_LEVEL}-${PATCH_LEVEL}${NAME_SUFFIX}" | |
| echo "FILE_NAME=$NAME" >> "$GITHUB_ENV" | |
| - name: Upload | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.FILE_NAME }}-AnyKernel3 | |
| path: ./AnyKernel3/** |