Skip to content

OP13 SukiSU Ultra

OP13 SukiSU Ultra #221

name: OP13 SukiSU Ultra
env:
TZ: Asia/Shanghai
ANDROID_VERSION: android15
KERNEL_VERSION: "6.6"
SUB_VERSION: "89"
DEVICE_NAME: OP13
DEVICE_CODENAME: sun
CCTV18_BUILD_REPO: cctv18/oppo_oplus_realme_sm8750
CCTV18_COMMON_REPO: cctv18/android_kernel_common_oneplus_sm8750
CCTV18_KERNEL_REF: oneplus/sm8750_b_16.0.0_oneplus_13_6.6.118
CCTV18_TOOLCHAIN_RELEASE: LLVM-Clang18-r510928
CCACHE_DIR: ${{ github.workspace }}/.ccache_op13_sukisu_cctv18
CCACHE_MAXSIZE: 8G
on:
workflow_dispatch:
inputs:
profile:
description: "Build profile. stock_daily is recommended for stable daily-driver use."
required: true
type: choice
options: [stock_daily, stock_plus, minimal_safe, debug_only]
default: stock_daily
sukisu_ref:
description: "SukiSU Ultra ref. MUST BE PAIRED WITH susfs_ref AND with the manager APK. DEFAULT f2201c4 = builtin branch, no_su generation, and the newest builtin commit that actually compiles (HEAD e2912817 does not: kernel/feature/kernel_umount.c references kernel_umount_feature_set without defining it). Pair with susfs_ref 40e6c19 and INSTALL THE v4.2.0 MANAGER APK -- its ksud carries the matching UAPI and the susfs CLI needed to hide mounts. Using the v4.1.3 APK with this driver breaks App Profile writes. To stay on v4.1.3, use the OLD pair instead: sukisu 6c13a0695a1115e1000c998872b66f4ff5b2f11e + susfs 83dcd81e7e13440e1b8b756dea4e8311e54f4c3a."
required: true
type: string
default: "f2201c472b607557ed8563e2d1b729af25f3111b"
kpm_enable:
description: "Enable SukiSU KPM support. Kernel support only; no random KPM modules bundled."
required: true
type: boolean
default: true
multi_manager_support:
description: "Enable SukiSU multi-manager support if the selected ref exposes it."
required: true
type: boolean
default: true
SUSFS:
description: "Enable CCTV18 susfs4oki only. No WildKernels/TheWildJames SUSFS tree."
required: true
type: choice
options: ["On", "Off"]
default: "On"
susfs_ref:
description: "CCTV18 susfs4oki ref. MUST MATCH sukisu_ref generation. 40e6c19 (default) uses susfs_is_current_proc_no_su() (upstream fix 525c450) and includes the later SUS_MOUNT hardening -- pair with sukisu f2201c4 + the v4.2.0 manager APK. 83dcd81 is the last umounted-generation commit -- pair with sukisu 6c13a06 + the v4.1.3 APK. A mismatched pair makes the kernel skip the su redirect for every app and shell."
required: true
type: string
default: "40e6c19ec2ca88804d6c2e3ddf52623af6685463"
susfs_patch_policy:
description: "strict = fail on reject; audited_cctv18 = allow known task_mmu reject only; native_cctv18 = CCTV18-style continue/audit."
required: true
type: choice
options: [audited_cctv18, strict, native_cctv18]
default: audited_cctv18
hook_mode:
description: "Hook source. inline_susfs uses the SUSFS-inlined hooks (the supported target for these refs)."
required: true
type: choice
options: [inline_susfs, sukisu_auto, kprobe_test]
default: inline_susfs
kernel_suffix:
description: "Stock-like LOCALVERSION without leading dash (this is the real uname -r). Leave default for current OP13 stock mimic / best app compatibility."
required: false
type: string
default: "android15-8-g93e223c276e7-abogki500782043-4k"
kernel_name:
description: "Display name shown in kernel-manager apps and the flash banner (does NOT change uname -r). Customise this freely, e.g. 'OX13-SukiSU'."
required: false
type: string
default: "PJZ110 | SukiSU Ultra [Inline StockLike] + CCTV18 SUSFS (OP-OX)"
optimise:
description: "O2 is stable daily-driver default. O3 is test-only."
required: true
type: choice
options: [O2, O3]
default: O2
lz4_zram_patch:
description: "Apply CCTV18 LZ4/ZSTD/ZRAM patch group. DEFAULT OFF: these patches modify kernel HEADERS (include/linux/lz4.h etc.), a GKI 2.0 KMI risk, and do not apply cleanly to this source. The kernel already builds ZRAM with lz4+zstd from the stock config, so leaving this Off changes nothing at runtime and removes 3 skip-warnings per build. Only set On if you have verified the patch group applies to your exact source."
required: true
type: choice
options: ["On", "Off"]
default: "Off"
zram_module:
description: "Enable kernel ZRAM + LZ4/LZ4HC/ZSTD support in config (recommended). Runtime ZRAM tuning (LZ4 / enable-disable) is handled by the X1 Kernel Manager module."
required: true
type: choice
options: ["On", "Off"]
default: "On"
bbg:
description: "Enable Baseband Guard if CCTV18 patch exists and applies cleanly."
required: true
type: choice
options: ["On", "Off"]
default: "On"
bbr:
description: "Compile in TCP BBR (selectable at runtime, not forced as system default). On is a safe daily-driver throughput option."
required: true
type: choice
options: ["Off", "On", "Default"]
default: "On"
adios:
description: "Enable ADIOS I/O scheduler if CCTV18 patch exists and applies cleanly."
required: true
type: choice
options: ["On", "Off"]
default: "On"
rekernel:
description: "Enable Re:Kernel (best-effort: applied only if the CCTV18 patch dry-runs cleanly, else skipped)."
required: true
type: choice
options: ["On", "Off"]
default: "On"
proxy:
description: "Enable better net/TTL/IP_SET style options."
required: true
type: choice
options: ["On", "Off"]
default: "On"
fengchi:
description: "Experimental FengChi/HMBIRD scheduler patch if present. Off by default for stability/battery."
required: true
type: choice
options: ["Off", "On"]
default: "Off"
zram_comp:
description: "Preferred ZRAM compressor where supported."
required: true
type: choice
options: [lz4, lzo-rle, zstd]
default: lz4
clean_ccache:
description: "Delete ccache before building. Use only when testing cache issues."
required: true
type: boolean
default: false
BUILD_TIME:
description: "Custom build time string. Enter F to use current UTC."
required: false
type: string
default: "F"
BUILD_USER:
description: "KBUILD_BUILD_USER"
required: false
type: string
default: "ox1d3x3"
BUILD_HOST:
description: "KBUILD_BUILD_HOST"
required: false
type: string
default: "op13-cctv18-sukisu"
concurrency:
group: op13-sukisu-cctv18-inline-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
actions: read
jobs:
build:
name: "${{ inputs.profile }} | SukiSU=${{ inputs.sukisu_ref }} | Hook=${{ inputs.hook_mode }} | SUSFS=${{ inputs.SUSFS }}"
runs-on: ubuntu-24.04
timeout-minutes: 360
permissions:
contents: read
actions: read
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_HARDLINK: "true"
steps:
- name: "Runner disk preflight"
id: disk
shell: bash
run: |
set -euo pipefail
echo "Memory and swap:"
free -h || true
echo ""
echo "Disk usage:"
df -hT || true
root_free_mb="$(df -Pm / | awk 'NR==2 {print $4}' || echo 0)"
echo "root_free_mb=$root_free_mb" >> "$GITHUB_OUTPUT"
if [ "$root_free_mb" -lt 25000 ]; then
echo "use_cleanup=true" >> "$GITHUB_OUTPUT"
else
echo "use_cleanup=false" >> "$GITHUB_OUTPUT"
fi
- name: "Extra disk cleanup"
if: steps.disk.outputs.use_cleanup == 'true'
shell: bash
run: |
set -euo pipefail
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/boost /usr/local/share/powershell /opt/hostedtoolcache/CodeQL || true
sudo docker image prune -a -f || true
sudo docker system prune -af || true
sudo apt-get clean || true
df -hT || true
- name: "Checkout"
uses: actions/checkout@v5
with:
fetch-depth: 1
- name: "Git auth for non-interactive clones"
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git config --global credential.helper store
git config --global core.askPass true
git config --global url."https://x-access-token:${GH_TOKEN}@github.com/".insteadOf "https://github.com/"
- name: "Setup ccache directory"
shell: bash
run: |
set -euo pipefail
mkdir -p "$CCACHE_DIR"
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
- name: "Restore ccache"
uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ runner.os }}-op13-sukisu-ultra-cctv18-${{ inputs.SUSFS }}-${{ inputs.sukisu_ref }}-${{ inputs.hook_mode }}-${{ github.ref_name }}
restore-keys: |
ccache-${{ runner.os }}-op13-sukisu-ultra-cctv18-${{ inputs.SUSFS }}-${{ inputs.sukisu_ref }}-
ccache-${{ runner.os }}-op13-sukisu-ultra-cctv18-
- name: "Install build dependencies"
shell: bash
run: |
set -euo pipefail
sudo apt-get -o Acquire::Retries=3 update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
aria2 ca-certificates curl wget git unzip zip tar xz-utils zstd rsync file jq \
build-essential gcc g++ make bc bison flex gawk python3 python-is-python3 \
libssl-dev libelf-dev libncurses-dev zlib1g-dev liblz4-tool dwarves \
ccache clang lld llvm patch kmod
sudo apt-get clean
echo "Runner: $(uname -a)"
ccache --version || true
- name: "Sync kernel sources"
shell: bash
run: |
set -euo pipefail
rm -rf kernel_workspace cctv18_assets _ox_debug artifacts ak3_work zram_tuner_work
mkdir -p kernel_workspace _ox_debug artifacts
echo "==> Cloning CCTV18 build assets only"
git clone --depth=1 "https://github.com/${CCTV18_BUILD_REPO}.git" cctv18_assets
git -C cctv18_assets rev-parse HEAD | tee _ox_debug/cctv18_assets_commit.txt
cd kernel_workspace
echo "==> Downloading CCTV18 OP13 common kernel source: ${{ env.CCTV18_KERNEL_REF }}"
aria2c -s16 -x16 -k1M --retry-wait=5 --max-tries=5 \
"https://github.com/${CCTV18_COMMON_REPO}/archive/refs/heads/${{ env.CCTV18_KERNEL_REF }}.zip" \
-o common.zip
unzip -q common.zip
src_dir="$(find . -maxdepth 1 -type d -name 'android_kernel_common_oneplus_sm8750-*' -print -quit)"
if [ -z "$src_dir" ]; then
echo "::error::Could not locate extracted OP13 common kernel directory."
find . -maxdepth 2 -type d | sort
exit 1
fi
mv "$src_dir" common
rm -f common.zip
echo "==> Downloading CCTV18 LLVM/Clang18 and build-tools"
aria2c -s16 -x16 -k1M --retry-wait=5 --max-tries=5 \
"https://github.com/cctv18/oneplus_sm8650_toolchain/releases/download/${CCTV18_TOOLCHAIN_RELEASE}/clang-r510928.zip" \
-o clang.zip
unzip -q clang.zip -d clang18
rm -f clang.zip
aria2c -s16 -x16 -k1M --retry-wait=5 --max-tries=5 \
"https://github.com/cctv18/oneplus_sm8650_toolchain/releases/download/${CCTV18_TOOLCHAIN_RELEASE}/build-tools.zip" \
-o build-tools.zip
unzip -q build-tools.zip
rm -f build-tools.zip
echo "==> Removing ABI export guard files and dirty suffix noise"
rm -f common/android/abi_gki_protected_exports_* || true
if [ -f common/scripts/setlocalversion ]; then
sed -i 's/ -dirty//g' common/scripts/setlocalversion
sed -i '$i res=$(echo "$res" | sed '\''s/-dirty//g'\'')' common/scripts/setlocalversion || true
fi
echo "==> Source layout" | tee ../_ox_debug/source-layout.txt
find . -maxdepth 2 -type d | sort | tee -a ../_ox_debug/source-layout.txt
- name: "Select toolchain"
shell: bash
run: |
set -euo pipefail
echo "$GITHUB_WORKSPACE/kernel_workspace/clang18/bin" >> "$GITHUB_PATH"
echo "$GITHUB_WORKSPACE/kernel_workspace/build-tools/path/linux-x86" >> "$GITHUB_PATH"
export PATH="$GITHUB_WORKSPACE/kernel_workspace/clang18/bin:$GITHUB_WORKSPACE/kernel_workspace/build-tools/path/linux-x86:$PATH"
echo "Toolchain check:"
command -v clang || true
clang --version || true
command -v ld.lld || true
ld.lld --version || true
- name: "Configure ccache limits"
shell: bash
run: |
set -euo pipefail
if [ "${{ inputs.clean_ccache }}" = "true" ]; then
rm -rf "$CCACHE_DIR"
fi
mkdir -p "$CCACHE_DIR"
ccache -M "$CCACHE_MAXSIZE"
ccache -o compression=true
ccache -z || true
ccache -s || true
- name: "Integrate SukiSU Ultra (builtin, official setup.sh)"
id: sukisu
shell: bash
run: |
set -euo pipefail
cd kernel_workspace
export PATH="$GITHUB_WORKSPACE/kernel_workspace/clang18/bin:$GITHUB_WORKSPACE/kernel_workspace/build-tools/path/linux-x86:$PATH"
echo "==> Installing SukiSU Ultra"
requested_ref="${{ inputs.sukisu_ref }}"
selected_ref="$requested_ref"
echo "Requested SukiSU Ultra ref: $requested_ref"
echo "Manual ref is required: true"
if [ "$requested_ref" = "auto-susfs" ]; then
echo "==> Resolving best available SukiSU SUSFS ref"
selected_ref=""
for cand in susfs-main susfs-dev susfs-test builtin main; do
if git ls-remote --exit-code --heads https://github.com/SukiSU-Ultra/SukiSU-Ultra.git "$cand" >/dev/null 2>&1 || git ls-remote --exit-code --tags https://github.com/SukiSU-Ultra/SukiSU-Ultra.git "$cand" >/dev/null 2>&1; then
selected_ref="$cand"
break
fi
done
if [ -z "$selected_ref" ]; then
echo "::error::Could not resolve any usable SukiSU ref from the clean candidate list."
exit 1
fi
fi
echo "Selected SukiSU Ultra ref: $selected_ref" | tee ../_ox_debug/sukisu-selected-ref.txt
echo "SUKISU_SELECTED_REF=$selected_ref" >> "$GITHUB_ENV"
echo "selected_ref=$selected_ref" >> "$GITHUB_OUTPUT"
# Use SukiSU Ultra's own setup script, then audit that it did not silently fall back.
# This keeps the root framework clean: no KernelSU-Next/ReSukiSU glue in the SukiSU-first workflow.
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s "$selected_ref"
if [ -d KernelSU ]; then
KSU_TREE="KernelSU"
elif [ -d common/drivers/kernelsu ]; then
KSU_TREE="common/drivers/kernelsu"
else
echo "::error::SukiSU Ultra tree was not found after setup."
find . -maxdepth 4 -type d | sort | sed -n '1,240p'
exit 1
fi
echo "KSU_TREE=$KSU_TREE" | tee -a "$GITHUB_ENV"
echo "sukisu_tree=$KSU_TREE" >> "$GITHUB_OUTPUT"
if [ -d KernelSU/.git ]; then
git -C KernelSU rev-parse HEAD | tee ../_ox_debug/sukisu_commit.txt || true
git -C KernelSU status --short --branch | tee ../_ox_debug/sukisu_status.txt || true
actual_branch="$(git -C KernelSU branch --show-current 2>/dev/null || true)"
requested="$selected_ref"
requested_sha="$(git -C KernelSU rev-parse --verify "$requested^{commit}" 2>/dev/null || true)"
actual_sha="$(git -C KernelSU rev-parse HEAD 2>/dev/null || true)"
{
echo "requested=$requested"
echo "actual_branch=$actual_branch"
echo "requested_sha=$requested_sha"
echo "actual_sha=$actual_sha"
} | tee ../_ox_debug/sukisu-ref-audit.txt
if [ "true" = "true" ] && [ "${{ inputs.sukisu_ref }}" != "auto-susfs" ]; then
if [ -n "$requested_sha" ] && [ "$requested_sha" != "$actual_sha" ]; then
echo "::error::SukiSU setup did not checkout the requested ref."
exit 1
fi
if [ -z "$requested_sha" ] && [ "$actual_branch" != "$requested" ]; then
echo "::error::Requested SukiSU ref '$requested' was not checked out. Use a valid SukiSU ref or disable require_sukisu_ref only for testing."
exit 1
fi
fi
fi
if [ ! -e common/drivers/kernelsu ]; then
echo "::error::common/drivers/kernelsu link/path missing after SukiSU setup."
exit 1
fi
if [ ! -f common/drivers/Makefile ] || ! grep -q 'CONFIG_KSU' common/drivers/Makefile; then
echo "::error::drivers/Makefile does not include SukiSU kernelsu object."
exit 1
fi
if [ ! -f common/drivers/Kconfig ] || ! grep -q 'drivers/kernelsu/Kconfig' common/drivers/Kconfig; then
echo "::error::drivers/Kconfig does not include SukiSU kernelsu Kconfig."
exit 1
fi
# Version information is best-effort only. Do not break clean builds if upstream changes Kbuild variables.
if [ -d KernelSU/.git ]; then
cnt="$(git -C KernelSU rev-list --count HEAD 2>/dev/null || true)"
tag="$(git -C KernelSU describe --tags --abbrev=0 2>/dev/null || true)"
[ -n "$cnt" ] && echo "KSUVER=$((cnt + 30000))" >> "$GITHUB_ENV" || true
[ -n "$tag" ] && echo "KSUTAG=$tag" >> "$GITHUB_ENV" || true
echo "sukisu_version_count=$cnt" >> "$GITHUB_OUTPUT"
echo "sukisu_tag=$tag" >> "$GITHUB_OUTPUT"
fi
# v109: apk_sign.patch is OFF by default. SukiSU Ultra ships its own
# manager-signature verification, so CCTV18's KernelSU apk_sign.patch is
# unnecessary here and never dry-runs cleanly on the SukiSU tree -- it only
# produced a recurring skip-warning. Set OX_TRY_APK_SIGN=1 in the workflow
# env to re-enable the attempt if you ever need it.
if [ "${OX_TRY_APK_SIGN:-0}" = "1" ] && [ -f "$GITHUB_WORKSPACE/cctv18_assets/other_patch/apk_sign.patch" ] && [ -d common/drivers/kernelsu ]; then
echo "==> Checking CCTV18 apk_sign.patch for SukiSU manager compatibility"
if ( cd common/drivers/kernelsu && patch --dry-run -p2 -N -F 3 < "$GITHUB_WORKSPACE/cctv18_assets/other_patch/apk_sign.patch" > "$GITHUB_WORKSPACE/_ox_debug/sukisu-apk-sign-dryrun.log" 2>&1 ); then
( cd common/drivers/kernelsu && patch -p2 -N -F 3 < "$GITHUB_WORKSPACE/cctv18_assets/other_patch/apk_sign.patch" ) || true
echo "==> apk_sign.patch applied."
else
echo "==> apk_sign.patch did not dry-run cleanly on this SukiSU tree; skipped (expected)."
cat "$GITHUB_WORKSPACE/_ox_debug/sukisu-apk-sign-dryrun.log" || true
fi
else
echo "==> Skipping apk_sign.patch (SukiSU has native manager signing; set OX_TRY_APK_SIGN=1 to attempt)."
fi
- name: "SukiSU Ultra compile compatibility repairs"
shell: bash
run: |
set -euo pipefail
cd kernel_workspace
mkdir -p "$GITHUB_WORKSPACE/_ox_debug"
echo "==> Checking SukiSU Ultra sulog USER_ARG_NULL pointer compatibility"
python3 - <<'PY'
from pathlib import Path
import re
candidates = [
Path('common/drivers/kernelsu/sulog/event.c'),
Path('common/KernelSU/kernel/sulog/event.c'),
Path('KernelSU/kernel/sulog/event.c'),
Path('KernelSU/sulog/event.c'),
Path('drivers/kernelsu/sulog/event.c'),
]
patched = []
for path in candidates:
if not path.exists():
continue
src = path.read_text(encoding='utf-8', errors='ignore')
original = src
# In current SukiSU Ultra, USER_ARG_NULL expands to user_arg_null_ptr(), which already
# returns a 'struct user_arg_ptr *'. With CONFIG_KSU_SUSFS the active ksu_sulog_capture()
# takes 'struct user_arg_ptr *argv_user', so passing the bare USER_ARG_NULL is correct.
# A stray '&USER_ARG_NULL' is "address of an rvalue" and fails to compile, so normalize
# to the bare pointer form (strip any leading '&'). No-op when the source is already correct.
src = re.sub(
r'ksu_sulog_capture\(\s*KSU_SULOG_EVENT_IOCTL_GRANT_ROOT\s*,\s*NULL\s*,\s*&\s*USER_ARG_NULL\s*,\s*gfp\s*\)',
'ksu_sulog_capture(KSU_SULOG_EVENT_IOCTL_GRANT_ROOT, NULL, USER_ARG_NULL, gfp)',
src,
)
if src != original:
path.write_text(src, encoding='utf-8')
patched.append(str(path))
out = Path('../_ox_debug/sukisu-compile-compat.txt')
if patched:
out.write_text('patched=' + ','.join(patched) + '\n', encoding='utf-8')
print('Patched SukiSU sulog USER_ARG_NULL call in:')
for p in patched:
print(' -', p)
else:
out.write_text('patched=none\n', encoding='utf-8')
print('No SukiSU sulog USER_ARG_NULL compile fix needed.')
PY
if [ -f common/drivers/kernelsu/sulog/event.c ]; then
grep -n "KSU_SULOG_EVENT_IOCTL_GRANT_ROOT" common/drivers/kernelsu/sulog/event.c | tee -a "$GITHUB_WORKSPACE/_ox_debug/sukisu-compile-compat.txt" || true
fi
- name: "Apply SUSFS"
if: ${{ inputs.SUSFS == 'On' }}
shell: bash
run: |
set -euo pipefail
cd kernel_workspace
rm -rf susfs4ksu
echo "==> Cloning CCTV18 SUSFS only: cctv18/susfs4oki @ ${{ inputs.susfs_ref }}"
# v109: susfs_ref may be a BRANCH/TAG or a COMMIT SHA.
# `git clone --branch` only accepts branch/tag names, so a pinned SHA has
# to be fetched explicitly (verified working: GitHub allows shallow
# fetch-by-SHA on this repo).
SUSFS_REF_IN="${{ inputs.susfs_ref }}"
if printf '%s' "$SUSFS_REF_IN" | grep -qE '^[0-9a-fA-F]{7,40}$'; then
echo "==> Fetching CCTV18 SUSFS at pinned commit $SUSFS_REF_IN"
rm -rf susfs4ksu && mkdir -p susfs4ksu
git -C susfs4ksu init -q
git -C susfs4ksu remote add origin https://github.com/cctv18/susfs4oki.git
if git -C susfs4ksu fetch -q --depth=1 origin "$SUSFS_REF_IN"; then
git -C susfs4ksu checkout -q FETCH_HEAD
else
echo "==> Shallow fetch-by-SHA unavailable; falling back to full clone."
rm -rf susfs4ksu
git clone -q https://github.com/cctv18/susfs4oki.git susfs4ksu
git -C susfs4ksu checkout -q "$SUSFS_REF_IN"
fi
got="$(git -C susfs4ksu rev-parse HEAD)"
case "$got" in
"$SUSFS_REF_IN"*) echo "==> SUSFS checked out at $got" ;;
*) echo "::error::Requested SUSFS ref $SUSFS_REF_IN but got $got."; exit 1 ;;
esac
else
git clone --depth=1 --branch "$SUSFS_REF_IN" https://github.com/cctv18/susfs4oki.git susfs4ksu
fi
git -C susfs4ksu rev-parse HEAD | tee ../_ox_debug/cctv18_susfs_commit.txt
git -C susfs4ksu remote -v | tee ../_ox_debug/cctv18_susfs_remote.txt
SUSFS_PATCH="susfs4ksu/kernel_patches/50_add_susfs_in_gki-${ANDROID_VERSION}-${KERNEL_VERSION}.patch"
if [ ! -f "$SUSFS_PATCH" ]; then
echo "::error::CCTV18 SUSFS common patch not found: $SUSFS_PATCH"
find susfs4ksu/kernel_patches -maxdepth 3 -type f | sort
exit 1
fi
cp -af susfs4ksu/kernel_patches/fs/. common/fs/
cp -af susfs4ksu/kernel_patches/include/linux/. common/include/linux/
cp -f "$SUSFS_PATCH" common/
echo "==> Skipping 69_hide_stuff.patch for SukiSU Ultra stable lane"
echo "Reason: v77 reached final link, then failed on SELinux policy-query symbols. Keep this lane clean and compile-safe first."
cd common
# ---- v109: upstream security fix CVE-2026-43499 (rtmutex) ----
# Fixes a NULL-pointer dereference in remove_waiter(): when called via
# rt_mutex_start_proxy_lock(), waiter->task may be NULL (never enqueued),
# and the old code dereferenced it unconditionally.
# Adopted from cctv18/oppo_oplus_realme_sm8750 (other_patch/), which is the
# same asset repo this workflow already clones. CCTV18's own 6.6.118 builder
# applies it WITHOUT "|| true", i.e. they treat it as mandatory.
# SAFETY: this patch touches ONLY kernel/locking/rtmutex.c and rtmutex_api.c
# -- no headers, so no struct/prototype change and therefore no GKI 2.0 KMI
# impact. Verified absent from the 6.6.118 source, so it is genuinely needed.
# Applied all-or-nothing: if it would not apply fully we skip and warn loudly
# rather than half-patch core locking code.
CVE_PATCH="$GITHUB_WORKSPACE/cctv18_assets/other_patch/cve-2026-43499-rtmutex-6.6.patch"
if [ -f "$CVE_PATCH" ]; then
if patch --dry-run -p1 -N -F 3 < "$CVE_PATCH" \
> "$GITHUB_WORKSPACE/_ox_debug/dryrun-cve-2026-43499.log" 2>&1; then
patch -p1 -N -F 3 < "$CVE_PATCH" \
> "$GITHUB_WORKSPACE/_ox_debug/apply-cve-2026-43499.log" 2>&1
if grep -q "waiter_task" kernel/locking/rtmutex.c; then
echo "==> CVE-2026-43499 rtmutex security patch APPLIED and verified."
echo "CVE-2026-43499: applied" > "$GITHUB_WORKSPACE/_ox_debug/cve-status.txt"
else
echo "::warning::CVE-2026-43499 patch reported success but the fix marker is missing. Treating as NOT applied."
echo "CVE-2026-43499: NOT applied (verification failed)" > "$GITHUB_WORKSPACE/_ox_debug/cve-status.txt"
fi
else
echo "::warning::CVE-2026-43499 rtmutex patch would not apply cleanly (kernel may already contain the fix, or context changed). Skipped -- kernel is NOT patched for this CVE."
echo "CVE-2026-43499: SKIPPED (dry-run failed)" > "$GITHUB_WORKSPACE/_ox_debug/cve-status.txt"
cat "$GITHUB_WORKSPACE/_ox_debug/dryrun-cve-2026-43499.log" || true
fi
else
echo "::warning::CVE-2026-43499 patch not found in cctv18_assets/other_patch; skipping."
echo "CVE-2026-43499: patch file not found" > "$GITHUB_WORKSPACE/_ox_debug/cve-status.txt"
fi
# ---- v109: driver/SUSFS pairing check ----
# The su hook is guarded in fs/exec.c. CCTV18 SUSFS 525c450 renamed that
# guard from susfs_is_current_proc_umounted() to
# susfs_is_current_proc_no_su(); SukiSU e060b7c switched the driver to set
# the matching no_su mark. Mixing generations makes the new guard read the
# old umounted mark as "no su", so `su` is skipped for every app and shell
# while apps using binder/supercall still get root.
SUSFS_NEW=0; DRV_NEW=0
grep -qs "susfs_is_current_proc_no_su" susfs4ksu/kernel_patches/include/linux/susfs_def.h && SUSFS_NEW=1
grep -rqs "susfs_set_current_proc_no_su" KernelSU/kernel/ 2>/dev/null && DRV_NEW=1
echo "==> pairing: SUSFS_new=$SUSFS_NEW driver_new=$DRV_NEW"
if [ "$SUSFS_NEW" != "$DRV_NEW" ]; then
echo "::error::MISMATCHED PAIR: SUSFS_new=$SUSFS_NEW but driver_new=$DRV_NEW. This breaks \`su\` from shells. Use a sukisu_ref at/after e060b7c with a susfs_ref at/after 525c450, or pin BOTH to the older generation."
echo "pairing: MISMATCH (susfs=$SUSFS_NEW driver=$DRV_NEW)" > "$GITHUB_WORKSPACE/_ox_debug/pairing.txt"
exit 1
fi
echo "pairing: OK (susfs=$SUSFS_NEW driver=$DRV_NEW)" > "$GITHUB_WORKSPACE/_ox_debug/pairing.txt"
echo "==> Applying CCTV18 SUSFS patch with policy: ${{ inputs.susfs_patch_policy }}"
set +e
patch -p1 -N -F 3 < "50_add_susfs_in_gki-${ANDROID_VERSION}-${KERNEL_VERSION}.patch" 2>&1 | tee "$GITHUB_WORKSPACE/_ox_debug/cctv18-susfs-patch.log"
patch_rc=${PIPESTATUS[0]}
set -e
if [ "$patch_rc" -ne 0 ]; then
echo "SUSFS patch returned rc=$patch_rc" | tee -a "$GITHUB_WORKSPACE/_ox_debug/cctv18-susfs-patch.log"
find . -name '*.rej' -print | sort | tee "$GITHUB_WORKSPACE/_ox_debug/susfs-rejects.txt"
reject_count="$(wc -l < "$GITHUB_WORKSPACE/_ox_debug/susfs-rejects.txt" | tr -d ' ')"
only_task_mmu="false"
if [ "$reject_count" = "1" ] && grep -qx './fs/proc/task_mmu.c.rej' "$GITHUB_WORKSPACE/_ox_debug/susfs-rejects.txt"; then
only_task_mmu="true"
fi
if [ "${{ inputs.susfs_patch_policy }}" = "strict" ]; then
echo "::error::SUSFS patch reject found and strict policy is enabled."
exit 1
elif [ "${{ inputs.susfs_patch_policy }}" = "audited_cctv18" ] && [ "$only_task_mmu" != "true" ]; then
echo "::error::Unexpected SUSFS reject. audited_cctv18 only allows ./fs/proc/task_mmu.c.rej."
exit 1
else
echo "==> Note: continuing with audited CCTV18 SUSFS reject handling (the known, expected task_mmu.c reject). No non-CCTV18 SUSFS source is imported."
while read -r rej; do
[ -n "$rej" ] || continue
safe="$(echo "$rej" | sed 's#^./##; s#[/ ]#_#g')"
cp -f "$rej" "$GITHUB_WORKSPACE/_ox_debug/${safe}" || true
rm -f "$rej"
done < "$GITHUB_WORKSPACE/_ox_debug/susfs-rejects.txt"
fi
fi
echo "==> 69_hide_stuff.patch is intentionally not applied in v109 SukiSU stable lane."
echo " SUSFS inline remains enabled; extra SELinux/map-hide policy-query patches are deferred to ReSuki/test builds."
# ---- v109: verify the SUSFS su-hook guard is the FIXED variant ----
# History: CCTV18 susfs4oki 83dcd81 (2026-08-15) guarded the su hook in
# fs/exec.c with susfs_is_current_proc_umounted(). That flag is set for
# every SUSFS-"umounted" process -- i.e. every zygote-spawned app AND every
# shell -- so execve of /system/bin/su was skipped and failed with ENOENT
# ("cannot execute: required file not found") in Termux / adb shell, while
# apps taking root via binder/supercall were unaffected.
# Upstream fixed this in 525c450 (2026-08-19) by switching to
# susfs_is_current_proc_no_su(), which only skips processes explicitly
# marked as not-allowed-root.
# susfs_ref is pinned to a commit containing that fix; this check makes a
# regression impossible to miss if the pin is ever changed.
if grep -q "susfs_is_current_proc_no_su" fs/exec.c 2>/dev/null; then
echo "==> su-hook guard OK: fs/exec.c uses susfs_is_current_proc_no_su() (fixed variant)."
echo "su-hook guard: no_su (FIXED)" > "$GITHUB_WORKSPACE/_ox_debug/su-hook-guard.txt"
elif grep -q "susfs_is_current_proc_umounted" fs/exec.c 2>/dev/null; then
# v109: OLD generation is VALID when the driver is also old generation.
# The pairing check above is authoritative; this is informational only.
echo "==> su-hook guard: susfs_is_current_proc_umounted() (OLD generation)."
echo " Valid only with an OLD-generation driver (e.g. sukisu 6c13a06)."
echo " The pairing check above verifies that; it is what can fail the build."
echo "su-hook guard: umounted (old generation)" > "$GITHUB_WORKSPACE/_ox_debug/su-hook-guard.txt"
else
echo "::warning::Could not find either su-hook guard in fs/exec.c; SUSFS layout may have changed."
echo "su-hook guard: not found" > "$GITHUB_WORKSPACE/_ox_debug/su-hook-guard.txt"
fi
if find . -name '*.rej' -print -quit | grep -q .; then
echo "::error::Unexpected patch rejects remain after CCTV18 SUSFS stage."
find . -name '*.rej' -print | tee "$GITHUB_WORKSPACE/_ox_debug/rejects-after-susfs-stage.txt"
exit 1
fi
- name: "SukiSU SELinux policy-query compatibility guard"
if: ${{ inputs.SUSFS == 'On' }}
shell: bash
run: |
set -euo pipefail
cd kernel_workspace/common
mkdir -p "$GITHUB_WORKSPACE/_ox_debug"
{
echo "==> SukiSU + CCTV18 SELinux policy-query compatibility guard"
echo "v77 reached final link with SukiSU SUSFS_INLINE_HOOK + KPM + SUSFS v2.2.0."
echo "The failure was unresolved SELinux policy-query symbols, not root/SUSFS integration."
echo "For the stable daily-driver SukiSU lane, disable only the source-level SELinux policy-query replacement blocks in hooks.c and selinuxfs.c."
echo "This does not import another SUSFS tree and does not touch the core SUSFS fs/include payload."
} | tee "$GITHUB_WORKSPACE/_ox_debug/selinux-policy-query-guard.log"
python3 - <<'PY'
from pathlib import Path
paths = [Path('security/selinux/hooks.c'), Path('security/selinux/selinuxfs.c')]
changed = []
for path in paths:
if not path.exists():
continue
src = path.read_text(encoding='utf-8', errors='ignore')
original = src
# CCTV18's 50_add_susfs patch adds source-level SELinux policy-query wrappers
# under CONFIG_KSU_SUSFS. Current SukiSU builtin does not provide the backup_sepolicy
# and *_with_policy symbols needed by those wrappers, causing final link failure.
# Gate those wrappers behind a symbol we intentionally do not enable for this
# stable SukiSU lane. Core SUSFS and SukiSU inline hook remain enabled.
src = src.replace(
'#ifdef CONFIG_KSU_SUSFS\n',
'#if defined(CONFIG_KSU_SUSFS) && defined(CONFIG_KSU_SUSFS_SELINUX_POLICY_HIDE)\n'
)
if src != original:
path.write_text(src, encoding='utf-8')
changed.append(str(path))
out = Path('../../_ox_debug/selinux-policy-query-guard-files.txt')
out.write_text('\n'.join(changed) + ('\n' if changed else 'none\n'), encoding='utf-8')
print('Guarded files:', ', '.join(changed) if changed else 'none')
PY
echo "==> SELinux symbol scan after guard" | tee -a "$GITHUB_WORKSPACE/_ox_debug/selinux-policy-query-guard.log"
for sym in backup_sepolicy security_context_to_sid_with_policy security_sid_to_context_with_policy security_compute_av_user_with_policy; do
echo "--- $sym ---" | tee -a "$GITHUB_WORKSPACE/_ox_debug/selinux-policy-query-guard.log"
grep -Rsn "$sym" security/selinux | tee -a "$GITHUB_WORKSPACE/_ox_debug/selinux-policy-query-guard.log" || true
done
- name: "Apply optional CCTV18 feature patches"
shell: bash
run: |
set -euo pipefail
cd kernel_workspace/common
find_patch_by_regex() {
local regex="$1"
find "$GITHUB_WORKSPACE/cctv18_assets" -type f | grep -Ei "$regex" | sort | head -n 1 || true
}
clean_apply_optional_patch() {
local label="$1"
local enabled="$2"
local regex="$3"
if [ "$enabled" != "true" ]; then
echo "Skipping $label: disabled" | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
return 0
fi
local patch_file
patch_file="$(find_patch_by_regex "$regex" || true)"
if [ -z "$patch_file" ]; then
echo "Optional patch not found for $label; skipping." | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
return 0
fi
echo "Checking optional patch for $label: $patch_file" | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
if patch --dry-run -p1 -N -F 3 < "$patch_file" > "$GITHUB_WORKSPACE/_ox_debug/dryrun-${label//[^A-Za-z0-9]/_}.log" 2>&1; then
echo "Applying optional patch for $label" | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
patch -p1 -N -F 3 < "$patch_file" >> "$GITHUB_WORKSPACE/_ox_debug/apply-${label//[^A-Za-z0-9]/_}.log" 2>&1
else
echo "::warning::Optional patch for $label did not apply cleanly; skipped to avoid partial patch contamination."
cat "$GITHUB_WORKSPACE/_ox_debug/dryrun-${label//[^A-Za-z0-9]/_}.log" || true
fi
}
clean_apply_optional_patch "adios" "${{ inputs.adios == 'On' }}" "(^|/).*adios.*\.patch$"
clean_apply_optional_patch "baseband_guard" "${{ inputs.bbg == 'On' }}" "(^|/).*(baseband|bbg).*\.patch$"
clean_apply_optional_patch "unicode" "true" "(^|/).*unicode.*\.patch$"
clean_apply_optional_patch "rekernel" "${{ inputs.rekernel == 'On' }}" "(^|/).*(rekernel|re-kernel).*\.patch$"
clean_apply_optional_patch "fengchi" "${{ inputs.fengchi == 'On' }}" "(^|/).*(fengchi|hmbird|sched).*\.patch$"
clean_apply_patch_group() {
local label="$1"
local enabled="$2"
local regex="$3"
if [ "$enabled" != "true" ]; then
echo "Skipping $label patch group: disabled" | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
return 0
fi
mapfile -t patches < <(find "$GITHUB_WORKSPACE/cctv18_assets" -type f | grep -Ei "$regex" | sort || true)
if [ "$label" = "lz4_zstd" ] && [ "${#patches[@]}" -gt 0 ]; then
mapfile -t patches < <(printf "%s\n" "${patches[@]}" | grep -Evi '(lz4kd|lz4k)' || true)
fi
if [ "${#patches[@]}" -eq 0 ]; then
echo "Optional patch group not found for $label; skipping." | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
return 0
fi
for patch_file in "${patches[@]}"; do
echo "Checking optional patch group $label: $patch_file" | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
safe="${label}_$(basename "$patch_file" | sed 's/[^A-Za-z0-9]/_/g')"
if patch --dry-run -p1 -N -F 3 < "$patch_file" > "$GITHUB_WORKSPACE/_ox_debug/dryrun-${safe}.log" 2>&1; then
echo "Applying optional patch group $label: $patch_file" | tee -a "$GITHUB_WORKSPACE/_ox_debug/optional-patches.log"
patch -p1 -N -F 3 < "$patch_file" >> "$GITHUB_WORKSPACE/_ox_debug/apply-${safe}.log" 2>&1
else
echo "::warning::Optional patch $patch_file for $label did not apply cleanly; skipped to avoid partial patch contamination."
cat "$GITHUB_WORKSPACE/_ox_debug/dryrun-${safe}.log" || true
fi
done
}
clean_apply_patch_group "lz4_zstd" "${{ inputs.lz4_zram_patch == 'On' }}" "(^|/).*(zram_patch|lz4|zstd).*[.]patch$"
clean_apply_patch_group "lz4kd" "false" "(^|/).*(lz4kd|lz4k).*[.]patch$"
if find . -name '*.rej' -print -quit | grep -q .; then
echo "::error::Patch rejects found after optional patch stage."
find . -name '*.rej' -print | tee "$GITHUB_WORKSPACE/_ox_debug/rejects-after-optional-stage.txt"
exit 1
fi
- name: "Enable features via defconfig"
shell: bash
run: |
set -euo pipefail
cd kernel_workspace/common
export PATH="$GITHUB_WORKSPACE/kernel_workspace/clang18/bin:$GITHUB_WORKSPACE/kernel_workspace/build-tools/path/linux-x86:$PATH"
export ARCH=arm64
export SUBARCH=arm64
export LLVM=1
export LLVM_IAS=1
MAKE_ARGS=(O=out ARCH=arm64 LLVM=1 LLVM_IAS=1)
echo "==> Toolchain check"
which clang | tee "$GITHUB_WORKSPACE/_ox_debug/clang-path.txt"
clang --version | tee "$GITHUB_WORKSPACE/_ox_debug/clang-version.txt"
echo "==> Generating gki_defconfig"
make "${MAKE_ARGS[@]}" gki_defconfig
cfg="out/.config"
if [ ! -f "$cfg" ]; then
echo "::error::Missing generated .config"
exit 1
fi
cfg_sym() { local s="$1"; echo "${s#CONFIG_}"; }
set_y() { scripts/config --file "$cfg" -e "$(cfg_sym "$1")" || true; }
set_m() { scripts/config --file "$cfg" -m "$(cfg_sym "$1")" || true; }
set_n() { scripts/config --file "$cfg" -d "$(cfg_sym "$1")" || true; }
set_str() { scripts/config --file "$cfg" --set-str "$(cfg_sym "$1")" "$2" || true; }
echo "==> Applying v109 SukiSU Ultra daily-driver config profile: ${{ inputs.profile }}"
set_y CONFIG_KSU
set_y CONFIG_KSU_MANUAL_SU
if [ "${{ inputs.kpm_enable }}" = "true" ]; then
echo "==> Enabling SukiSU KPM support"
set_y CONFIG_KPM
set_y CONFIG_KALLSYMS
set_y CONFIG_KALLSYMS_ALL
fi
if [ "${{ inputs.multi_manager_support }}" = "true" ]; then
set_y CONFIG_KSU_MULTI_MANAGER_SUPPORT
fi
echo "==> Requested SukiSU hook target: ${{ inputs.hook_mode }}"
case "${{ inputs.hook_mode }}" in
inline_susfs)
# SUSFS-inlined hooks ONLY (CONFIG_KSU_NONE_HOOK).
#
# KNOWN LIMITATION -- `su` from a shell does NOT work in this mode.
# CCTV18's SUSFS patch inlines this into fs/exec.c:
# if (likely(susfs_is_current_proc_umounted()))
# goto orig_flow; <-- skips sucompat
# if (static_branch_likely(&ksu_su_compat_enabled))
# ksu_handle_execveat_sucompat(...);
# Any process SUSFS marked "umounted" (every zygote-spawned app, and
# shell contexts) jumps past the su hook, so execve of /system/bin/su
# is never redirected and fails ENOENT ("cannot execute: required file
# not found"). The stat/faccessat hooks are inlined WITHOUT that guard,
# which is why `su` still LOOKS present. Apps taking root via the
# binder/supercall path are unaffected -- only `su` via execve breaks.
# NONE_HOOK also disables SukiSU's own hooks, so there is no fallback.
set_y CONFIG_KSU_NONE_HOOK
set_n CONFIG_KSU_MANUAL_HOOK
set_n CONFIG_KSU_KPROBES_HOOK
set_n CONFIG_KSU_WITH_KPROBES
set_n CONFIG_KSU_TRACEPOINT_HOOK
;;
sukisu_auto)
# Let the selected SukiSU ref choose its hook defaults. Useful only for testing.
true
;;
kprobe_test)
set_y CONFIG_KPROBES
set_y CONFIG_KSU_KPROBES_HOOK
set_n CONFIG_KSU_NONE_HOOK
set_n CONFIG_KSU_MANUAL_HOOK
;;
esac
if [ "${{ inputs.SUSFS }}" = "On" ]; then
# v109: only the 10 SUSFS options that ACTUALLY EXIST in the root driver.
# Verified against the Kconfig of all three projects (SukiSU-Ultra,
# ReSukiSU, pershoot/KernelSU-Next) -- each defines exactly these 10.
# REMOVED because no project defines them, so olddefconfig silently
# dropped them and they only created false confidence:
# CONFIG_KSU_SUSFS_TRY_UMOUNT
# CONFIG_KSU_SUSFS_SUS_SU (deprecated upstream)
# CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
# CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
# Per-app unmounting is handled by the manager's App Profile
# ("umount modules"), not by a kernel config symbol.
for opt in \
CONFIG_KSU_SUSFS \
CONFIG_KSU_SUSFS_SUS_PATH \
CONFIG_KSU_SUSFS_SUS_MOUNT \
CONFIG_KSU_SUSFS_SUS_KSTAT \
CONFIG_KSU_SUSFS_SUS_MAP \
CONFIG_KSU_SUSFS_SPOOF_UNAME \
CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG \
CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS \
CONFIG_KSU_SUSFS_OPEN_REDIRECT; do
set_y "$opt"
done
# Battery/performance: SUSFS kernel logging is pure overhead on a daily
# driver (it logs on SUSFS operations). Upstream defaults it to y; we
# force it off.
set_n CONFIG_KSU_SUSFS_ENABLE_LOG
fi
# Mountify / Magic Mount friendly tmpfs support.
set_y CONFIG_TMPFS_XATTR
set_y CONFIG_TMPFS_POSIX_ACL
# Stock-mimic default: keep uname -r exactly controlled by kernel_suffix.
set_str CONFIG_LOCALVERSION "-${{ inputs.kernel_suffix }}"
set_n CONFIG_LOCALVERSION_AUTO
set_y CONFIG_LTO_CLANG_THIN
set_n CONFIG_LTO_NONE
case "${{ inputs.optimise }}" in
O2)
set_y CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
set_n CONFIG_CC_OPTIMIZE_FOR_SIZE
set_n CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
;;
O3)
set_y CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
;;
esac
if [ "${{ inputs.zram_module }}" = "On" ]; then
echo "==> Enabling ZRAM + LZ4/LZ4HC/ZSTD config support"
for opt in \
CONFIG_ZRAM \
CONFIG_ZSMALLOC \
CONFIG_CRYPTO_LZ4 \
CONFIG_CRYPTO_LZ4HC \
CONFIG_CRYPTO_ZSTD \
CONFIG_LZ4_COMPRESS \
CONFIG_LZ4_DECOMPRESS \
CONFIG_ZSTD_COMPRESS \
CONFIG_ZSTD_DECOMPRESS; do
set_y "$opt"
done
set_y CONFIG_ZRAM_WRITEBACK
set_y CONFIG_ZRAM_MULTI_COMP
set_str CONFIG_ZRAM_DEF_COMP "${{ inputs.zram_comp }}"
fi
if [ "${{ inputs.proxy == 'On' }}" = "true" ]; then
for opt in \
CONFIG_NETFILTER \
CONFIG_NETFILTER_ADVANCED \
CONFIG_NETFILTER_XTABLES \
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE \
CONFIG_NETFILTER_XT_MATCH_COMMENT \
CONFIG_NETFILTER_XT_MATCH_CONNTRACK \
CONFIG_NETFILTER_XT_MATCH_MARK \
CONFIG_NETFILTER_XT_TARGET_MARK \
CONFIG_NETFILTER_XT_TARGET_HL \
CONFIG_NETFILTER_XT_MATCH_HL \
CONFIG_IP_SET \
CONFIG_IP_SET_BITMAP_IP \
CONFIG_IP_SET_BITMAP_IPMAC \
CONFIG_IP_SET_BITMAP_PORT \
CONFIG_IP_SET_HASH_IP \
CONFIG_IP_SET_HASH_IPMARK \
CONFIG_IP_SET_HASH_IPPORT \
CONFIG_IP_SET_HASH_IPPORTIP \
CONFIG_IP_SET_HASH_IPPORTNET \
CONFIG_IP_SET_HASH_IPMAC \
CONFIG_IP_SET_HASH_MAC \
CONFIG_IP_SET_HASH_NET \
CONFIG_IP_SET_HASH_NETNET \
CONFIG_IP_SET_HASH_NETPORT \
CONFIG_IP_SET_HASH_NETPORTNET \
CONFIG_IP_SET_HASH_NETIFACE \
CONFIG_IP_SET_LIST_SET \
CONFIG_NETFILTER_XT_SET \
CONFIG_IP6_NF_IPTABLES \
CONFIG_IP6_NF_NAT \
CONFIG_IP6_NF_TARGET_MASQUERADE; do
set_y "$opt"
done
# v109: symbol-name corrections verified against the real 6.6.118
# net/netfilter Kconfig. The following DID NOT EXIST and were silently
# dropped by olddefconfig, so these features were never actually built:
# CONFIG_NETFILTER_XT_TARGET_HL -> real symbol is ..._TARGET_HL
# CONFIG_NETFILTER_XT_MATCH_TTL -> real symbol is ..._MATCH_HL
# (upstream merged them: "adds the HL (for IPv6) and TTL (for IPv4)")
# CONFIG_NETFILTER_XT_MATCH_SET -> real symbol is CONFIG_NETFILTER_XT_SET
# (without it CONFIG_IP_SET is unusable from iptables)
# The extra IP_SET_* variants match CCTV18's own builder and make ipset
# actually usable for firewall / ad-block modules.
scripts/config --file "$cfg" --set-val CONFIG_IP_SET_MAX 65534 || true
fi
set_n CONFIG_DEFAULT_BBR || true
case "${{ inputs.bbr }}" in
off|Off)
set_n CONFIG_TCP_CONG_BBR
;;
enabled|On)
set_y CONFIG_TCP_CONG_BBR
;;
default|Default)
set_y CONFIG_TCP_CONG_BBR
set_y CONFIG_DEFAULT_BBR || true
;;
esac
if [ "true" = "true" ]; then
set_y CONFIG_NTSYNC
fi
if [ "false" = "true" ]; then
for opt in CONFIG_TMPFS_XATTR CONFIG_TMPFS_POSIX_ACL CONFIG_PID_NS CONFIG_USER_NS CONFIG_UTS_NS CONFIG_IPC_NS; do
set_y "$opt"
done
fi
case "${{ inputs.profile }}" in
minimal_safe)
set_n CONFIG_TCP_CONG_BBR
set_n CONFIG_IP_SET
set_n CONFIG_NTSYNC
set_n CONFIG_KPM
;;
debug_only)
set_y CONFIG_KSU_SUSFS_ENABLE_LOG
;;
stock_plus)
set_y CONFIG_NTSYNC
;;
esac
echo "==> Running olddefconfig directly; no yes pipe, no SIGPIPE false failure"
make "${MAKE_ARGS[@]}" olddefconfig
cp -f out/.config "$GITHUB_WORKSPACE/_ox_debug/final.config"
echo "==> Final SukiSU/SUSFS inline hook audit" | tee "$GITHUB_WORKSPACE/_ox_debug/hook-mode-audit.txt"
{
echo "Requested hook mode: ${{ inputs.hook_mode }}"
echo "Detected SukiSU symbols:"
grep -E 'CONFIG_KSU(_NONE_HOOK|_MANUAL_HOOK|_KPROBES_HOOK|_WITH_KPROBES|_TRACEPOINT_HOOK|_SUSFS|_MANUAL_SU|_MULTI_MANAGER_SUPPORT)?=' out/.config || true
grep -E 'CONFIG_KPM=|CONFIG_KALLSYMS=|CONFIG_KALLSYMS_ALL=' out/.config || true
echo "Detected generic KPROBES state:"
grep -E 'CONFIG_KPROBES=' out/.config || true
echo "SukiSU hook selector availability:"
if grep -Rqs 'config KSU_NONE_HOOK' ../KernelSU/kernel ./drivers/kernelsu 2>/dev/null; then
echo "KSU_NONE_HOOK symbol exists in this SukiSU tree"
else
echo "KSU_NONE_HOOK symbol NOT exposed by this SukiSU ref"
fi
} | tee -a "$GITHUB_WORKSPACE/_ox_debug/hook-mode-audit.txt"
if ! grep -q '^CONFIG_KSU=y' out/.config; then
echo "::error::CONFIG_KSU=y missing after olddefconfig."
exit 1
fi
if [ "${{ inputs.SUSFS }}" = "On" ]; then
if ! grep -q '^CONFIG_KSU_SUSFS=y' out/.config; then
echo "::error::CONFIG_KSU_SUSFS=y missing after olddefconfig. The selected SukiSU ref may not contain SUSFS integration. Try a valid SukiSU susfs ref."
exit 1
fi
fi
if [ "${{ inputs.hook_mode }}" = "inline_susfs" ]; then
if grep -Rqs 'config KSU_NONE_HOOK' ../KernelSU/kernel ./drivers/kernelsu 2>/dev/null; then
if ! grep -q '^CONFIG_KSU_NONE_HOOK=y' out/.config; then
echo "::error::inline_susfs requested, but CONFIG_KSU_NONE_HOOK=y is not present after olddefconfig."
exit 1
fi
else
echo "==> Note: this SukiSU ref does not expose CONFIG_KSU_NONE_HOOK; inline SUSFS hook is provided via CONFIG_KSU_SUSFS (expected on this pin)."
fi
if grep -q '^CONFIG_KSU_MANUAL_HOOK=y' out/.config || grep -q '^CONFIG_KSU_KPROBES_HOOK=y' out/.config || grep -q '^CONFIG_KSU_WITH_KPROBES=y' out/.config; then
echo "::error::inline_susfs requested, but a manual/kprobe KSU hook selector is still enabled."
exit 1
fi
fi
if [ "${{ inputs.kpm_enable }}" = "true" ] && ! grep -q '^CONFIG_KPM=y' out/.config; then
echo "::warning::KPM requested but CONFIG_KPM=y is not present after olddefconfig. This SukiSU ref/kernel may not expose it."
fi
echo "==> Final config scan"
grep -E 'CONFIG_KSU|CONFIG_KPM|CONFIG_KALLSYMS|CONFIG_LOCALVERSION|CONFIG_TCP_CONG_BBR|CONFIG_DEFAULT_BBR|CONFIG_IP_SET|CONFIG_NTSYNC|CONFIG_LTO|CONFIG_CC_OPTIMIZE|CONFIG_NETFILTER_XT_TARGET_HL|CONFIG_ADIOS|CONFIG_BBG|CONFIG_BASEBAND|CONFIG_ZRAM|CONFIG_ZSMALLOC|CONFIG_CRYPTO_LZ4|CONFIG_LZ4|CONFIG_ZSTD' out/.config | tee "$GITHUB_WORKSPACE/_ox_debug/config-scan.txt" || true
# ---- v109 SUSFS config audit ----
# Verifies that every SUSFS option we asked for actually landed as =y in
# the FINAL .config. Previously four options were requested that no root
# project defines, so olddefconfig silently dropped them and the build
# looked correct while the features were absent. This turns that class of
# silent failure into a visible warning.
{
echo "SUSFS config audit (expected =y unless noted)"
for s in CONFIG_KSU_SUSFS CONFIG_KSU_SUSFS_SUS_PATH CONFIG_KSU_SUSFS_SUS_MOUNT \
CONFIG_KSU_SUSFS_SUS_KSTAT CONFIG_KSU_SUSFS_SUS_MAP \
CONFIG_KSU_SUSFS_SPOOF_UNAME CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG \
CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS CONFIG_KSU_SUSFS_OPEN_REDIRECT; do
if grep -q "^${s}=y" out/.config; then
echo " OK ${s}=y"
else
echo " MISSING ${s} (requested but not enabled)"
echo "::warning::SUSFS option ${s} was requested but is NOT enabled in the final .config."
fi
done
if grep -q "^CONFIG_KSU_SUSFS_ENABLE_LOG=y" out/.config; then
echo " NOTE CONFIG_KSU_SUSFS_ENABLE_LOG=y (expected off for battery)"
echo "::warning::SUSFS kernel logging is ON; this costs battery on a daily driver."
else
echo " OK CONFIG_KSU_SUSFS_ENABLE_LOG disabled (battery-friendly)"
fi
} | tee "$GITHUB_WORKSPACE/_ox_debug/susfs-config-audit.txt" || true
- name: "Build kernel"
id: build
shell: bash
run: |
set -euo pipefail
cd kernel_workspace/common
export PATH="$GITHUB_WORKSPACE/kernel_workspace/clang18/bin:$GITHUB_WORKSPACE/kernel_workspace/build-tools/path/linux-x86:$PATH"
export ARCH=arm64
export SUBARCH=arm64
export LLVM=1
export LLVM_IAS=1
export CCACHE_DIR="$CCACHE_DIR"
export CCACHE_BASEDIR="$GITHUB_WORKSPACE/kernel_workspace"
export CCACHE_NOHASHDIR=true
export CCACHE_COMPILERCHECK=none
export KBUILD_BUILD_USER="${{ inputs.BUILD_USER }}"
export KBUILD_BUILD_HOST="${{ inputs.BUILD_HOST }}"
if [ "${{ inputs.BUILD_TIME }}" = "F" ]; then
export KBUILD_BUILD_TIMESTAMP="$(date -u '+%a %b %d %H:%M:%S UTC %Y')"
else
export KBUILD_BUILD_TIMESTAMP="${{ inputs.BUILD_TIME }}"
fi
MAKE_ARGS=(O=out ARCH=arm64 LLVM=1 LLVM_IAS=1 CC="ccache clang")
echo "==> Building Image"
# ---- v109: report a driver version that matches the manager APK ----
# SukiSU's kernel/Makefile computes its version from a LIVE api.github.com
# call counting commits on upstream `main`:
# REPO_BRANCH := main
# KSU_VERSION := 40000 + LOCAL_COUNT - 2815
# That number climbs on every upstream push regardless of which commit we
# build, which is why the manager shows a permanent "version mismatch".
# We pin the driver to 6c13a06, which IS the v4.1.3 line (it predates the
# b8279c3 UAPI change), so 40796 -- the released v4.1.3 manager version --
# is the ACCURATE number here; the API-derived one is the misleading one.
# v109: default is now EMPTY, because the driver is pinned to the v4.2 line
# and you should install the v4.2.0 manager APK. Reporting 40796 would be
# wrong for that pairing. Set SUKISU_VERSION_COMMITS=3611 only if you go
# back to the OLD pair with the v4.1.3 APK.
SUKISU_VERSION_COMMITS="${SUKISU_VERSION_COMMITS-}"
if [ -n "$SUKISU_VERSION_COMMITS" ]; then
echo "==> Reported SukiSU driver version: $((40000 + SUKISU_VERSION_COMMITS - 2815)) (matches manager v4.1.3)"
MAKE_ARGS+=("GITHUB_COMMITS=$SUKISU_VERSION_COMMITS")
else
echo "==> Using upstream version computation (driver version will track SukiSU main and may not match the manager)."
fi
make -j"$(nproc --all)" "${MAKE_ARGS[@]}" Image 2>&1 | tee "$GITHUB_WORKSPACE/_ox_debug/build.log"
image="out/arch/arm64/boot/Image"
if [ ! -s "$image" ]; then
echo "::error::Kernel Image was not produced."
find out -maxdepth 6 -type f -name 'Image*' -printf '%p %s bytes\n' | tee "$GITHUB_WORKSPACE/_ox_debug/image-search.txt" || true
exit 1
fi
cp -f "$image" "$GITHUB_WORKSPACE/artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109"
sha256sum "$GITHUB_WORKSPACE/artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109" | tee "$GITHUB_WORKSPACE/artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109.sha256"
uts="unknown"
if [ -f out/include/generated/utsrelease.h ]; then
cp -f out/include/generated/utsrelease.h "$GITHUB_WORKSPACE/_ox_debug/utsrelease.h"
uts="$(sed -n 's/^#define UTS_RELEASE "\(.*\)"/\1/p' out/include/generated/utsrelease.h | head -n1)"
fi
echo "UTS_RELEASE=$uts" | tee "$GITHUB_WORKSPACE/_ox_debug/uts.txt"
echo "uts=$uts" >> "$GITHUB_OUTPUT"
ccache -s | tee "$GITHUB_WORKSPACE/_ox_debug/ccache-final.txt" || true
- name: "Package AnyKernel3 (single zip, banner, no breakage)"
shell: bash
run: |
set -euo pipefail
rm -rf ak3_work
git clone --depth=1 https://github.com/osm0sis/AnyKernel3.git ak3_work
git -C ak3_work rev-parse HEAD | tee "$GITHUB_WORKSPACE/_ox_debug/anykernel3_commit.txt"
# --- AK3 naming-convention guard (v109) ---
# AK3 renamed its anykernel.sh settings to UPPERCASE. We write both
# spellings, but if upstream ever changes convention AGAIN this guard
# makes it obvious in the log instead of silently shipping a zip that
# aborts at flash time with "Flash failed".
if grep -q '\$BLOCK' ak3_work/tools/ak3-core.sh; then
echo "AK3 convention: UPPERCASE (\$BLOCK) - our uppercase vars will be used."
elif grep -q '\$block' ak3_work/tools/ak3-core.sh; then
echo "AK3 convention: lowercase (\$block) - our legacy aliases will be used."
else
echo "::warning::Could not detect AK3 boot-partition variable convention in ak3-core.sh. Flashing may fail; inspect _ox_debug/anykernel3_commit.txt."
fi
# --- Bundle a best-effort RAM Expansion fix module into the AK3 zip ---
mkdir -p ak3_work/ramfix
cat > ak3_work/ramfix/module.prop <<'RAMFIXPROP'
id=op13_ram_expansion_fix
name=OP13 RAM Expansion Fix
version=v1
versionCode=1
author=ox1d3x3
description=Restores OnePlus RAM Expansion (extended RAM) under root by re-asserting persist.sys.oplus.nandswap.condition on each boot. Bundled with the kernel.
RAMFIXPROP
cat > ak3_work/ramfix/service.sh <<'RAMFIXSVC'
#!/system/bin/sh
MODDIR=${0%/*}
until [ "$(getprop sys.boot_completed)" = "1" ]; do sleep 2; done
sleep 8
resetprop persist.sys.oplus.nandswap.condition true 2>/dev/null || setprop persist.sys.oplus.nandswap.condition true 2>/dev/null || true
for n in /sys/block/zram0/hybridswap_dev_life /sys/block/zram*/hybridswap_dev_life; do
[ -e "$n" ] && echo 1 > "$n" 2>/dev/null || true
done
RAMFIXSVC
chmod 0755 ak3_work/ramfix/service.sh
rm -rf ak3_work/.git ak3_work/.github ak3_work/README.md || true
cp -f artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109 ak3_work/Image
cat > ak3_work/anykernel.sh <<'AK3'
### AnyKernel3 Ramdisk Mod Script
## osm0sis @ xda-developers
## OX1D3X3 OP13 SukiSU + CCTV18 clean SUSFS package
### AnyKernel setup
# global properties
properties() { '
kernel.string=${{ inputs.kernel_name }}
do.devicecheck=0
do.modules=0
do.systemless=0
do.cleanup=1
do.cleanuponabort=0
do.check_boot_version=0
device.name1=
device.name2=
device.name3=
device.name4=
device.name5=
supported.versions=
supported.patchlevels=
supported.vendorpatchlevels=
'; } # end properties
### AnyKernel install
## boot shell variables
# OP13 / PJZ110 is A/B slot device. Use partition-name detection, not old omap path.
# v109 FLASH FIX: AnyKernel3 upstream renamed these settings to UPPERCASE
# (BLOCK, IS_SLOT_DEVICE, ...). This workflow clones AK3 master unpinned,
# so a fresh build picks up the new ak3-core.sh, which IGNORES the old
# lowercase names. With $BLOCK unset, AK3 cannot resolve the boot
# partition and aborts immediately after "Installing..." -> "Flash failed".
# We therefore set BOTH spellings: uppercase for current AK3, lowercase
# for older/pinned AK3 revisions. Harmless either way.
BLOCK=boot
IS_SLOT_DEVICE=auto
SLOT_SELECT=active
RAMDISK_COMPRESSION=auto
PATCH_VBMETA_FLAG=auto
NO_MAGISK_CHECK=1
# legacy lowercase aliases (older AnyKernel3)
block=boot
is_slot_device=auto
slot_select=active
ramdisk_compression=auto
patch_vbmeta_flag=auto
no_magisk_check=1
# import functions/variables and setup patching - see for reference (DO NOT REMOVE)
. tools/ak3-core.sh
# --- OX1D3X3 CUSTOM START ---
ui_print ' '
ui_print '===================================================='
ui_print ' __ __ __ '
ui_print ' \ \ / //_ |'
ui_print ' \ V / | |'
ui_print ' > < | |'
ui_print ' / . \ | |'
ui_print ' /_/ \_\ |_|'
ui_print '===================================================='
ui_print ' '
ui_print 'PJZ110 | SukiSU Ultra [Inline StockLike Daily] + CCTV18 SUSFS'
ui_print 'Kernel : 6.6.118-${{ inputs.kernel_suffix }}'
ui_print 'Hook : ${{ inputs.hook_mode }} (target: inline SUSFS)'
ui_print 'Audit : CONFIG_KSU + CONFIG_KSU_SUSFS + inline hook audit'
ui_print 'Build : #${{ github.run_number }} (${{ inputs.kernel_suffix }})'
ui_print 'Builder: @Ox1d3x3'
ui_print 'Credits: @SukiSU-Ultra, @cctv18, osm0sis, @mrcxlinux'
ui_print 'Source : CCTV18 susfs4oki only - no mixed SUSFS patch tree'
ui_print "ZRAM : kernel config=${{ inputs.zram_module == 'On' }} / preferred=${{ inputs.zram_comp }}"
ui_print ' '
device="$(getprop ro.product.device 2>/dev/null || true)"
model="$(getprop ro.product.model 2>/dev/null || true)"
android="$(getprop ro.build.version.release 2>/dev/null || true)"
patch="$(getprop ro.build.version.security_patch 2>/dev/null || true)"
slot="$(getprop ro.boot.slot_suffix 2>/dev/null || true)"
[ -z "$slot" ] && slot="$(getprop ro.boot.slot 2>/dev/null || true)"
if [ -n "$device" ]; then ui_print "Device : $device ($model)"; fi
if [ -n "$android" ]; then ui_print "Android: $android"; fi
if [ -n "$patch" ]; then ui_print "Patch : $patch"; fi
if [ -n "$slot" ]; then ui_print "Slot : $slot"; fi
ui_print 'Target : active boot partition'
ui_print ' '
# Best-effort open project page (may be ignored in recovery)
if command -v cmd >/dev/null 2>&1; then
(cmd activity start --user 0 -a android.intent.action.VIEW -d 'https://github.com/ox1d3x3/Op13_Susfs_kernel' >/dev/null 2>&1 &) || true
elif command -v am >/dev/null 2>&1; then
(am start --user 0 -a android.intent.action.VIEW -d 'https://github.com/ox1d3x3/Op13_Susfs_kernel' >/dev/null 2>&1 &) || true
fi
# --- OX1D3X3 CUSTOM END ---
# GKI check
kernel_version=$(cat /proc/version | awk -F '-' '{print $1}' | awk '{print $3}')
case $kernel_version in
5.1*) ksu_supported=true ;;
6.1*) ksu_supported=true ;;
6.6*) ksu_supported=true ;;
*) ksu_supported=false ;;
esac
ui_print ' '
ui_print ' -> Thank you for using this kernel'
$ksu_supported || abort ' -> Non-GKI device, abort.'
# boot install
split_boot
if [ -f "$SPLITIMG/ramdisk.cpio" ]; then
unpack_ramdisk
write_boot
else
flash_boot
fi
ui_print ' '
# --- OX RAM Expansion auto-install (best effort; needs decrypted /data) ---
if [ -d /data/adb/modules ] && [ -d "$home/ramfix" ]; then
ui_print ' Installing bundled RAM Expansion fix...'
RFM=/data/adb/modules/op13_ram_expansion_fix
mkdir -p "$RFM"
cp -f "$home/ramfix/module.prop" "$RFM/module.prop" 2>/dev/null
cp -f "$home/ramfix/service.sh" "$RFM/service.sh" 2>/dev/null
chmod 0755 "$RFM/service.sh" 2>/dev/null
rm -f "$RFM/disable" "$RFM/remove" "$RFM/update" 2>/dev/null
ui_print ' RAM Expansion fix installed (applies after reboot).'
else
ui_print ' RAM Expansion: /data not mounted - use X1 Kernel Manager instead.'
fi
ui_print ' Reboot Your System '
ui_print ' '
ui_print ' '
AK3
sed -i 's/^ //' ak3_work/anykernel.sh
chmod 0755 ak3_work/anykernel.sh
cat > ak3_work/OX_CCTV18_CLEAN_BUILD_INFO.txt <<EOF
OX1D3X3 OP13 SukiSU Ultra + CCTV18 INLINE SUSFS v109
Device: OnePlus 13 / sun / PJZ110
Kernel source: cctv18/android_kernel_common_oneplus_sm8750 @ ${{ env.CCTV18_KERNEL_REF }}
Build assets: cctv18/oppo_oplus_realme_sm8750
Root: SukiSU Ultra via official setup.sh, requested=${{ inputs.sukisu_ref }}, selected=${SUKISU_SELECTED_REF:-unknown}
SUSFS: cctv18/susfs4oki only, ref=${{ inputs.susfs_ref }}
SUSFS patch policy: ${{ inputs.susfs_patch_policy }}
WildKernels SUSFS patch tree: NOT USED
TheWildJames kernel_patches: NOT USED
KernelSU-Next/ReSukiSU root glue: NOT USED in this SukiSU-first workflow
Profile: ${{ inputs.profile }}
Optimise: ${{ inputs.optimise }}
ZRAM/LZ4 config: ${{ inputs.zram_module == 'On' }} / compressor=${{ inputs.zram_comp }}
ZRAM runtime tuner: X1 Kernel Manager module (separate tuner zip removed)
UTS_RELEASE: ${{ steps.build.outputs.uts }}
Built UTC: $(date -u '+%Y-%m-%d %H:%M:%S')
EOF
sed -i 's/^ //' ak3_work/OX_CCTV18_CLEAN_BUILD_INFO.txt
zip_name="AK3_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109_${{ github.run_number }}_FLASH_THIS.zip"
( cd ak3_work && zip -r9 "../artifacts/$zip_name" . >/dev/null )
sha256sum "artifacts/$zip_name" | tee "artifacts/$zip_name.sha256"
echo "==> AnyKernel3 package scan"
unzip -p "artifacts/$zip_name" anykernel.sh | sed -n '1,130p' | tee _ox_debug/packaged-anykernel.sh.txt
if unzip -p "artifacts/$zip_name" anykernel.sh | grep -q 'omap_hsmmc'; then
echo "::error::Packaged anykernel.sh still contains old omap boot path."
exit 1
fi
if unzip -p "artifacts/$zip_name" anykernel.sh | grep -q 'ExampleKernel'; then
echo "::error::Packaged anykernel.sh still contains default ExampleKernel banner."
exit 1
fi
if ! unzip -p "artifacts/$zip_name" anykernel.sh | grep -q 'OX1D3X3 CUSTOM START'; then
echo "::error::OX1D3X3 custom install banner was not packaged."
exit 1
fi
if ! unzip -p "artifacts/$zip_name" anykernel.sh | grep -q 'SukiSU Ultra \[Inline StockLike Daily\]'; then
echo "::error::OX install info banner was not packaged."
exit 1
fi
- name: "Write build summary and collect debug"
if: always()
shell: bash
run: |
set -euo pipefail
mkdir -p _ox_debug artifacts
{
echo "Workflow: OP13 SukiSU Ultra"
echo "Profile: ${{ inputs.profile }}"
echo "SukiSU Ultra requested ref: ${{ inputs.sukisu_ref }}"
echo "SukiSU Ultra selected ref: ${SUKISU_SELECTED_REF:-unknown}"
echo "SUSFS enabled: ${{ inputs.SUSFS }}"
echo "SUSFS ref: ${{ inputs.susfs_ref }}"
echo "SUSFS patch policy: ${{ inputs.susfs_patch_policy }}"
echo "Hook mode: ${{ inputs.hook_mode }}"
echo "KPM enabled: ${{ inputs.kpm_enable }}"
echo "Multi-manager support: ${{ inputs.multi_manager_support }}"
echo "Manager spoof patch: true (fixed default)"
echo "LZ4/ZSTD patch group: ${{ inputs.lz4_zram_patch == 'On' }}"
echo "LZ4KD patch group: false (fixed default for stability)"
echo "Kernel ref: ${CCTV18_KERNEL_REF}"
echo "Stock mimic: true"
echo "Kernel suffix: ${{ inputs.kernel_suffix }}"
echo "BBR mode: ${{ inputs.bbr }}"
echo "Better net/proxy: ${{ inputs.proxy }}"
echo "ZRAM/LZ4: ${{ inputs.zram_module }} / ${{ inputs.zram_comp }}"
echo "No WildKernels SUSFS patch tree is cloned or used."
echo "No TheWildJames kernel_patches tree is cloned or used."
echo "No KernelSU-Next/ReSukiSU root glue is cloned or used."
echo "UTS: ${{ steps.build.outputs.uts }}"
} | tee _ox_debug/build-summary.txt
if [ -d kernel_workspace/susfs4ksu ]; then
git -C kernel_workspace/susfs4ksu remote -v | tee _ox_debug/susfs-remote-final.txt || true
fi
if [ -d cctv18_assets ]; then
git -C cctv18_assets remote -v | tee _ox_debug/cctv18-assets-remote.txt || true
fi
if [ -d kernel_workspace/common ]; then
find kernel_workspace/common -name '*.rej' -o -name '*.orig' 2>/dev/null | sort | tee _ox_debug/rejects-orig-files.txt || true
fi
find artifacts _ox_debug -maxdepth 3 -type f -printf '%p %s bytes\n' 2>/dev/null | sort | tee _ox_debug/artifact-preview.txt || true
if [ -f "$GITHUB_STEP_SUMMARY" ]; then
{
echo "# OP13 SukiSU Ultra + CCTV18 Inline StockLike Daily v109"
echo ""
echo "## Verdict"
echo "- Main flashable zip uses fixed AnyKernel3 OP13 boot detection."
echo "- SUSFS source: CCTV18 only."
echo "- ZRAM/LZ4 config enabled: ${{ inputs.zram_module == 'On' }} / ${{ inputs.zram_comp }}."
echo "- ZRAM runtime tuning: use the X1 Kernel Manager module (separate tuner zip removed)."
echo ""
echo "## Outputs"
find artifacts -maxdepth 1 -type f -printf '- `%f`\n' 2>/dev/null | sort
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: "Upload build outputs (Actions artifact)"
if: always()
uses: actions/upload-artifact@v6
with:
name: OP13-SUKISU-CCTV18-INLINE-STOCKLIKE-v109-${{ github.run_number }}
path: |
artifacts/**
_ox_debug/**
if-no-files-found: warn
compression-level: 6
retention-days: 14
- name: "Summary"
if: always()
shell: bash
run: |
set -euo pipefail
if [ ! -s artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109 ]; then
echo "::error::Build failed before producing Image. Download OP13-SUKISU-CCTV18-INLINE-STOCKLIKE-v109 debug artifact."
exit 1
fi
if [ "true" = "true" ] && ! ls artifacts/AK3_OP13_SukiSUUltra_CCTV18_inline_stocklike_v109_*_FLASH_THIS.zip >/dev/null 2>&1; then
echo "::error::Image built but AnyKernel3 flashable ZIP was not produced."
exit 1
fi
echo "✅ OP13 SukiSU Ultra + CCTV18-only SUSFS inline stock-like daily-driver build completed."
echo "✅ Fixed AnyKernel3 boot partition detection: BLOCK=boot, IS_SLOT_DEVICE=auto, SLOT_SELECT=active."
echo "✅ No WildKernels/TheWildJames SUSFS patch tree was used."