Skip to content

Build and Release OnePlus Kernels #489

Build and Release OnePlus Kernels

Build and Release OnePlus Kernels #489

name: Build and Release OnePlus Kernels
permissions:
contents: write
actions: write
on:
workflow_dispatch:
inputs:
make_release:
description: 'Do you want to create a release?'
required: true
type: boolean
default: false
op_model:
description: 'Select the OnePlus kernels to build'
required: true
type: choice
options:
- OOS14+15+16
- OOS15+16
- OOS14+15
- OOS16
- OOS15
- OOS14
- android16-6.12
- android15-6.6
- android14-6.1
- android13-5.15
- android12-5.10
default: OOS14+15+16
ksu_options:
description: 'Enter KernelSU build json'
required: true
type: string
default: '[{"type":"ksun","hash":"dev"}]'
optimize_level:
description: "Compiler optimization level"
required: true
type: choice
options: [O2, O3]
default: O2
clean_build:
description: 'Clean build (no ccache)'
type: boolean
default: false
android12-5_10_susfs_branch_or_commit:
description: 'Enter SusFS Branch or commit hash for android12-5.10'
type: string
default: ''
android13-5_15_susfs_branch_or_commit:
description: 'Enter SusFS Branch or commit hash for android13-5.15'
type: string
default: ''
android14-6_1_susfs_branch_or_commit:
description: 'Enter SusFS Branch or commit hash for android14-6.1'
type: string
default: ''
android15-6_6_susfs_branch_or_commit:
description: 'Enter SusFS Branch or commit hash for android15-6.6'
type: string
default: ''
android16-6_12_susfs_branch_or_commit:
description: 'Enter SusFS Branch or commit hash for android16-6.12'
type: string
default: ''
jobs:
set-op-model:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
device_count: ${{ steps.set-matrix.outputs.count }}
active_gki_keys: ${{ steps.set-matrix.outputs.active_gki_keys }}
susfs_hash_android12_5_10: ${{ steps.set-matrix.outputs.susfs_hash_android12_5_10 }}
susfs_hash_android13_5_15: ${{ steps.set-matrix.outputs.susfs_hash_android13_5_15 }}
susfs_hash_android14_6_1: ${{ steps.set-matrix.outputs.susfs_hash_android14_6_1 }}
susfs_hash_android15_6_6: ${{ steps.set-matrix.outputs.susfs_hash_android15_6_6 }}
susfs_hash_android16_6_12: ${{ steps.set-matrix.outputs.susfs_hash_android16_6_12 }}
ksu_resolved_hash: ${{ steps.set-matrix.outputs.ksu_resolved_hash }}
ksu_options_normalized: ${{ steps.set-matrix.outputs.ksu_options_normalized }}
susfs_base_version: ${{ steps.set-matrix.outputs.susfs_base_version }}
env:
GH_TOKEN: ${{ github.token }}
GH_HTTP_TIMEOUT: 600
steps:
- name: 📥 Checkout Code (to access configs/)
uses: actions/checkout@v6
with:
sparse-checkout: |
configs/
sparse-checkout-cone-mode: false
- name: 🔍 Generate build matrix
id: set-matrix
shell: bash
run: |
set -euo pipefail
echo "::group::Matrix generation"
input="${{ github.event.inputs.op_model }}"
ksu_options_raw='${{ github.event.inputs.ksu_options }}'
if ! ksu_options_normalized=$(echo "$ksu_options_raw" | jq -c 'map(if .type then .type |= ascii_upcase | if .hash == null then if .type == "KSUN" then .hash = "dev" elif .type == "KSU" then .hash = "main" else .hash end else . end else error("No type found") end)' 2>&1); then
echo "::error::ksu_options validation failed: $ksu_options_normalized"
exit 1
fi
echo "ksu_options_normalized=$ksu_options_normalized" >> $GITHUB_OUTPUT
echo "[" > matrix.json
mapfile -t all_json_files < <(find configs/ -name "*.json" -print0 | xargs -0 -n1)
for i in "${!all_json_files[@]}"; do
file="${all_json_files[$i]}"
if [ -f "$file" ]; then
jq -r '.' "$file" >> matrix.json
if [ $((i+1)) -lt ${#all_json_files[@]} ]; then
echo "," >> matrix.json
fi
fi
done
echo "]" >> matrix.json
jq_filter="."
case "$input" in
OOS14+15+16)
;;
OOS15+16)
jq_filter="map(select(.os_version == \"OOS15\" or .os_version == \"OOS16\"))"
;;
OOS14+15)
jq_filter="map(select(.os_version == \"OOS14\" or .os_version == \"OOS15\"))"
;;
OOS16)
jq_filter="map(select(.os_version == \"OOS16\"))"
;;
OOS15)
jq_filter="map(select(.os_version == \"OOS15\"))"
;;
OOS14)
jq_filter="map(select(.os_version == \"OOS14\"))"
;;
android*-*.*)
# Extract android version and kernel version
IFS='-' read -r av kv <<< "$input"
# Build android*-* only for OOS15 and OOS16
jq_filter="map(select(.os_version == \"OOS15\" or .os_version == \"OOS16\")) | map(select(.android_version == \"$av\" and .kernel_version == \"$kv\"))"
echo "ℹ️ Android-Kernel filter applied: $av-$kv"
echo " Restricted to: OOS15 and OOS16 only"
;;
*)
echo "::warning::Unknown input '$input'. Using empty filter."
jq_filter="map(select(false))"
;;
esac
filtered=$(jq -c "$jq_filter" matrix.json)
count=$(jq 'length' <<<"$filtered")
if [ "$count" -eq 0 ]; then
echo "::error::No config files found for input '$input' after applying filters!"
echo ""
echo "Available configurations:"
jq -r '.[] | " - \(.model) (\(.os_version), \(.android_version)-\(.kernel_version))"' matrix.json
exit 1
fi
echo "$filtered" | jq '.' > matrix.json
# For each device + each ksu option → one combined entry
merged_matrix=$(jq -n --argjson devices "$filtered" --argjson ksu_list "$ksu_options_normalized" '[ $devices[] as $dev | $ksu_list[] as $ksu | ($dev + {ksu_type: $ksu.type, ksu_hash: $ksu.hash}) ]')
final_count=$(echo "$merged_matrix" | jq 'length')
echo "✅ Found $final_count device(s) to build"
echo ""
echo "Selected devices:"
jq -r '.[] | " - \(.model) (\(.os_version), \(.android_version)-\(.kernel_version), \(.ksu_type) - \(.ksu_hash))"' <<<"$merged_matrix"
echo "count=$count" >> "$GITHUB_OUTPUT"
ksu_type=$(echo "$ksu_options_normalized" | jq -r '.[0].type')
ksu_ref=$(echo "$ksu_options_normalized" | jq -r '.[0].hash')
if [ "$ksu_type" == "KSUN" ]; then
KSU_REPO_OWNER="KernelSU-Next"
KSU_REPO_NAME="KernelSU-Next"
else
KSU_REPO_OWNER="tiann"
KSU_REPO_NAME="KernelSU"
fi
HEAD_REF="refs/heads/${ksu_ref}"
TAG_REF="refs/tags/${ksu_ref}"
QUERY='query($owner: String!, $name: String!, $headRef: String!, $tagRef: String!, $objRef: String!) {
repository(owner: $owner, name: $name) {
hb: ref(qualifiedName: $headRef) { t: target { ... on Commit { o: oid } } }
ht: ref(qualifiedName: $tagRef) { t: target { ... on Commit { o: oid } } }
ho: object(expression: $objRef) { ... on Commit { o: oid } }
}
}'
MAX_RETRIES=3
RETRY_COUNT=0
RETRY_DELAY=5 # Seconds to wait between retries
echo "Resolving $ksu_type hash ($ksu_ref)..."
until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do
RESULT=$(gh api graphql -f query="$QUERY" -f owner="$KSU_REPO_OWNER" -f name="$KSU_REPO_NAME" -f headRef="$HEAD_REF" -f tagRef="$TAG_REF" -f objRef="$ksu_ref" 2>/dev/null)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] && [ ! -z "$RESULT" ]; then
echo " ✅ API Success"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "::warning::API failed (Attempt $RETRY_COUNT/$MAX_RETRIES). Retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "::error::GitHub API unreachable. Cannot validate $ksu_ref"
exit 1
fi
resolved_sha=$(echo "$RESULT" | jq -r '.data.repository | (.hb.t.o // .ht.t.o // .ho.o // "unknown")')
if [ "$resolved_sha" == "unknown" ]; then
echo "::error::Ref/Hash '$ksu_ref' does not exist in $KSU_REPO_OWNER/$KSU_REPO_NAME"
exit 1
fi
echo " ✅ Resolved: $ksu_type/$ksu_ref → $resolved_sha"
echo "ksu_resolved_hash=$resolved_sha" >> "$GITHUB_OUTPUT"
# Inject ksu_resolved_hash into each device in the matrix
merged_matrix=$(echo "$merged_matrix" | jq --arg resolved_sha "$resolved_sha" 'map(.ksu_resolved_hash = $resolved_sha)')
# SUSFS hash fetch
SUSFS_REPO="https://gitlab.com/simonpunk/susfs4ksu.git"
PROJECT_ID="simonpunk%2fsusfs4ksu"
declare -A default_branches=(
["android12-5.10"]="gki-android12-5.10"
["android13-5.15"]="gki-android13-5.15"
["android14-6.1"]="gki-android14-6.1"
["android15-6.6"]="gki-android15-6.6"
["android16-6.12"]="gki-android16-6.12"
)
declare -A user_inputs=(
["android12-5.10"]="${{ inputs.android12-5_10_susfs_branch_or_commit }}"
["android13-5.15"]="${{ inputs.android13-5_15_susfs_branch_or_commit }}"
["android14-6.1"]="${{ inputs.android14-6_1_susfs_branch_or_commit }}"
["android15-6.6"]="${{ inputs.android15-6_6_susfs_branch_or_commit }}"
["android16-6.12"]="${{ inputs.android16-6_12_susfs_branch_or_commit }}"
)
mapfile -t active_keys < <(
echo "$merged_matrix" \
| jq -r '.[] | select(.susfs == true) | "\(.android_version)-\(.kernel_version)"' \
| sort -u
)
echo "🔍 Resolving SUSFS hashes for: ${active_keys[*]:-none}"
declare -A susfs_hashes
for key in "${active_keys[@]}"; do
default_val="${default_branches[$key]}"
user_val="${user_inputs[$key]:-$default_val}"
MAX_RETRIES=3
RETRY_COUNT=0
RETRY_DELAY=5
resolved=""
until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do
# We query the commit endpoint which accepts Branch, Tag, or SHA
RESULT=$(curl -s --fail "https://gitlab.com/api/v4/projects/${PROJECT_ID}/repository/commits/${user_val}")
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] && [ -n "$RESULT" ]; then
resolved=$(echo "$RESULT" | jq -r '.id')
echo " ✅ Query for $key input field Success"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "::warning::GitLab API failed (Attempt $RETRY_COUNT/$MAX_RETRIES). Retrying..."
sleep $RETRY_DELAY
done
# Final Validation
if [ -z "$resolved" ] || [ "$resolved" = "null" ]; then
echo "::error::Could not resolve SUSFS ref '$user_val' for $key on GitLab."
exit 1
fi
susfs_hashes["$key"]="$resolved"
echo " ✅ Resolved: $key → $resolved"
done
# Write CSV list of active GKI versions
active_gki_keys_csv=$(IFS=','; echo "${active_keys[*]:-}")
echo "active_gki_keys=$active_gki_keys_csv" >> "$GITHUB_OUTPUT"
# Write hash to GITHUB_OUTPUT
echo "susfs_hash_android12_5_10=${susfs_hashes[android12-5.10]:-unknown}" >> "$GITHUB_OUTPUT"
echo "susfs_hash_android13_5_15=${susfs_hashes[android13-5.15]:-unknown}" >> "$GITHUB_OUTPUT"
echo "susfs_hash_android14_6_1=${susfs_hashes[android14-6.1]:-unknown}" >> "$GITHUB_OUTPUT"
echo "susfs_hash_android15_6_6=${susfs_hashes[android15-6.6]:-unknown}" >> "$GITHUB_OUTPUT"
echo "susfs_hash_android16_6_12=${susfs_hashes[android16-6.12]:-unknown}" >> "$GITHUB_OUTPUT"
# Fetch SUSFS_VERSION
susfs_base_version="unknown"
if [ ${#active_keys[@]} -gt 0 ]; then
first_key="${active_keys[0]}"
first_user_val="${user_inputs[$first_key]:-}"
first_ref="${first_user_val:-${default_branches[$first_key]:-gki-android15-6.6}}"
echo "🔍 Fetching SUSFS_VERSION from susfs.h (ref: $first_ref)..."
MAX_RETRIES=3
RETRY_COUNT=0
RETRY_DELAY=5
until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do
RAW_CONTENT=$(curl -s --fail \
"https://gitlab.com/api/v4/projects/${PROJECT_ID}/repository/files/kernel_patches%2Finclude%2Flinux%2Fsusfs.h/raw?ref=${first_ref}")
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] && [ -n "$RAW_CONTENT" ]; then
extracted=$(echo "$RAW_CONTENT" | grep '#define SUSFS_VERSION' | awk -F'"' '{print $2}')
if [ -n "$extracted" ]; then
susfs_base_version="$extracted"
echo " ✅ SUSFS_VERSION = $susfs_base_version"
break
fi
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "::warning::Failed to fetch susfs.h (Attempt $RETRY_COUNT/$MAX_RETRIES). Retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
done
if [ "$susfs_base_version" = "unknown" ]; then
echo "::warning::Could not extract SUSFS_VERSION from susfs.h. SUSFS_BASE_VERSION will be 'unknown'."
fi
# Warn if other GKI keys use different manual refs than the first one (possible version inconsistency)
if [ ${#active_keys[@]} -gt 1 ]; then
for key in "${active_keys[@]:1}"; do
other_val="${user_inputs[$key]:-}"
if [ -n "$other_val" ] && [ "$other_val" != "$first_ref" ]; then
echo "::warning::GKI key '$key' uses ref '$other_val', different from '$first_ref' used to extract SUSFS_VERSION. Verify that the versions in susfs.h match."
fi
done
fi
else
echo "ℹ️ No active SUSFS keys — skipping SUSFS_VERSION fetch."
fi
echo "susfs_base_version=$susfs_base_version" >> "$GITHUB_OUTPUT"
# Inject susfs_resolved_hash into each device in the matrix
susfs_hashes_json=$(jq -n \
--arg a12 "${susfs_hashes[android12-5.10]:-unknown}" \
--arg a13 "${susfs_hashes[android13-5.15]:-unknown}" \
--arg a14 "${susfs_hashes[android14-6.1]:-unknown}" \
--arg a15 "${susfs_hashes[android15-6.6]:-unknown}" \
--arg a16 "${susfs_hashes[android16-6.12]:-unknown}" \
'{
"android12-5.10": $a12,
"android13-5.15": $a13,
"android14-6.1": $a14,
"android15-6.6": $a15,
"android16-6.12": $a16
}')
merged_matrix=$(echo "$merged_matrix" | jq \
--argjson hashes "$susfs_hashes_json" \
'[.[] | . + {susfs_resolved_hash: ($hashes["\(.android_version)-\(.kernel_version)"] // "unknown")}]')
# Recalculate wrapped with updated matrix and write output
wrapped=$(jq -n --argjson items "$merged_matrix" '{ include: $items }')
echo "matrix=$(jq -c . <<< "$wrapped")" >> "$GITHUB_OUTPUT"
echo "::endgroup::"
- name: Upload build matrix
uses: actions/upload-artifact@v7
with:
name: build-matrix
path: matrix.json
archive: false
retention-days: 7
- name: 📊 Build plan summary
run: |
ksu_type="${{ fromJSON(steps.set-matrix.outputs.ksu_options_normalized)[0].type }}"
ksu_ref="${{ fromJSON(steps.set-matrix.outputs.ksu_options_normalized)[0].hash }}"
ksu_display=""
if [ "$ksu_type" = "KSUN" ] || [ "$ksu_type" = "KSU" ]; then
if [[ "$ksu_ref" =~ ^[0-9a-f]{40}$ ]]; then
ksu_display+="📌 \`$ksu_type\`, \`$ksu_ref\`"
else
ksu_display+="🔀 \`$ksu_type\`, \`$ksu_ref\` (\`${{ steps.set-matrix.outputs.ksu_resolved_hash }}\`)"
fi
fi
{
cat << 'EOF'
## 🎯 Build Plan
**Target:** ${{ inputs.op_model }}
**Devices:** ${{ steps.set-matrix.outputs.count }}
**Configuration:**
EOF
echo "- KSU Config: $ksu_display"
cat << 'EOF'
- Optimization: ${{ inputs.optimize_level }}
- Clean Build/No Ccache: ${{ inputs.clean_build && '✅ Yes' || '❌ No' }}
- Create Release: ${{ inputs.make_release && '✅ Yes' || '❌ No' }}
**SUSFS Configuration:**
EOF
} >> "$GITHUB_STEP_SUMMARY"
echo "- **Version:** 📦 \`${{ steps.set-matrix.outputs.susfs_base_version }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Display SUSFS config for each kernel version
declare -A susfs_inputs=(
["android12-5.10"]="${{ inputs.android12-5_10_susfs_branch_or_commit }}"
["android13-5.15"]="${{ inputs.android13-5_15_susfs_branch_or_commit }}"
["android14-6.1"]="${{ inputs.android14-6_1_susfs_branch_or_commit }}"
["android15-6.6"]="${{ inputs.android15-6_6_susfs_branch_or_commit }}"
["android16-6.12"]="${{ inputs.android16-6_12_susfs_branch_or_commit }}"
)
declare -A step_hashes=(
["android12-5.10"]="${{ steps.set-matrix.outputs.susfs_hash_android12_5_10 }}"
["android13-5.15"]="${{ steps.set-matrix.outputs.susfs_hash_android13_5_15 }}"
["android14-6.1"]="${{ steps.set-matrix.outputs.susfs_hash_android14_6_1 }}"
["android15-6.6"]="${{ steps.set-matrix.outputs.susfs_hash_android15_6_6 }}"
["android16-6.12"]="${{ steps.set-matrix.outputs.susfs_hash_android16_6_12 }}"
)
# Iterate only on the GKI versions present in this run
IFS=',' read -ra active_keys_display \
<<< "${{ steps.set-matrix.outputs.active_gki_keys }}"
for key in "${active_keys_display[@]}"; do
value="${susfs_inputs[$key]:-}"
fetched_hash="${step_hashes[$key]:-unknown}"
if [ -z "$value" ]; then
# Auto: no input > hash resolved by ls-remote
echo "- $key: 🔄 auto (\`$fetched_hash\`)" >> $GITHUB_STEP_SUMMARY
elif [[ "$value" =~ ^[0-9a-f]{40}$ ]]; then
# Manual 40-char hash > direct passthrough
echo "- $key: 📌 \`$value\`" >> $GITHUB_STEP_SUMMARY
else
# Branch name > show branch + resolved hash
echo "- $key: 🔀 \`$value\` (\`$fetched_hash\`)" >> $GITHUB_STEP_SUMMARY
fi
done
echo "" >> $GITHUB_STEP_SUMMARY
echo "> **💡 Note:** Hashes are resolved at run time via API calls before builds start." >> $GITHUB_STEP_SUMMARY
# Add OOS restriction note for android-kernel filters
if [[ "${{ inputs.op_model }}" == android*-*.* ]]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "> **⚠️ Android-Kernel Filter:** Only OOS15 and OOS16 devices will be built for \`${{ inputs.op_model }}\`" >> $GITHUB_STEP_SUMMARY
fi
build:
name: build (${{ matrix.model }}, ${{ matrix.soc }}, ${{ matrix.branch }}, ${{ matrix.manifest }}, ${{ matrix.android_version }}, ${{ matrix.kernel_version }}, ${{ matrix.os_version }}, ${{ matrix.ksu_type }})
needs: set-op-model
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.set-op-model.outputs.matrix) }}
outputs:
ksun_ver: ${{ steps.build-stat.outputs.ksu_version }}
ksu_ver: ${{ steps.build-stat.outputs.ksu_version }}
steps:
- name: 🧹 Emergency Disk Cleanup
if: ${{ matrix.disk_cleanup }}
run: |
echo "::group::Disk Usage Before Cleanup"
df -h
echo "::endgroup::"
echo "::group::Removing Unnecessary Software"
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo apt-get clean
if command -v docker >/dev/null 2>&1; then
docker rmi $(docker images -q) 2>/dev/null || true
fi
echo "::endgroup::"
echo "::group::Disk Usage After Cleanup"
df -h
AVAIL=$(df -h / | awk 'NR==2 {print $4}')
echo "✅ Available space: $AVAIL"
echo "::endgroup::"
- name: Download Apache Arrow's util_free_space.sh
if: ${{ matrix.disk_cleanup }}
run: |
curl -L -o util_free_space.sh https://raw.githubusercontent.com/apache/arrow/main/ci/scripts/util_free_space.sh
chmod +x util_free_space.sh
./util_free_space.sh
- name: 📥 Checkout Code
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Install Minimal Dependencies
run: |
set -euo pipefail
echo "::group::Install dependencies"
sudo apt-get -o Acquire::Retries=3 update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
git curl ca-certificates build-essential clang lld flex bison \
libelf-dev libssl-dev libncurses-dev zlib1g-dev liblz4-tool \
libxml2-utils rsync unzip dwarves file python3 ccache jq bc dos2unix kmod libdw-dev elfutils
sudo apt-get clean
echo "✅ Dependencies installed"
echo "::endgroup::"
- name: 📦 Disk usage (pre-build)
run: |
echo "::group::Disk usage pre-build"
df -h /
du -sh "$GITHUB_WORKSPACE" 2>/dev/null || true
sudo rm -rf /tmp/* || true
echo "::endgroup::"
- name: ♻️ Configure ccache (bounded)
if: ${{ inputs.clean_build != true }}
run: |
if command -v ccache >/dev/null 2>&1; then
echo "::group::ccache configuration"
ccache -o max_size=1.0G
ccache -o compression=true
ccache -o compression_level=3
ccache -s
echo "::endgroup::"
fi
- name: 🧹 Prepare op_config_json (without KSU fields)
id: prepare_config
shell: bash
run: |
echo "config_json=$(jq -nc --argjson m '${{ toJSON(matrix) }}' '$m | del(.ksu_type, .ksu_hash, .ksu_resolved_hash)')" >> "$GITHUB_OUTPUT"
- name: 🔨 Build Kernel
id: build
uses: ./.github/actions
with:
op_config_json: ${{ steps.prepare_config.outputs.config_json }}
ksu_type: ${{ matrix.ksu_type }}
ksu_branch_or_hash: ${{ matrix.ksu_resolved_hash }}
susfs_commit_hash_or_branch: ${{ matrix.susfs_resolved_hash }}
optimize_level: ${{ inputs.optimize_level }}
clean: ${{ inputs.clean_build }}
- name: 📊 Build statistics
id: build-stat
if: always()
run: |
echo "::group::Build Statistics"
echo "Device: ${{ matrix.model }}"
echo "OS Version: ${{ matrix.os_version }}"
echo "Kernel: ${{ matrix.android_version }}-${{ matrix.kernel_version }}"
if [ "${{ matrix.susfs }}" = true ]; then
echo "SUSFS Hash: ${{ matrix.susfs_resolved_hash }}"
fi
echo "Status: ${{ job.status }}"
if [ "${{ steps.build.outcome }}" = "success" ]; then
echo ""
echo "✅ Build completed successfully"
echo ""
echo "Outputs:"
echo " - Kernel: ${{ steps.build.outputs.kernel_version }}"
if [ "${{ matrix.ksu_type }}" = "KSUN" ]; then
echo " - KSU Next: v${{ steps.build.outputs.ksu_version }}"
echo "ksun_ver=${{ steps.build.outputs.ksu_version }}" >> "$GITHUB_OUTPUT"
echo "ksu_ver=" >> "$GITHUB_OUTPUT"
else
echo " - KSU: v${{ steps.build.outputs.ksu_version }}"
echo "ksun_ver=" >> "$GITHUB_OUTPUT"
echo "ksu_ver=${{ steps.build.outputs.ksu_version }}" >> "$GITHUB_OUTPUT"
fi
if [ "${{ matrix.susfs }}" = true ]; then
echo " - SUSFS: ${{ steps.build.outputs.susfs_version }}"
fi
echo " - Build time: ${{ steps.build.outputs.build_time }}s"
if [ "${{ inputs.clean_build }}" != "true" ]; then
echo " - ccache hit rate: ${{ steps.build.outputs.ccache_hit_rate }}"
echo " - ccache direct rate: ${{ steps.build.outputs.ccache_direct_rate }}"
else
echo " - ccache: disabled (clean build)"
fi
if [ -n "${{ steps.build.outputs.warnings }}" ]; then
echo " - Warnings: ${{ steps.build.outputs.warnings }}"
fi
else
echo "❌ Build failed"
fi
echo "::endgroup::"
- name: 📝 Job summary
if: always()
run: |
cat >> $GITHUB_STEP_SUMMARY << EOF
### ${{ matrix.model }} (${{ matrix.os_version }}) - ${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}
**Kernel:** ${{ matrix.android_version }}-${{ matrix.kernel_version }}
EOF
if [ "${{ matrix.susfs }}" = true ]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
**SUSFS Hash:** \`${{ matrix.susfs_resolved_hash }}\`
EOF
fi
if [ "${{ steps.build.outcome }}" = "success" ]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
| Metric | Value |
|--------|-------|
| **Kernel** | ${{ steps.build.outputs.kernel_version }} |
EOF
if [ "${{ matrix.ksu_type }}" = "KSUN" ]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
| **KSU Next** | v${{ steps.build.outputs.ksu_version }} |
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
| **KSU** | v${{ steps.build.outputs.ksu_version }} |
EOF
fi
if [ "${{ matrix.susfs }}" = true ]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
| **SUSFS** | ${{ steps.build.outputs.susfs_version }} |
EOF
fi
cat >> $GITHUB_STEP_SUMMARY << EOF
| **Build Time** | ${{ steps.build.outputs.build_time }}s |
EOF
if [ "${{ inputs.clean_build }}" != "true" ]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
| **ccache Hit Rate** | ${{ steps.build.outputs.ccache_hit_rate }} |
| **ccache Direct Rate** | ${{ steps.build.outputs.ccache_direct_rate }} |
EOF
fi
if [ -n "${{ steps.build.outputs.warnings }}" ]; then
echo "| **Warnings** | ${{ steps.build.outputs.warnings }} |" >> $GITHUB_STEP_SUMMARY
fi
cat >> $GITHUB_STEP_SUMMARY << EOF
**SHA256:** \`${{ steps.build.outputs.image_sha256 }}\`
EOF
fi
- name: 🧹 Final cleanup and space report
if: always()
run: |
echo "::group::Cleanup"
# Remove build artifacts but PRESERVE ccache
sudo rm -rf "$GITHUB_WORKSPACE/out" || true
sudo rm -rf "$GITHUB_WORKSPACE/build" || true
sudo rm -rf "$GITHUB_WORKSPACE/kernel/out" || true
sudo rm -rf "$GITHUB_WORKSPACE/.repo" || true
sudo rm -rf /tmp/* || true
# Show ccache stats (don't clear it!)
if command -v ccache >/dev/null 2>&1; then
echo ""
echo "📊 ccache statistics after build:"
ccache -s
echo ""
echo "💾 ccache preserved for next build"
fi
echo ""
echo "💽 Final disk usage:"
df -h /
echo "::endgroup::"
trigger-release:
needs: [set-op-model, build]
runs-on: ubuntu-latest
if: ${{ inputs.make_release }}
env:
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SUSFS_BASE_VERSION: ${{ needs.set-op-model.outputs.susfs_base_version }}
RELEASE_NAME: '*TEST BUILD* OnePlus Kernels With KernelSU Next & SUSFS ${{ needs.set-op-model.outputs.susfs_base_version }} *TEST BUILD*'
steps:
- name: 📥 Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: 🏷️ Generate and Create New Tag
run: |
# Only search for tags starting with this SUSFS version
LATEST_MATCHING_TAG=$(gh api repos/$REPO_OWNER/$REPO_NAME/tags \
--jq "[.[] | select(.name | startswith(\"${SUSFS_BASE_VERSION}-r\"))] \
| sort_by(.name | split(\"-r\")[1] | tonumber) \
| last | .name // empty")
if [ -z "$LATEST_MATCHING_TAG" ]; then
# No existing release for this version SUSFS → first release
NEW_TAG="${SUSFS_BASE_VERSION}-r1"
else
# Only increment the -r* suffix of the latest release for this version
NEW_TAG=$(echo "$LATEST_MATCHING_TAG" | awk -F'-r' '{printf "%s-r%d", $1, $2+1}')
fi
echo "New tag: $NEW_TAG"
echo "NEW_TAG=${NEW_TAG}" >> $GITHUB_ENV
git tag $NEW_TAG
git push origin $NEW_TAG
- name: 📥 Download Artifacts
uses: actions/download-artifact@v8
with:
path: ./downloaded-artifacts
merge-multiple: true
skip-decompress: true
- name: 📝 Generate Device List and Final Release Notes
id: generate-notes
run: |
echo "=== Start building the release notes ==="
# Collect build metadata
declare -A device_info
JSON_BUILD_DATA=$(jq -c '.' ./downloaded-artifacts/matrix.json)
for file in $(find downloaded-artifacts -maxdepth 1 -name "*.zip" -type f | sort); do
if [ -f "$file" ]; then
zipname=$(basename "$file" .zip)
rest="${zipname#AK3_}"
IFS='_' read -ra parts <<< "$rest"
model="${parts[0]}"
os_version="${parts[1]}"
kernel_version="${parts[2]}"
ksu_type="${parts[3]}"
ksu_ver="${parts[4]}"
if [ "${parts[5]:-}" = "SuSFS" ]; then
susfs_ver="${parts[6]:-unknown}"
else
susfs_ver="none"
fi
full_model="${model}_${os_version}"
device_info["$full_model"]="$model|$os_version|$kernel_version|$ksu_type|$ksu_ver|$susfs_ver"
fi
done
ksu_type="${{ fromJSON(needs.set-op-model.outputs.ksu_options_normalized)[0].type }}"
ksu_ref="${{ fromJSON(needs.set-op-model.outputs.ksu_options_normalized)[0].hash }}"
ksu_resolved_hash="${{ needs.set-op-model.outputs.ksu_resolved_hash }}"
OPTIMIZE_LEVEL="${{ inputs.optimize_level }}"
CLEAN_BUILD="${{ inputs.clean_build }}"
# Hash map for GKI version
declare -A susfs_map=(
["android12-5.10"]="${{ needs.set-op-model.outputs.susfs_hash_android12_5_10 }}"
["android13-5.15"]="${{ needs.set-op-model.outputs.susfs_hash_android13_5_15 }}"
["android14-6.1"]="${{ needs.set-op-model.outputs.susfs_hash_android14_6_1 }}"
["android15-6.6"]="${{ needs.set-op-model.outputs.susfs_hash_android15_6_6 }}"
["android16-6.12"]="${{ needs.set-op-model.outputs.susfs_hash_android16_6_12 }}"
)
# List of GKI versions present in this run
IFS=',' read -ra active_gki_keys \
<<< "${{ needs.set-op-model.outputs.active_gki_keys }}"
cat << EOF > release_notes.md
# 🎯 OnePlus Kernels with KernelSU Next & SUSFS $SUSFS_BASE_VERSION
> **Build Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')
> **Build ID:** \`${{ github.run_id }}\`
> **Workflow:** [\`${{ github.workflow }}\`](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
---
## 📦 Built Devices (${#device_info[@]} total)
| Model | OS Version | Kernel Version | Features |
|-------|------------|----------------|----------|
EOF
for full_key in $(printf '%s\n' "${!device_info[@]}" | sort); do
IFS='|' read -r model os_ver kernel_ver _ _ _ <<< "${device_info[$full_key]}"
# Use jq to extract feature flags for this model from the JSON matrix
feature_flags=$(jq -r --arg MODEL "$model" '
.[] |
select(.model == $MODEL) |
"\(.hmbird // false)\t\(.susfs // false)\t\(.bbr // false)\t\(.bbg // false)\t\(.ttl // false)\t\(.ip_set // false)\t\(.unicode // false)"
' <<< "$JSON_BUILD_DATA")
features=""
# If no match found, skip or handle error
if [[ -z "$feature_flags" ]]; then
echo "Warning: No JSON entry found for model: $model" >&2
else
IFS=$'\t' read -r hmbird susfs bbr bbg ttl ip_set unicode <<< "$feature_flags"
# Build features string based on boolean values
[[ "$hmbird" == "true" ]] && features+="🐦 HMBIRD "
[[ "$susfs" == "true" ]] && features+="ඞ SusFS "
[[ "$bbr" == "true" ]] && features+="🚀 BBR "
[[ "$bbg" == "true" ]] && features+="🛡️ BBG "
[[ "$ttl" == "true" ]] && features+="🌐 TTL "
[[ "$ip_set" == "true" ]] && features+="🧱 IP_SET & IPv6 NAT"
[[ "$unicode" == "true" ]] && features+="🔧 Unicode Fix"
fi
printf "| %-13s | %-10s | %-16s | %-30s |\n" \
"$model" "$os_ver" "$kernel_ver" "$features" >> release_notes.md
done
if [ ${#device_info[@]} -gt 0 ]; then
first_entry=$(printf '%s\n' "${!device_info[@]}" | sort | head -n1)
IFS='|' read -r _ _ _ _ ksu_ver_global _ <<< "${device_info[$first_entry]}"
else
echo "::error::No device info found!" >&2
exit 1
fi
cat << EOF >> release_notes.md
---
## 🔧 Build Configuration
| Component | Version/Setting |
|-----------|----------------|
EOF
# Display function: hash → direct hash, branch → branch (hash)
ksu_display_value() {
local input="$1"
local resolved="$2"
if [[ "$input" =~ ^[0-9a-f]{40}$ ]]; then
echo "$input"
else
echo "$input ($resolved)"
fi
}
ksu_display=$(ksu_display_value "$ksu_ref" "$ksu_resolved_hash")
if [ "$ksu_type" == "KSUN" ]; then
cat << EOF >> release_notes.md
| **KernelSU Next Branch** | \`$ksu_display\` |
| **KernelSU Next Version** | \`$ksu_ver_global\` |
EOF
elif [ "$ksu_type" == "KSU" ]; then
cat << EOF >> release_notes.md
| **KernelSU Branch** | \`$ksu_display\` |
| **KernelSU Version** | \`$ksu_ver_global\` |
EOF
else
cat << EOF >> release_notes.md
| **Unknown KernelSU Fork Branch** | \`\` |
| **Unknown KernelSU Fork Version** | \`\` |
EOF
fi
cat << EOF >> release_notes.md
| **SUSFS Version** | \`$SUSFS_BASE_VERSION\` |
| **Optimization Level** | \`$OPTIMIZE_LEVEL\` |
| **Clean Build** | $( [ "$CLEAN_BUILD" = "true" ] && echo "✅ Yes (no ccache)" || echo "❌ No (ccache enabled)" ) |
| **Compiler** | Clang (version varies by device) |
### 📌 SUSFS Branch Mapping
| Kernel Version | SUSFS Commit |
|----------------|--------------|
$(for key in "${active_gki_keys[@]}"; do
echo "| $key | \`${susfs_map[$key]:-unknown}\` |"
done)
---
## ✨ Features & Capabilities
### 🔐 Root Management
EOF
if [ "$ksu_type" == "KSUN" ]; then
cat << EOF >> release_notes.md
- **KernelSU Next** - Next-generation kernel-level root solution
EOF
elif [ "$ksu_type" == "KSU" ]; then
cat << EOF >> release_notes.md
- **KernelSU** - Kernel-level root solution
EOF
fi
cat << EOF >> release_notes.md
- **SUSFS $SUSFS_BASE_VERSION** - Advanced hiding and security features
EOF
if [ "$ksu_type" == "KSUN" ] && [ "${ksu_ver_global:-0}" -lt 12884 ]; then
cat << EOF >> release_notes.md
- **Magic Mount Support** - Seamless file system modifications
EOF
fi
if [ "${ksu_ver_global:-0}" -lt 12884 ]; then
cat << EOF >> release_notes.md
- **Manual Hooks** - scope_min_manual_hooks_v1.4 for better compatibility
EOF
fi
cat << EOF >> release_notes.md
### 🛡️ Security & Privacy
- **Baseband Guard (BBG)** - LSM-based baseband security
- **SUSFS Hide Features**:
- ✅ SUS_PATH - Hide suspicious paths
- ✅ SUS_MOUNT - Hide mount points (No Cli Support)
- ✅ SUS_KSTAT - Spoof kernel statistics
- ✅ SPOOF_UNAME - Kernel version spoofing
- ✅ SPOOF_CMDLINE - Boot parameters spoofing
- ✅ OPEN_REDIRECT - File access redirection
- ✅ SUS_MAP - Memory mapping protection
- ✅ AVC_SPOOF - Spoof Procfs avc denial logs
- **Ptrace Leak Fix** - For kernels < 5.16
- **Unicode Fix** - Prevent path traversal and other detections using non-printable Unicode codepoints [Experimental]
### 🚀 Performance & Networking
- **BBRv1** - Improved TCP congestion control
- **Wireguard** - Built-in VPN support
- **IP Set & IPv6 NAT Support** - Advanced firewall capabilities and IPv6 NAT Support
- **TTL Target Support** - Network packet manipulation
- **LTO (Link Time Optimization)** - Optimized binary size and performance
- **ccache-accelerated builds** - Faster compilation times
- **Optimisation Patches** - Memory, I/O, CPU scheduler, network and other general tunings
### 🔧 System Features
- **TMPFS_XATTR** - Extended attributes for tmpfs (Mountify support)
- **TMPFS_POSIX_ACL** - POSIX ACLs for tmpfs
- **HMBIRD SCX** - Scheduler extensions for all SM8750/MT6991 devices
---
## 📱 Manager Applications
### Official Manager
EOF
if [ "$ksu_type" == "KSUN" ]; then
cat << EOF >> release_notes.md
- **KernelSU Next Manager**
→ [GitHub Release](https://github.com/KernelSU-Next/KernelSU-Next/releases)
EOF
elif [ "$ksu_type" == "KSU" ]; then
cat << EOF >> release_notes.md
- **KernelSU Manager**
→ [GitHub Release](https://github.com/tiann/KernelSU/releases)
EOF
fi
cat << EOF >> release_notes.md
### Community Managers
- **WildKSU Manager** (Recommended for additional features)
→ [GitHub Release](https://github.com/WildKernels/Wild_KSU/releases)
### Required Module
- **KSU SUSFS Module** (Required for SUSFS features)
→ [GitHub Release](https://github.com/sidex15/ksu_module_susfs/releases)
### Recomended Flasher
- **Kernel Flasher** (Required for flashing AnyKernel3 zips and backups and OTA)
→ [GitHub Release](https://github.com/fatalcoder524/KernelFlasher/releases)
---
## 📥 Installation Instructions
### Prerequisites
- Unlocked bootloader.
- Backup your current boot image.
- Have root access using Magisk / KernelSU / Apatch (Any forks).
### Via Kernel Flasher
1. Download the appropriate ZIP for your device.
2. Flash the ZIP file to active slot using Kernel Flasher.
3. Based on root method, follow the steps:
a. If root access is from Magisk(any forks): Perform complete uninstall from Magisk Manager.
b. If root access is from KSU LKM(any forks): Flash Stock boot/init_boot/vendor_boot based on what you image you patched to get root access or Uninstall LKM. Recommended to flash stock image.
c. If root access is from KSU GKI(any forks): Uninstall Manager. Delete /data/adb/ksud bin file. Reinstall Manager based on AK3 zip version. [Not required to do everytime]
d. If root access is from Apatch(any forks): Delete all files from /data/adb. This is to start fresh and reduce multiple root implementation detected by modules.
4. Reboot system.
5. Install KernelSU-Next/WildSU Manager.
6. Install SusFS module from manager.
---
## 📜 Changelog
### This Release
- Updated SUSFS to $SUSFS_BASE_VERSION.
- Added HMBIRD support for all SM8750/MT6991 devices.
- Improved ccache build system.
- Enhanced SUSFS hiding capabilities.
- Added IP_SET, IPv6 NAT and TTL support.
- Added TMPFS_XATTR and TMPFS_POSIX_ACL support for Mountify.
- Added Ptrace leak fix for kernels < 5.16.
- Compiler optimizations (${{ inputs.optimize_level }}).
- Additional General Optimisations.
- Wild_KSU Manager Support.
- Unicode Bypass Fix
### Previous Releases
See [Releases Page](${{ github.server_url }}/${{ github.repository }}/releases)
---
## 🙏 Credits
- **KernelSU Next Team** - Root solution
- **KernelSU Team** - Root solution
- **simonpunk** - SUSFS development
- **OnePlus** - Kernel source code
- **Community Contributors** - Testing and feedback
---
**⚡ Built with ❤️ by the community**
EOF
echo "--- Final Release Notes ---"
cat release_notes.md
- name: 🚀 Create GitHub Release
run: |
gh release create "${{ env.NEW_TAG }}" \
--repo "${{ env.REPO_OWNER }}/${{ env.REPO_NAME }}" \
--title "${{ env.RELEASE_NAME }}" \
--notes-file release_notes.md \
--draft
- name: 📤 Upload Release Assets Dynamically
run: |
for file in ./downloaded-artifacts/*.zip; do
if [ -f "$file" ]; then
echo "Uploading $file..."
gh release upload "${{ env.NEW_TAG }}" "$file" --clobber
fi
done
- name: 📊 Release summary
if: success()
run: |
cat >> $GITHUB_STEP_SUMMARY << EOF
---
## 🎉 Release Created Successfully
**Tag:** [\`${{ env.NEW_TAG }}\`](${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ env.NEW_TAG }})
**Kernels:** $(find ./downloaded-artifacts -maxdepth 1 -name "*.zip" -type f | wc -l)
### 📦 Assets
EOF
for zip in ./downloaded-artifacts/*.zip; do
if [ -f "$zip" ]; then
name=$(basename "$zip")
size=$(stat -c%s "$zip")
size_mb=$(echo "scale=2; $size / 1024 / 1024" | bc)
echo "- \`$name\` (${size_mb} MB)" >> $GITHUB_STEP_SUMMARY
fi
done