Skip to content

OP13 SukiSU Ultra

OP13 SukiSU Ultra #220

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 -- both OLD or both NEW. OLD PAIR (default): sukisu 6c13a06 + susfs 83dcd81. Driver UAPI matches the released v4.1.3 manager APK, so App Profile toggles SAVE correctly; su works as long as kernel_umount is not forced on. NEW PAIR: sukisu f2201c472b607557ed8563e2d1b729af25f3111b + susfs 40e6c19ec2ca88804d6c2e3ddf52623af6685463. su works unconditionally, BUT that driver includes b8279c3 (new manager<->driver UAPI) and no v4.2 manager APK is released, so the manager reports a version mismatch and every App Profile write FAILS. Mixing generations breaks su."
required: true
type: string
default: "6c13a0695a1115e1000c998872b66f4ff5b2f11e"
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. 83dcd81 (default) is the last commit using the susfs_is_current_proc_umounted() guard -- pair with sukisu 6c13a06. 40e6c19 uses susfs_is_current_proc_no_su() (upstream fix 525c450) -- pair with sukisu f2201c4. A mismatched pair makes the kernel skip the su redirect for every app and shell."
required: true
type: string
default: "83dcd81e7e13440e1b8b756dea4e8311e54f4c3a"
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
# v108: 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 }}"
# v108: 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
# ---- v108: 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
# ---- v108: 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 v108 SukiSU stable lane."
echo " SUSFS inline remains enabled; extra SELinux/map-hide policy-query patches are deferred to ReSuki/test builds."
# ---- v108: 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
# v108: 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 v108 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
# v108: 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
# v108: 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
# ---- v108 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"
# ---- v108: 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.
# 40796 = 40000 + 3611 - 2815. Set SUKISU_VERSION_COMMITS="" to opt out.
SUKISU_VERSION_COMMITS="${SUKISU_VERSION_COMMITS-3611}"
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_v108"
sha256sum "$GITHUB_WORKSPACE/artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v108" | tee "$GITHUB_WORKSPACE/artifacts/Image_OP13_SukiSUUltra_CCTV18_inline_stocklike_v108.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 (v108) ---
# 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_v108 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.
# v108 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 v108
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_v108_${{ 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 v108"
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-v108-${{ 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_v108 ]; then
echo "::error::Build failed before producing Image. Download OP13-SUKISU-CCTV18-INLINE-STOCKLIKE-v108 debug artifact."
exit 1
fi
if [ "true" = "true" ] && ! ls artifacts/AK3_OP13_SukiSUUltra_CCTV18_inline_stocklike_v108_*_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."