OP13 KSUN #47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: OP13 KSUN | |
| 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_TOOLCHAIN_RELEASE: LLVM-Clang18-r510928 | |
| CCACHE_DIR: ${{ github.workspace }}/.ccache_op13_ksun_cctv18_v92 | |
| CCACHE_MAXSIZE: 8G | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| profile: | |
| description: "Daily-driver profile. stock_daily is the recommended default." | |
| required: true | |
| type: choice | |
| options: | |
| - stock_daily | |
| - stock_plus | |
| - minimal_safe | |
| - debug_only | |
| default: stock_daily | |
| ksun_ref: | |
| description: "KernelSU-Next ref. For clean CCTV18 KSUN lane keep dev-susfs unless you are testing a known-good commit." | |
| required: true | |
| type: string | |
| default: "dev-susfs" | |
| susfs_enable: | |
| description: "Enable CCTV18 susfs4oki only. No WildKernels/TheWildJames SUSFS patch tree is used." | |
| required: true | |
| type: boolean | |
| default: true | |
| susfs_ref: | |
| description: "CCTV18 susfs4oki branch/ref. OP13 Android 15/16 6.6 should use oki-android15-6.6." | |
| required: true | |
| type: string | |
| default: "oki-android15-6.6" | |
| susfs_patch_policy: | |
| description: "strict fails on any reject; audited_cctv18 allows only the known task_mmu reject; native_cctv18 audits but continues like CCTV18." | |
| required: true | |
| type: choice | |
| options: | |
| - audited_cctv18 | |
| - strict | |
| - native_cctv18 | |
| default: audited_cctv18 | |
| hook_mode: | |
| description: "KernelSU hook/audit mode. cctv18_susfs_auto is recommended for clean KSUN dev-susfs; strict_none_hook_test only passes if the KSUN tree exposes CONFIG_KSU_NONE_HOOK." | |
| required: true | |
| type: choice | |
| options: | |
| - cctv18_susfs_auto | |
| - hybrid_compat | |
| - strict_none_hook_test | |
| default: cctv18_susfs_auto | |
| cctv18_kernel_ref: | |
| description: "CCTV18 OP13 6.6.118 source branch/ref (update this when a new OTA bumps the kernel)." | |
| required: true | |
| type: string | |
| default: "oneplus/sm8750_b_16.0.0_oneplus_13_6.6.118" | |
| 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: true | |
| 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-KSUN'." | |
| required: false | |
| type: string | |
| default: "PJZ110 | KernelSU-Next [StockLike] + CCTV18 SUSFS (OP-OX)" | |
| stock_mimic: | |
| description: "Keep uname -r stock-like by using only the supplied kernel_suffix as LOCALVERSION." | |
| required: true | |
| type: boolean | |
| default: true | |
| optimise: | |
| description: "O2 is the stable daily-driver default. O3 is test-only." | |
| required: true | |
| type: choice | |
| options: | |
| - O2 | |
| - O3 | |
| default: O2 | |
| adios: | |
| description: "Apply CCTV18 ADIOS scheduler patch if it exists and dry-run applies cleanly." | |
| required: true | |
| type: boolean | |
| default: true | |
| baseband_guard: | |
| description: "Apply CCTV18 Baseband Guard patch if it exists and dry-run applies cleanly." | |
| required: true | |
| type: boolean | |
| default: true | |
| better_net: | |
| description: "Enable netfilter/ipset/TTL support for useful advanced networking without making BBR default." | |
| required: true | |
| type: boolean | |
| default: true | |
| bbr_mode: | |
| description: "enabled compiles TCP BBR in (selectable at runtime, not forced default). Recommended for daily use." | |
| required: true | |
| type: choice | |
| options: | |
| - off | |
| - enabled | |
| - default | |
| default: enabled | |
| zram_lz4: | |
| description: "Enable kernel ZRAM + LZ4/LZ4HC/ZSTD support in config. This is config-only, not a random patch mix." | |
| required: true | |
| type: boolean | |
| default: true | |
| zram_comp: | |
| description: "Preferred ZRAM default compressor when kernel config supports CONFIG_ZRAM_DEF_COMP." | |
| required: true | |
| type: choice | |
| options: | |
| - lz4 | |
| - lzo-rle | |
| - zstd | |
| default: lz4 | |
| ntsync: | |
| description: "Enable NTSYNC config if available." | |
| required: true | |
| type: boolean | |
| default: true | |
| unicode_fix: | |
| description: "Apply CCTV18 Unicode-related patch if present and dry-run applies cleanly." | |
| required: true | |
| type: boolean | |
| default: true | |
| droidspaces: | |
| description: "Enable Droidspaces/container namespace options. Off is safer for normal phone use." | |
| required: true | |
| type: boolean | |
| default: false | |
| rekernel: | |
| description: "Apply Re:Kernel patch when present (best-effort, dry-run gated; skips if it does not apply cleanly)." | |
| required: true | |
| type: boolean | |
| default: true | |
| package_ak3: | |
| description: "Create fixed AnyKernel3 flashable ZIP plus raw Image." | |
| required: true | |
| type: boolean | |
| default: true | |
| clean_ccache: | |
| description: "Delete ccache before building. Use only when you suspect cache contamination." | |
| required: true | |
| type: boolean | |
| default: false | |
| BUILD_TIME: | |
| description: "Custom build time string. Enter F to use current UTC; enter stock timestamp string for stock-like build time." | |
| required: false | |
| type: string | |
| default: "F" | |
| upload_debug: | |
| description: "Always upload logs, final config, rejects, UTS, Image and ZIPs." | |
| required: true | |
| type: boolean | |
| default: true | |
| concurrency: | |
| group: op13-ksun-cctv18-clean-v92-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| actions: read | |
| jobs: | |
| build: | |
| name: "${{ inputs.profile }} | KSUN=${{ inputs.ksun_ref }} | CCTV18 SUSFS=${{ inputs.susfs_enable }}" | |
| 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-ksun-cctv18-v92-${{ inputs.profile }}-${{ inputs.susfs_enable }}-${{ inputs.ksun_ref }}-${{ github.ref_name }} | |
| restore-keys: | | |
| ccache-${{ runner.os }}-op13-ksun-cctv18-v92-${{ inputs.profile }}-${{ inputs.susfs_enable }}- | |
| ccache-${{ runner.os }}-op13-ksun-cctv18-v92- | |
| - 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: ${{ inputs.cctv18_kernel_ref }}" | |
| aria2c -s16 -x16 -k1M --retry-wait=5 --max-tries=5 \ | |
| "https://github.com/${CCTV18_COMMON_REPO}/archive/refs/heads/${{ inputs.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: "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 KernelSU-Next" | |
| id: ksun | |
| 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 KernelSU-Next via CCTV18-compatible pershoot dev-susfs setup path" | |
| echo "Requested KSUN ref: ${{ inputs.ksun_ref }}" | |
| curl -LSs "https://raw.githubusercontent.com/pershoot/KernelSU-Next/refs/heads/dev-susfs/kernel/setup.sh" | bash -s "${{ inputs.ksun_ref }}" | |
| if [ -d common/drivers/kernelsu ]; then | |
| KSU_TREE="common/drivers/kernelsu" | |
| elif [ -d KernelSU-Next ]; then | |
| KSU_TREE="KernelSU-Next" | |
| elif [ -d common/KernelSU-Next ]; then | |
| KSU_TREE="common/KernelSU-Next" | |
| else | |
| echo "::error::KernelSU-Next 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 "ksu_tree=$KSU_TREE" >> "$GITHUB_OUTPUT" | |
| if [ -d "$KSU_TREE/.git" ]; then | |
| git -C "$KSU_TREE" rev-parse HEAD | tee ../_ox_debug/ksun_commit.txt || true | |
| fi | |
| KSU_KBUILD="common/drivers/kernelsu/kernel/Kbuild" | |
| if [ -f "$KSU_KBUILD" ]; then | |
| KSU_VERSION="33129" | |
| if [ -d "$KSU_TREE/.git" ]; then | |
| cnt="$(git -C "$KSU_TREE" rev-list --count HEAD 2>/dev/null || true)" | |
| if [ -n "$cnt" ]; then KSU_VERSION="$((cnt + 30000))"; fi | |
| fi | |
| KSU_TAG="v3.2.0" | |
| sed -i "s/^KSU_VERSION_FALLBACK := .*/KSU_VERSION_FALLBACK := $KSU_VERSION/g" "$KSU_KBUILD" || true | |
| sed -i "s/^KSU_VERSION_TAG_FALLBACK := .*/KSU_VERSION_TAG_FALLBACK := $KSU_TAG/g" "$KSU_KBUILD" || true | |
| echo "KSUVER=$KSU_VERSION" >> "$GITHUB_ENV" | |
| echo "KSUTAG=$KSU_TAG" >> "$GITHUB_ENV" | |
| echo "ksuver=$KSU_VERSION" >> "$GITHUB_OUTPUT" | |
| fi | |
| # CCTV18 local manager signature compatibility patch only. This is not a SUSFS source mix. | |
| if [ -f "$GITHUB_WORKSPACE/cctv18_assets/other_patch/apk_sign.patch" ] && [ -d common/drivers/kernelsu ]; then | |
| echo "==> Applying CCTV18 apk_sign.patch" | |
| ( cd common/drivers/kernelsu && patch -p2 -N -F 3 < "$GITHUB_WORKSPACE/cctv18_assets/other_patch/apk_sign.patch" ) || true | |
| fi | |
| - name: "Apply CCTV18-only SUSFS" | |
| if: ${{ inputs.susfs_enable == true }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd kernel_workspace | |
| rm -rf susfs4ksu | |
| echo "==> Cloning CCTV18 SUSFS only: cctv18/susfs4oki @ ${{ inputs.susfs_ref }}" | |
| git clone --depth=1 --branch "${{ inputs.susfs_ref }}" https://github.com/cctv18/susfs4oki.git susfs4ksu | |
| 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/ | |
| # v92: 69_hide_stuff.patch is deliberately NOT staged for this lane. | |
| # It has 4 hunks against fs/proc/task_mmu.c. Hunks 1, 2 and 4 (which ADD | |
| # show_vma_header_prefix_fake(), "struct dentry *dentry;" and the "bypass:" | |
| # label) match near-stock context and apply, but hunk 3 -- the only hunk | |
| # that USES all three -- needs SUSFS's "bypass_orig_flow:" label as context. | |
| # This lane's audited policy intentionally tolerates a task_mmu.c SUSFS | |
| # reject, so hunk 3 fails and leaves three orphaned symbols behind: | |
| # error: unused function 'show_vma_header_prefix_fake' | |
| # warning: unused variable 'dentry' / unused label 'bypass' | |
| # The ReSukiSU and SukiSU lanes have never applied this patch and both | |
| # build and boot correctly, so skipping it here costs nothing: it is | |
| # supplementary /proc hiding, while SUSFS provides the real hiding. | |
| echo "==> Skipping 69_hide_stuff.patch for the KSUN stable lane (see comment above)." | |
| rm -f common/69_hide_stuff.patch | |
| cd common | |
| # ---- v92: upstream security fix CVE-2026-43499 (rtmutex) ---- | |
| # NULL-pointer dereference in remove_waiter(): when called via | |
| # rt_mutex_start_proxy_lock(), waiter->task may be NULL (never enqueued). | |
| # From cctv18/oppo_oplus_realme_sm8750, the asset repo already cloned here. | |
| # CCTV18's own 6.6.118 builder applies it unconditionally (no "|| true"). | |
| # SAFETY: touches ONLY kernel/locking/rtmutex.c and rtmutex_api.c -- no | |
| # headers, so no struct/prototype change and no GKI 2.0 KMI impact. | |
| # Verified absent from the 6.6.118 source. All-or-nothing: skip + warn | |
| # 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. 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 | |
| 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 "::warning::Continuing with audited CCTV18 SUSFS reject handling. 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 | |
| if [ -f 69_hide_stuff.patch ]; then | |
| # v92 FIX (KSUN build failure on 6.6.118): | |
| # 69_hide_stuff.patch modifies fs/proc/task_mmu.c and its hunks use | |
| # SUSFS's "bypass_orig_flow:" label as CONTEXT -- i.e. it assumes the | |
| # SUSFS task_mmu.c hunk already applied. This lane's audited policy | |
| # deliberately TOLERATES a ./fs/proc/task_mmu.c reject, so that context | |
| # can be absent. Applying with "|| true" then left the file half-patched: | |
| # "struct dentry *dentry;" and the "bypass:" label were inserted while | |
| # the hunks that USE them failed, producing | |
| # error: unused variable 'dentry' [-Werror,-Wunused-variable] | |
| # error: unused label 'bypass' [-Werror,-Wunused-label] | |
| # Fix: dry-run first and apply ALL-OR-NOTHING, matching the discipline | |
| # already used by clean_apply_optional_patch below. If it cannot apply | |
| # fully, skip it entirely rather than contaminate task_mmu.c. This patch | |
| # is supplementary /proc hiding only -- SUSFS provides the real hiding, | |
| # and the ReSukiSU/SukiSU lanes skip it outright and boot fine. | |
| echo "==> Checking CCTV18 69_hide_stuff.patch (all-or-nothing)" | |
| if patch --dry-run -p1 -N -F 3 < 69_hide_stuff.patch \ | |
| > "$GITHUB_WORKSPACE/_ox_debug/dryrun-69_hide_stuff.log" 2>&1; then | |
| echo "==> Applying CCTV18 69_hide_stuff.patch (dry-run clean)" | |
| patch -p1 -N -F 3 < 69_hide_stuff.patch \ | |
| > "$GITHUB_WORKSPACE/_ox_debug/apply-69_hide_stuff.log" 2>&1 || true | |
| else | |
| echo "::warning::69_hide_stuff.patch would not apply cleanly (expected when the SUSFS task_mmu.c hunk rejected); skipping it to avoid partial patch contamination." | |
| cat "$GITHUB_WORKSPACE/_ox_debug/dryrun-69_hide_stuff.log" || true | |
| fi | |
| # Safety net (defence in depth): if task_mmu.c somehow still ends up | |
| # with an orphaned label/declaration, strip them here rather than | |
| # failing deep in the compile with -Werror. Both checks are | |
| # conservative: they only fire when the symbol has NO real user. | |
| if [ -f fs/proc/task_mmu.c ]; then | |
| if grep -qE '^[[:space:]]*bypass:[[:space:]]*$' fs/proc/task_mmu.c \ | |
| && ! grep -q 'goto bypass;' fs/proc/task_mmu.c; then | |
| echo "::warning::Removing orphaned 'bypass:' label from fs/proc/task_mmu.c" | |
| sed -i '/^[[:space:]]*bypass:[[:space:]]*$/d' fs/proc/task_mmu.c | |
| fi | |
| if grep -qE '^[[:space:]]*struct dentry \*dentry;[[:space:]]*$' fs/proc/task_mmu.c \ | |
| && ! grep -qE '\bdentry[[:space:]]*(=|->)' fs/proc/task_mmu.c; then | |
| echo "::warning::Removing orphaned 'struct dentry *dentry;' from fs/proc/task_mmu.c" | |
| sed -i '/^[[:space:]]*struct dentry \*dentry;[[:space:]]*$/d' fs/proc/task_mmu.c | |
| fi | |
| fi | |
| 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: "KSUN SELinux policy-query compatibility guard" | |
| if: ${{ inputs.susfs_enable == true }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd kernel_workspace/common | |
| mkdir -p "$GITHUB_WORKSPACE/_ox_debug" | |
| { | |
| echo "==> KernelSU-Next + CCTV18 SELinux policy-query compatibility guard" | |
| echo "KSUN reaches final link, then fails on SELinux policy-query symbols without this guard." | |
| echo "The failure was unresolved SELinux policy-query symbols, not root/SUSFS integration." | |
| echo "For the stable daily-driver KSUN 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. KernelSU-Next 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 KSUN lane. Core SUSFS and KernelSU-Next 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 }}" "(^|/).*adios.*\.patch$" | |
| clean_apply_optional_patch "baseband_guard" "${{ inputs.baseband_guard }}" "(^|/).*(baseband|bbg).*\.patch$" | |
| clean_apply_optional_patch "unicode" "${{ inputs.unicode_fix }}" "(^|/).*unicode.*\.patch$" | |
| clean_apply_optional_patch "rekernel" "${{ inputs.rekernel }}" "(^|/).*(rekernel|re-kernel).*\.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 v92 daily-driver config profile: ${{ inputs.profile }}" | |
| set_y CONFIG_KSU | |
| echo "==> Requested KernelSU hook/audit mode: ${{ inputs.hook_mode }}" | |
| case "${{ inputs.hook_mode }}" in | |
| cctv18_susfs_auto) | |
| # Clean CCTV18 KSUN lane uses pershoot KernelSU-Next dev-susfs. | |
| # That Kconfig does not expose CONFIG_KSU_NONE_HOOK; KSU depends on KPROBES || SUSFS. | |
| # Therefore the correct clean audit is CONFIG_KSU=y + CONFIG_KSU_SUSFS=y, not forcing a foreign hook selector. | |
| true | |
| ;; | |
| hybrid_compat) | |
| # Keep KernelSU-Next/CCTV18 default selection with no artificial hook selector forcing. | |
| true | |
| ;; | |
| strict_none_hook_test) | |
| # Test-only: only useful if you change KSUN source to a tree that actually exposes CONFIG_KSU_NONE_HOOK. | |
| set_y CONFIG_KSU_NONE_HOOK | |
| set_n CONFIG_KSU_MANUAL_HOOK | |
| set_n CONFIG_KSU_KPROBES_HOOK | |
| set_n CONFIG_KSU_WITH_KPROBES | |
| ;; | |
| esac | |
| if [ "${{ inputs.susfs_enable }}" = "true" ]; then | |
| # v92: 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 | |
| if [ "${{ inputs.stock_mimic }}" = "true" ]; then | |
| set_str CONFIG_LOCALVERSION "-${{ inputs.kernel_suffix }}" | |
| else | |
| set_str CONFIG_LOCALVERSION "-${{ inputs.kernel_suffix }}-ox" | |
| fi | |
| 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_lz4 }}" = "true" ]; 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.better_net }}" = "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; do | |
| set_y "$opt" | |
| done | |
| # v92: 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 | |
| # Avoid the previous DEFAULT_BBR choice warning by clearing DEFAULT_BBR before handling BBR mode. | |
| set_n CONFIG_DEFAULT_BBR || true | |
| case "${{ inputs.bbr_mode }}" in | |
| off) | |
| set_n CONFIG_TCP_CONG_BBR | |
| ;; | |
| enabled) | |
| set_y CONFIG_TCP_CONG_BBR | |
| ;; | |
| default) | |
| set_y CONFIG_TCP_CONG_BBR | |
| set_y CONFIG_DEFAULT_BBR || true | |
| ;; | |
| esac | |
| if [ "${{ inputs.ntsync }}" = "true" ]; then | |
| set_y CONFIG_NTSYNC | |
| fi | |
| if [ "${{ inputs.droidspaces }}" = "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 | |
| ;; | |
| 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 KSUN/SUSFS hook audit" | tee "$GITHUB_WORKSPACE/_ox_debug/hook-mode-audit.txt" | |
| { | |
| echo "Requested hook/audit mode: ${{ inputs.hook_mode }}" | |
| echo "Detected KSUN hook selector symbols, if any:" | |
| grep -E 'CONFIG_KSU(_NONE_HOOK|_MANUAL_HOOK|_KPROBES_HOOK|_WITH_KPROBES|_SUSFS)=' out/.config || true | |
| echo "Detected generic KPROBES state:" | |
| grep -E 'CONFIG_KPROBES=' out/.config || true | |
| echo "KernelSU Kconfig hook selector availability:" | |
| if grep -Rqs 'config KSU_NONE_HOOK' ../KernelSU-Next/kernel ./drivers/kernelsu 2>/dev/null; then | |
| echo "KSU_NONE_HOOK symbol exists in this KSUN tree" | |
| else | |
| echo "KSU_NONE_HOOK symbol NOT exposed by this clean CCTV18/pershoot dev-susfs KSUN tree" | |
| 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_enable }}" = "true" ]; then | |
| if ! grep -q '^CONFIG_KSU_SUSFS=y' out/.config; then | |
| echo "::error::CONFIG_KSU_SUSFS=y missing after olddefconfig. This is the real blocker for clean CCTV18 SUSFS." | |
| exit 1 | |
| fi | |
| fi | |
| if [ "${{ inputs.hook_mode }}" = "strict_none_hook_test" ]; then | |
| if ! grep -Rqs 'config KSU_NONE_HOOK' ../KernelSU-Next/kernel ./drivers/kernelsu 2>/dev/null; then | |
| echo "::error::strict_none_hook_test requested, but this clean KSUN dev-susfs tree does not expose CONFIG_KSU_NONE_HOOK. Use cctv18_susfs_auto for this branch." | |
| exit 1 | |
| fi | |
| if ! grep -q '^CONFIG_KSU_NONE_HOOK=y' out/.config; then | |
| echo "::error::strict_none_hook_test requested, but CONFIG_KSU_NONE_HOOK=y is not present after olddefconfig." | |
| exit 1 | |
| fi | |
| fi | |
| echo "==> Final config scan" | |
| grep -E 'CONFIG_KSU|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 | |
| # ---- v92 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="ox1d3x3" | |
| export KBUILD_BUILD_HOST="op13-cctv18-ksun" | |
| 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 | |
| echo "==> KBUILD_BUILD_TIMESTAMP=$KBUILD_BUILD_TIMESTAMP" | |
| # ---- v92: unconditional task_mmu.c orphan repair (runs after ALL patching) ---- | |
| # CCTV18's 69_hide_stuff.patch has 4 hunks against fs/proc/task_mmu.c: | |
| # hunk 2 -> adds "struct dentry *dentry;" (generic context, applies to stock) | |
| # hunk 4 -> adds "bypass:" label (generic context, applies to stock) | |
| # hunk 1 -> adds show_vma_header_prefix_fake (needs SUSFS context) | |
| # hunk 3 -> adds the USES: "dentry = ..." and "goto bypass;" (needs SUSFS's | |
| # "bypass_orig_flow:" label as context) | |
| # This lane's audited policy deliberately tolerates a task_mmu.c SUSFS reject, | |
| # so hunks 1+3 can fail while 2+4 succeed, leaving the declaration and label | |
| # with no users: | |
| # error: unused variable 'dentry' [-Werror,-Wunused-variable] | |
| # error: unused label 'bypass' [-Werror,-Wunused-label] | |
| # This repair is deliberately placed here -- in the build step, unconditional | |
| # and independent of which patch path ran -- because an earlier attempt that | |
| # lived inside the "if [ -f 69_hide_stuff.patch ]" block did not prevent the | |
| # failure. Removing an unused declaration/label cannot change runtime | |
| # behaviour; both are no-ops once the code that would use them is absent. | |
| echo "==> task_mmu.c orphan scan (BEFORE repair):" | |
| grep -nE 'show_vma_header_prefix_fake|struct dentry \*dentry;|^[[:space:]]*bypass:|goto bypass;' fs/proc/task_mmu.c || echo " none found (clean)" | |
| python3 - <<'ORPHANFIX' | |
| import re, pathlib | |
| p = pathlib.Path('fs/proc/task_mmu.c') | |
| if not p.exists(): | |
| print('task_mmu.c not found; skipping orphan repair') | |
| else: | |
| s = p.read_text(errors='ignore'); orig = s | |
| # Orphaned label: "bypass:" with no "goto bypass;" anywhere. | |
| if re.search(r'^[ \t]*bypass:[ \t]*$', s, re.M) and not re.search(r'goto\s+bypass\s*;', s): | |
| s = re.sub(r'^[ \t]*bypass:[ \t]*\n', '', s, flags=re.M) | |
| print('removed orphaned "bypass:" label') | |
| # Orphaned declaration: "struct dentry *dentry;" with no assignment to the | |
| # local variable. Member accesses like "f_path.dentry" / "->dentry" are NOT | |
| # uses of this local, so they are excluded via the (?<![.>]) lookbehind. | |
| if re.search(r'^[ \t]*struct dentry \*dentry;[ \t]*$', s, re.M): | |
| used = re.search(r'(?<![.>])\bdentry\b\s*(=[^=]|->)', s) | |
| if not used: | |
| s = re.sub(r'^[ \t]*struct dentry \*dentry;[ \t]*\n', '', s, flags=re.M) | |
| print('removed orphaned "struct dentry *dentry;" declaration') | |
| if s != orig: | |
| p.write_text(s) | |
| print('task_mmu.c orphan repair applied') | |
| else: | |
| print('task_mmu.c: no orphaned symbols found (nothing to repair)') | |
| ORPHANFIX | |
| echo "==> task_mmu.c orphan scan (AFTER repair):" | |
| grep -nE 'show_vma_header_prefix_fake|struct dentry \*dentry;|^[[:space:]]*bypass:|goto bypass;' fs/proc/task_mmu.c || echo " none found (clean)" | |
| MAKE_ARGS=(O=out ARCH=arm64 LLVM=1 LLVM_IAS=1 CC="ccache clang") | |
| # v92 safety valve: downstream hiding patches can leave cosmetically-unused | |
| # symbols behind when a dependent hunk rejects. Downgrade ONLY these two | |
| # warning classes from errors so a cosmetic leftover can never again kill an | |
| # otherwise-good build. Everything else stays -Werror. | |
| export KCFLAGS="${KCFLAGS:-} -Wno-error=unused-variable -Wno-error=unused-label -Wno-error=unused-function" | |
| echo "==> KCFLAGS=$KCFLAGS" | |
| echo "==> Building Image" | |
| make -j"$(nproc --all)" "${MAKE_ARGS[@]}" KCFLAGS="$KCFLAGS" 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_KSUN_CCTV18_clean_stocklike_v92" | |
| sha256sum "$GITHUB_WORKSPACE/artifacts/Image_OP13_KSUN_CCTV18_clean_stocklike_v92" | tee "$GITHUB_WORKSPACE/artifacts/Image_OP13_KSUN_CCTV18_clean_stocklike_v92.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" | |
| if: ${{ inputs.package_ak3 == true }} | |
| 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 (v92) --- | |
| # 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_KSUN_CCTV18_clean_stocklike_v92 ak3_work/Image | |
| cat > ak3_work/anykernel.sh <<'AK3' | |
| ### AnyKernel3 Ramdisk Mod Script | |
| ## osm0sis @ xda-developers | |
| ## OX1D3X3 OP13 KSUN + 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. | |
| # v92 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 | KernelSU-Next [StockLike Daily] + CCTV18 SUSFS' | |
| ui_print 'Kernel : 6.6.118-${{ inputs.kernel_suffix }}' | |
| ui_print 'Hook : ${{ inputs.hook_mode }}' | |
| ui_print 'Audit : CONFIG_KSU + CONFIG_KSU_SUSFS required' | |
| ui_print 'Build : #${{ github.run_number }} (${{ inputs.kernel_suffix }})' | |
| ui_print 'Builder: @Ox1d3x3' | |
| ui_print 'Credits: @cctv18, KernelSU-Next, osm0sis, @mrcxlinux' | |
| ui_print 'Source : CCTV18 susfs4oki only - no mixed SUSFS patch tree' | |
| ui_print 'ZRAM : kernel config=${{ inputs.zram_lz4 }} / 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 KSUN + CCTV18 CLEAN SUSFS v92 | |
| Device: OnePlus 13 / sun / PJZ110 | |
| Kernel source: cctv18/android_kernel_common_oneplus_sm8750 @ ${{ inputs.cctv18_kernel_ref }} | |
| Build assets: cctv18/oppo_oplus_realme_sm8750 | |
| Root: KernelSU-Next via CCTV18-compatible pershoot dev-susfs setup path, ref=${{ inputs.ksun_ref }} | |
| 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 | |
| Profile: ${{ inputs.profile }} | |
| Optimise: ${{ inputs.optimise }} | |
| ZRAM/LZ4 config: ${{ inputs.zram_lz4 }} / 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_KSUN_CCTV18_clean_stocklike_v92_${{ 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 'KernelSU-Next \[StockLike Daily\]'; then | |
| echo "::error::OX install info banner was not packaged." | |
| exit 1 | |
| fi | |
| - name: "Clean-source audit and debug collection" | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p _ox_debug artifacts | |
| { | |
| echo "Workflow: OP13 KSUN CCTV18 Clean StockLike Daily v92" | |
| echo "Profile: ${{ inputs.profile }}" | |
| echo "KSUN ref: ${{ inputs.ksun_ref }}" | |
| echo "SUSFS enabled: ${{ inputs.susfs_enable }}" | |
| echo "SUSFS ref: ${{ inputs.susfs_ref }}" | |
| echo "SUSFS patch policy: ${{ inputs.susfs_patch_policy }}" | |
| echo "Hook mode: ${{ inputs.hook_mode }}" | |
| echo "Kernel ref: ${{ inputs.cctv18_kernel_ref }}" | |
| echo "Stock mimic: ${{ inputs.stock_mimic }}" | |
| echo "Kernel name: ${{ inputs.kernel_name }}" | |
| echo "Build time input: ${{ inputs.BUILD_TIME }}" | |
| echo "BBR mode: ${{ inputs.bbr_mode }}" | |
| echo "Better net: ${{ inputs.better_net }}" | |
| echo "ZRAM/LZ4: ${{ inputs.zram_lz4 }} / ${{ inputs.zram_comp }}" | |
| echo "No WildKernels SUSFS patch tree is cloned or used." | |
| echo "No TheWildJames kernel_patches tree 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 KSUN + CCTV18 Clean StockLike Daily v92" | |
| 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_lz4 }} / ${{ 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" | |
| if: always() && (inputs.upload_debug == true || success()) | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: OP13-KSUN-CCTV18-CLEAN-STOCKLIKE-v92-${{ 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_KSUN_CCTV18_clean_stocklike_v92 ]; then | |
| echo "::error::Build failed before producing Image. Download OP13-KSUN-CCTV18-CLEAN-STOCKLIKE-v92 debug artifact." | |
| exit 1 | |
| fi | |
| if [ "${{ inputs.package_ak3 }}" = "true" ] && ! ls artifacts/AK3_OP13_KSUN_CCTV18_clean_stocklike_v92_*_FLASH_THIS.zip >/dev/null 2>&1; then | |
| echo "::error::Image built but AnyKernel3 flashable ZIP was not produced." | |
| exit 1 | |
| fi | |
| echo "✅ OP13 KSUN + CCTV18-only SUSFS clean 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." |