Skip to content

OP15 β€’ RSKSU β€’ auto timestamp #23

OP15 β€’ RSKSU β€’ auto timestamp

OP15 β€’ RSKSU β€’ auto timestamp #23

name: Build and Release OnePlus Kernels
run-name: >-
${{ github.event.inputs.op_target_model != '' && github.event.inputs.op_target_model || github.event.inputs.op_model }}
β€’ RSKSU
β€’ ${{ github.event.inputs.build_timestamp != '' && github.event.inputs.build_timestamp || 'auto timestamp' }}
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:
- A14+15+16
- A15+16
- A14+15
- A16
- A15
- A14
- android16-6.12
- android15-6.6
- android14-6.1
- android13-5.15
- android12-5.10
default: A14+15+16
op_target_model:
description: 'Optional exact/prefix model: OP12, OP13-CPH, OP-NORD-4, OP12-6.1.118. Case-sensitive. Empty = all.'
required: false
type: string
default: ''
ksu_options:
description: 'Enter ReSukiSU build json'
required: true
type: string
default: '[{"type":"rsksu","hash":"main"}]'
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
debug:
description: 'Enable debug mode: dumping debug related artifacts and show some debug logs'
required: false
type: boolean
default: false
enable_adios:
description: 'Enable ADIOS I/O scheduler support (6.1/6.6 kernels only)'
required: false
type: boolean
default: false
enable_hmbird:
description: 'Enable HMBIRD. CPU scheduler modification for processing heavy use tasks (e.g., gaming), but can cause instability. WARNING: ONLY FOR SM8750/MT6991 DEVICES! (OP13, OP13T, OP13S, OP-TURBO-6, OP-PAD-3-SM8750, OP-PAD-2-PRO, OP-PAD-2-MT6991, OP-NORD-6, OP-ACE-6, OP-ACE-5-ULTRA, OP-ACE-5-PRO)'
type: boolean
default: false
enable_ds:
description: 'Enable Droidspace. Support for portable containers running full Linux environments.'
type: boolean
default: false
enable_ntsync:
description: 'Enable NTSync. High-performance, low-latency synchronization primitives compatible with the Windows NT kernel API. Useful for multithreading on Wine/Proton compatiblity layers (gaming emulators like Winlator).'
type: boolean
default: false
enable_bbg:
description: 'Enables Baseband Guard (BBG). Prevents writing to critical device partitions for protection.'
type: boolean
default: false
enable_bbr:
description: 'Enables BBR. TCP congestion algorithm for improving throughput and preventing connection lag on high-latency or lossy networks (like crowded 5G or weak Wi-Fi). But increases CPU overhead.'
type: boolean
default: false
enable_bbr3:
description: 'Enables BBR v3. TCP congestion algorithm for improving throughput and preventing connection lag on high-latency or lossy networks (like crowded 5G or weak Wi-Fi). But increases CPU overhead.'
type: boolean
default: true
enable_ttl:
description: 'Enables TTL. Network packet manipulation. Bypass carrier mobile hotspot/tethering data caps.'
type: boolean
default: true
enable_ipset:
description: 'Enables IPSet. Advanced firewall capabilities.'
type: boolean
default: true
enable_unicode:
description: 'Enable Unicode Bypass Fix. Prevent path traversal and other detections using non-printable Unicode codepoints.'
type: boolean
default: true
enable_susfs:
description: "Enables SuSFS implementation. If no, ignore branch selections below."
required: true
type: boolean
default: true
mirror_toolchains:
description: 'Do you want to sync toolchains before build? [99% runs not required]'
required: false
type: boolean
default: false
build_timestamp:
description: 'Custom kernel build timestamp for uname -a (e.g. "Tue Feb 10 10:01:02 UTC 2026"). Leave empty for current time.'
type: string
default: ''
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 }}"
target_model="${{ github.event.inputs.op_target_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 .hash = "main" 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
A14+15+16)
;;
A15+16)
jq_filter="map(select(.os_version == \"A15\" or .os_version == \"A16\"))"
;;
A14+15)
jq_filter="map(select(.os_version == \"A14\" or .os_version == \"A15\"))"
;;
A16)
jq_filter="map(select(.os_version == \"A16\"))"
;;
A15)
jq_filter="map(select(.os_version == \"A15\"))"
;;
A14)
jq_filter="map(select(.os_version == \"A14\"))"
;;
android*-*.*)
# Extract android version and kernel version
IFS='-' read -r av kv <<< "$input"
# Build android*-* only for A15 and A16
jq_filter="map(select(.os_version == \"A15\" or .os_version == \"A16\")) | map(select(.android_version == \"$av\" and .kernel_version == \"$kv\"))"
echo "ℹ️ Android-Kernel filter applied: $av-$kv"
echo " Restricted to: A15 and A16 only"
;;
*)
echo "::warning::Unknown input '$input'. Using empty filter."
jq_filter="map(select(false))"
;;
esac
filtered=$(jq -c "$jq_filter" matrix.json)
if [ -n "$target_model" ]; then
echo "🎯 Target model filter enabled: $target_model"
echo " Matching exact model OR versioned model prefix."
filtered=$(echo "$filtered" | jq -c --arg target "$target_model" '
map(select(.model == $target or (.model | startswith($target + "-"))))
')
else
echo "ℹ️ No target model selected. Building all configs matching op_model='$input'."
fi
echo "πŸ”„ Overriding configuration features with manual workflow inputs..."
filtered="$(echo "$filtered" | jq -c \
--arg hmbird "${{ github.event.inputs.enable_hmbird }}" \
--arg ds "${{ github.event.inputs.enable_ds }}" \
--arg ntsync "${{ github.event.inputs.enable_ntsync }}" \
--arg bbg "${{ github.event.inputs.enable_bbg }}" \
--arg bbr "${{ github.event.inputs.enable_bbr }}" \
--arg bbr3 "${{ github.event.inputs.enable_bbr3 }}" \
--arg ttl "${{ github.event.inputs.enable_ttl }}" \
--arg ip_set "${{ github.event.inputs.enable_ipset }}" \
--arg unicode "${{ github.event.inputs.enable_unicode }}" \
--arg susfs "${{ github.event.inputs.enable_susfs }}" \
'map(. + {
hmbird: ($hmbird == "true"),
ds: ($ds == "true"),
ntsync: ($ntsync == "true"),
bbg: ($bbg == "true"),
bbr: ($bbr == "true"),
bbr3: ($bbr3 == "true"),
ttl: ($ttl == "true"),
ip_set: ($ip_set == "true"),
unicode: ($unicode == "true"),
susfs: ($susfs == "true")
})'
)"
count=$(jq 'length' <<<"$filtered")
filtered=$(echo "$filtered" | jq '
sort_by(
(.os_version | gsub("A"; "") | tonumber) * -1,
.model
)
')
if [ "$count" -eq 0 ]; then
echo "::error::No config files found for op_model='$input' and op_target_model='${target_model:-<empty>}' 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
echo "$ksu_options_normalized" > ksu_temp.json
# For each device + each ksu option β†’ one combined entry
merged_matrix=$(jq -n \
--slurpfile devices matrix.json \
--slurpfile ksu_list ksu_temp.json \
'[ $devices[0][] as $dev | $ksu_list[0][] as $ksu | ($dev + {ksu_type: $ksu.type, ksu_hash: $ksu.hash}) ]')
rm ksu_temp.json
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')
KSU_REPO_OWNER="ReSukiSU"
KSU_REPO_NAME="ReSukiSU"
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"
GITLAB_PROJECT_PATH="simonpunk/susfs4ksu"
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
declare -A susfs_versions_per_key
for key in "${active_keys[@]}"; do
default_val="${default_branches[$key]}"
user_val="${user_inputs[$key]:-$default_val}"
# Build GraphQL query β€” include blob fetch only when releasing
HASH_QUERY="{ project(fullPath: \"${GITLAB_PROJECT_PATH}\") { repository { ch: commit(ref: \"${user_val}\") { sha } } } }"
RELEASE_QUERY="{ project(fullPath: \"${GITLAB_PROJECT_PATH}\") { repository { ch: commit(ref: \"${user_val}\") { sha } blobs(paths: [\"kernel_patches/include/linux/susfs.h\"], ref: \"${user_val}\") { nodes { rawBlob } } } } }"
if [ "${{ inputs.make_release }}" = "true" ]; then
GL_QUERY="$RELEASE_QUERY"
else
GL_QUERY="$HASH_QUERY"
fi
MAX_RETRIES=3
RETRY_COUNT=0
RETRY_DELAY=5
resolved=""
until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do
RESPONSE=$(curl -s --fail -G "https://gitlab.com/api/graphql" --data-urlencode "query=$GL_QUERY")
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] && [ -n "$RESPONSE" ]; then
resolved=$(echo "$RESPONSE" | jq -r '.data.project.repository.ch.sha // empty')
if [ -n "$resolved" ] && [ "$resolved" != "null" ]; then
echo " βœ… Query for $key Success"
if [ "${{ inputs.make_release }}" = "true" ]; then
extracted=$(echo "$RESPONSE" | jq -r '.data.project.repository.blobs.nodes[0].rawBlob // empty' | grep '#define SUSFS_VERSION' | awk -F'"' '{print $2}')
susfs_versions_per_key[$key]="${extracted:-unknown}"
echo " βœ… $key β†’ SUSFS_VERSION = ${extracted:-unknown}"
fi
break
fi
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "::warning::GitLab GraphQL API failed (Attempt $RETRY_COUNT/$MAX_RETRIES). Retrying..."
sleep $RETRY_DELAY
done
# Final Validation β€” SUSFS hash
if [ -z "$resolved" ] || [ "$resolved" = "null" ]; then
echo "::error::Could not resolve SUSFS ref '$user_val' for $key on GitLab."
exit 1
fi
# Final Validation β€” SUSFS version
if [ "${{ inputs.make_release }}" = "true" ] && [ "${susfs_versions_per_key[$key]:-unknown}" = "unknown" ]; then
echo "::error::Could not extract SUSFS_VERSION from susfs.h for '$key' after $MAX_RETRIES attempts. Cannot proceed with release."
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"
# SUSFS_VERSION β€” only when releasing
if [ "${{ inputs.make_release }}" = "true" ]; then
susfs_base_version="unknown"
for key in "${active_keys[@]}"; do
ver="${susfs_versions_per_key[$key]:-unknown}"
if [ "$ver" != "unknown" ]; then
susfs_base_version="$ver"
break
fi
done
if [ "$susfs_base_version" = "unknown" ]; then
#handle the case where susfs is toggled off, so version will be unknown
if [ "${#active_keys[@]}" -eq 0 ] || [ "${active_keys[0]:-}" = "" ]; then
susfs_base_version="none"
else
echo "::error::Could not extract SUSFS_VERSION from susfs.h for any active GKI key."
exit 1
fi
fi
# make_release=true: mismatch is a hard error β€” prevents wrong tag
for key in "${active_keys[@]}"; do
key_ver="${susfs_versions_per_key[$key]:-unknown}"
if [ "$key_ver" != "$susfs_base_version" ]; then
echo "::error::SUSFS version mismatch: '$key' has '$key_ver', expected '$susfs_base_version'. Align refs before releasing."
exit 1
fi
done
echo "susfs_base_version=$susfs_base_version" >> "$GITHUB_OUTPUT"
fi
# 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
}')
echo "$susfs_hashes_json" > susfs_lookup_temp.json
merged_matrix=$(echo "$merged_matrix" | jq \
--slurpfile hashes_array susfs_lookup_temp.json \
'
($hashes_array[0]) as $hashes |
[.[] | . + {susfs_resolved_hash: ($hashes["\(.android_version)-\(.kernel_version)"] // "unknown")}]
')
rm susfs_lookup_temp.json
merged_matrix=$(echo "$merged_matrix" | jq '
sort_by(
(.os_version | gsub("A"; "") | tonumber? // 0) * -1,
.model
)
')
echo "$merged_matrix" > final_matrix_temp.json
# Recalculate wrapped with updated matrix and write output
wrapped=$(jq --slurp '{ include: .[] }' final_matrix_temp.json)
echo "matrix=$(echo "$wrapped" | jq -c .)" >> "$GITHUB_OUTPUT"
rm final_matrix_temp.json
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_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
build_ts_input="${{ inputs.build_timestamp }}"
if [ -z "$build_ts_input" ]; then
build_ts_display="⏱️ auto (current build time)"
else
build_ts_display="πŸ“Œ \`$build_ts_input\`"
fi
{
cat << 'EOF'
## 🎯 Build Plan
**Target:** ${{ inputs.op_model }}
**Target Model:** ${{ inputs.op_target_model != '' && inputs.op_target_model || 'all' }}
**Devices:** ${{ steps.set-matrix.outputs.count }}
**Configuration:**
EOF
echo "- KSU Config: $ksu_display"
echo "- Build Timestamp: $build_ts_display"
cat << 'EOF'
- Optimization: ${{ inputs.optimize_level }}
- ADIOS I/O Scheduler: ${{ inputs.enable_adios && 'βœ… Yes' || '❌ No' }}
- Clean Build/No Ccache: ${{ inputs.clean_build && 'βœ… Yes' || '❌ No' }}
- Create Release: ${{ inputs.make_release && 'βœ… Yes' || '❌ No' }}
- Create Debug Artifacts: ${{ inputs.debug && 'βœ… Yes' || '❌ No' }}
- Sync toolchains: ${{ inputs.mirror_toolchains && 'βœ… Yes' || '❌ No' }}
**SUSFS Configuration:**
EOF
} >> "$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 A restriction note for android-kernel filters
if [[ "${{ inputs.op_model }}" == android*-*.* ]]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "> **⚠️ Android-Kernel Filter:** Only A15 and A16 devices will be built for \`${{ inputs.op_model }}\`" >> $GITHUB_STEP_SUMMARY
fi
mirror_toolchain:
needs: set-op-model
if: ${{ inputs.mirror_toolchains }}
uses: ./.github/workflows/mirror-toolchains.yml
secrets: inherit
prepare_ccache:
name: Prepare ccache binary (download once)
runs-on: ubuntu-latest
steps:
# Download the custom ccache binary ONCE for the whole matrix and share it as
# an artifact. Previously every parallel matrix job curl'd the same GitHub raw
# URL simultaneously, which made GitHub's edge rate-limit / 504 the CI IPs and
# fail the whole build (the URL is fine in a browser -- it's the ~30x parallel
# hammering that triggers the throttle).
- name: Download custom ccache once
run: |
set -uo pipefail
echo "::group::Download ccache"
url="https://raw.githubusercontent.com/WildKernels/kernel_patches/refs/heads/main/ccache/ccache-x86-64"
ok=0
for attempt in 1 2 3 4 5 6; do
if curl -LfsS --connect-timeout 30 --max-time 120 -H "User-Agent: Mozilla/5.0" "$url" -o ccache && [ -s ccache ]; then
ok=1; break
fi
echo "attempt $attempt failed (GitHub raw 504/throttle); backing off..."
sleep $(( attempt * 5 + RANDOM % 6 ))
done
if [ "$ok" = 1 ]; then
echo "βœ… downloaded ccache once for all matrix jobs"
else
echo "::warning::custom ccache download failed; matrix jobs will fall back to the apt ccache"
rm -f ccache
fi
echo "::endgroup::"
- name: Upload ccache artifact
uses: actions/upload-artifact@v7
with:
name: ccache-binary
path: ccache
retention-days: 1
if-no-files-found: warn
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, mirror_toolchain, prepare_ccache]
if: |
!cancelled() &&
needs.set-op-model.result == 'success' &&
(needs.mirror_toolchain.result == 'success' || needs.mirror_toolchain.result == 'skipped')
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: πŸ“₯ Checkout Code
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: 🧹 Emergency Disk Cleanup
if: ${{ matrix.disk_cleanup }}
uses: ./.github/actions/disk-cleanup
- 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 pigz
sudo apt-get clean
echo "βœ… Dependencies installed"
echo "::endgroup::"
- name: Get prebuilt ccache (downloaded once by prepare_ccache)
uses: actions/download-artifact@v8
with:
name: ccache-binary
path: ccache-dl
continue-on-error: true
- name: Install ccache (ECS by cctv18, shared via artifact)
run: |
set -uo pipefail
echo "::group::Install ccache"
if [ -s ccache-dl/ccache ]; then
sudo cp -f ccache-dl/ccache /usr/bin/ccache
sudo chmod +x /usr/bin/ccache
echo "βœ… installed custom ccache from shared artifact"
else
# prepare_ccache could not fetch it (504/throttle); keep the apt ccache
# already installed in 'Install Minimal Dependencies'.
echo "::warning::shared ccache artifact unavailable; using apt ccache"
fi
rm -rf ccache-dl
echo "[DEBUG] Ccache version : $(ccache --version | head -1)"
echo "::endgroup::"
- name: ♻️ Configure ccache & LTO cache (bounded)
run: |
echo "::group::Configure ccache environment"
set -euo pipefail
if command -v ccache >/dev/null 2>&1; then
CACHE_DIR="${{ github.workspace }}/.ccache"
LDCACHE_DIR="${{ github.workspace }}/.ld_cache"
CCACHE_LOG="${{ github.workspace }}/ccache.log"
mkdir -p "$CACHE_DIR"
mkdir -p "$LDCACHE_DIR"
SETTINGS=(
"CCACHE_DIR=$CACHE_DIR"
"LDCACHE_DIR=$LDCACHE_DIR"
"CCACHE_MAXSIZE=12G"
"CCACHE_COMPILERCHECK=content"
"CCACHE_BASEDIR=${GITHUB_WORKSPACE}"
"CCACHE_NOHASHDIR=true"
"CCACHE_IGNOREOPTIONS=--sysroot*"
"CCACHE_COMPRESSION=true"
"CCACHE_COMPRESSION_LEVEL=3"
"CCACHE_DIRECT=true"
"CCACHE_FILE_CLONE=true"
"CCACHE_INODE_CACHE=true"
"CCACHE_IS_KERNEL_COMPILING=true"
"CCACHE_UMASK=002"
"CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,pch_defines,system_headers,locale"
)
for setting in "${SETTINGS[@]}"; do
export "$setting"
echo "$setting" >> "$GITHUB_ENV"
done
if [ "${{ inputs.debug }}" == "true" ]; then
export "CCACHE_LOGFILE=$CCACHE_LOG"
echo "CCACHE_LOGFILE=$CCACHE_LOG" >> "$GITHUB_ENV"
fi
if ccache --help 2>&1 | grep -q 'depend_mode'; then
export CCACHE_DEPEND=true
echo "CCACHE_DEPEND=true" >> "$GITHUB_ENV"
fi
echo "βœ… ccache configured successfully"
echo "Current ccache configuration:"
ccache -p
else
echo "ccache not found, skipping configuration."
fi
echo "::endgroup::"
- 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/build-kernel
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 }}
build_timestamp: ${{ inputs.build_timestamp }}
enable_adios: ${{ inputs.enable_adios }}
clean: ${{ inputs.clean_build }}
debug: ${{ inputs.debug }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- 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 }}"
echo " - ReSukiSU: v${{ steps.build.outputs.ksu_version }}"
echo "ksun_ver=" >> "$GITHUB_OUTPUT"
echo "ksu_ver=${{ steps.build.outputs.ksu_version }}" >> "$GITHUB_OUTPUT"
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
cat >> $GITHUB_STEP_SUMMARY << EOF
| **ReSukiSU** | v${{ steps.build.outputs.ksu_version }} |
EOF
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
**Image SHA256:** \`${{ steps.build.outputs.image_sha256 }}\`
**AnyKernel3 Zip SHA256:** \`${{ steps.build.outputs.zip_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: |
!cancelled() &&
inputs.make_release == true &&
needs.build.result == 'success' &&
needs.set-op-model.result == 'success'
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 ReSukiSU & 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 Checksums
shell: bash
run: |
set -euo pipefail
if ! compgen -G "./downloaded-artifacts/*.zip" > /dev/null; then
echo "::error::No ZIP artifacts found."
find ./downloaded-artifacts -maxdepth 2 -type f -print || true
exit 1
fi
cd downloaded-artifacts
sha256sum *.zip | sort -k2 > ../00_CHECKSUMS.txt
cd ..
echo "Generated 00_CHECKSUMS.txt:"
cat 00_CHECKSUMS.txt
- name: πŸ“₯ Generate Downloads Index
shell: bash
run: |
set -euo pipefail
{
echo "# πŸ“₯ OnePlus Kernel Downloads"
echo ""
echo "> Select the ZIP matching your exact **device**, **OS version**, and **kernel base**."
echo ""
echo "## ⚠️ Important"
echo ""
echo "- Flashing the wrong ZIP may cause bootloop."
echo "- Back up your current boot/init_boot/vendor_boot before flashing."
echo "- Use Kernel Flasher or another trusted flashing method."
echo "- If switching from Magisk, APatch, KernelSU, ReSukiSU, or another fork, clean old root leftovers carefully."
echo ""
echo "## Quick Guide"
echo ""
echo "| What to check | Example |"
echo "|---|---|"
echo "| **Device** | \`OP12\`, \`OP13r\`, \`OP-NORD-4\` |"
echo "| **OS Version** | \`A16\`, \`A15\`, \`A14\` |"
echo "| **Kernel Base** | \`android15-6.6.30\`, \`android14-6.1.75\` |"
echo "| **Root Type** | \`RSKSU\` |"
echo ""
} > 00_DOWNLOADS.md
for os in A16 A15 A14; do
mapfile -t files < <(find downloaded-artifacts -maxdepth 1 -type f -name "*_${os}_*.zip" | sort)
if [ "${#files[@]}" -eq 0 ]; then
continue
fi
{
echo "## ${os}"
echo ""
echo "| Device | Kernel | Root | SUSFS | Download | SHA256 |"
echo "|---|---|---|---|---|---|"
} >> 00_DOWNLOADS.md
for file in "${files[@]}"; do
name="$(basename "$file")"
zipname="${name%.zip}"
rest="${zipname#AK3_}"
IFS='_' read -ra parts <<< "$rest"
model="${parts[0]:-unknown}"
os_version="${parts[1]:-unknown}"
kernel_version="${parts[2]:-unknown}"
ksu_type="${parts[3]:-unknown}"
ksu_ver="${parts[4]:-unknown}"
susfs_ver="none"
if [ "${parts[5]:-}" = "SuSFS" ]; then
susfs_ver="${parts[6]:-unknown}"
fi
sha="$(sha256sum "$file" | awk '{print $1}')"
short_sha="${sha:0:12}"
asset_url="${{ github.server_url }}/${{ github.repository }}/releases/download/${{ env.NEW_TAG }}/${name}"
echo "| \`${model}\` | \`${kernel_version}\` | \`${ksu_type}-${ksu_ver}\` | \`${susfs_ver}\` | [Download](${asset_url}) | \`${short_sha}...\` |" >> 00_DOWNLOADS.md
done
echo "" >> 00_DOWNLOADS.md
done
{
echo "---"
echo ""
echo "## πŸ” Full SHA256 Checksums"
echo ""
echo "Use \`00_CHECKSUMS.txt\` from this release to verify downloads."
echo ""
echo '```bash'
echo "sha256sum -c 00_CHECKSUMS.txt"
echo '```'
} >> 00_DOWNLOADS.md
echo "Generated 00_DOWNLOADS.md:"
cat 00_DOWNLOADS.md
- name: πŸ“ Generate Device List and Final Release Notes
id: generate-notes
run: |
echo "=== Start building the release notes ==="
# Remove ccache-binary artifacts
rm -f ./downloaded-artifacts/ccache-binary.zip
# 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}_${kernel_version}"
device_info["$full_model"]="$model|$os_version|$kernel_version|$ksu_type|$ksu_ver|$susfs_ver|$zipname"
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 ReSukiSU & 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
while IFS= read -r full_key; do
[ -z "$full_key" ] && continue
IFS='|' read -r model os_ver kernel_ver _ _ _ zipfile <<< "${device_info["$full_key"]}"
# Use jq to extract feature flags for this model from the JSON matrix.
# Reconstruct the original model name (e.g. OP13r-6.1.118) in case the ZIP
# had the kernel version suffix stripped for display. Try exact ORIG_MODEL
# first, fall back to base MODEL, both filtered by os_version.
full_kv=$(echo "$kernel_ver" | sed 's/^[^-]*-//')
possible_orig_model="${model}-${full_kv}"
feature_flags=$(jq -r --arg MODEL "$model" --arg ORIG_MODEL "$possible_orig_model" --arg OS "$os_ver" '
([ .[] | select(.model == $ORIG_MODEL and .os_version == $OS) ][0]) //
([ .[] | select(.model == $MODEL and .os_version == $OS) ][0]) |
"\(.hmbird // false)\t\(.susfs // false)\t\(.bbr // false)\t\(.bbr3 // false)\t\(.bbg // false)\t\(.ttl // false)\t\(.ip_set // false)\t\(.unicode // false)\t\(.ds // false)\t\(.ntsync // 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
features_str="N/A"
else
IFS=$'\t' read -r hmbird susfs bbr bbr3 bbg ttl ip_set unicode ds ntsync <<< "$feature_flags"
# Build features string based on boolean values
[[ "$hmbird" == "true" ]] && features+=("🐦 HMBIRD")
[[ "$susfs" == "true" ]] && features+=("࢞ SusFS")
[[ "$bbr" == "true" ]] && features+=("πŸš€ BBR")
[[ "$bbr3" == "true" ]] && features+=("πŸš€ BBRv3")
[[ "$bbg" == "true" ]] && features+=("πŸ›‘οΈ BBG")
[[ "$ttl" == "true" ]] && features+=("🌐 TTL")
[[ "$ip_set" == "true" ]] && features+=("🧱 IP_SET & IPv6 NAT")
[[ "$unicode" == "true" ]] && features+=("πŸ”§ Unicode Fix")
[[ "$ds" == "true" ]] && features+=("πŸ’» Droidspaces")
[[ "$ntsync" == "true" ]] && features+=("πŸ”ƒ NTSync")
total=${#features[@]}
if [ $total -eq 0 ]; then
features_str="N/A"
else
features_str=""
count=0
for item in "${features[@]}"; do
count=$((count + 1))
features_str+="$item"
if [ $count -lt $total ]; then
if [ $((count % 3)) -eq 0 ]; then
features_str+="<br>"
else
features_str+=" "
fi
fi
done
fi
fi
if [[ "$os_ver" =~ ^A[0-9]+$ ]]; then
os_display_version="Android ${os_ver#A}"
else
os_display_version=$os_ver
fi
download_url="https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${NEW_TAG}/${zipfile}.zip"
model_link="[$model]($download_url)"
printf "| %s | %-10s | %-16s | %-48s |\n" \
"$model_link" "$os_display_version" "$kernel_ver" "$features_str" >> release_notes.md
done < <(printf '%s\n' "${!device_info[@]}" | sort)
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")
cat << EOF >> release_notes.md
| **ReSukiSU Branch** | \`$ksu_display\` |
| **ReSukiSU Version** | \`$ksu_ver_global\` |
EOF
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
cat << EOF >> release_notes.md
- **ReSukiSU** - Kernel-level root solution
EOF
cat << EOF >> release_notes.md
- **SUSFS $SUSFS_BASE_VERSION** - Advanced hiding and security features
EOF
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
- **BBRv3** - Improved TCP congestion control
- **CAKE and PIE qdisc Support** - Better Net Schedulers
- **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
- **Droidspaces** - Support Portable Linux containers to run full Linux environments.
- **NTSync** - Provide high-performance, low-latency synchronization primitives compatible with the Windows NT kernel API
---
## πŸ“± Manager Applications
### Official Manager
EOF
cat << EOF >> release_notes.md
- **ReSukiSU Manager**
β†’ [GitHub Release](https://github.com/ReSukiSU/ReSukiSU/releases)
EOF
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 correct AnyKernel3 ZIP for your device.
2. If you previously used another root method, clean it up first:
a. **Magisk**: perform a complete uninstall **after flashing the AnyKernel3 ZIP**.
b. **KSU LKM (boot/init_boot/vendor_boot patched)**: Flash back the stock boot/init_boot/vendor_boot depending on what you patched.
c. **KSU GKI**: if you are 100% sure you already flashed stock init_boot/boot/vendor_boot, no action is needed; otherwise, follow the same steps as KSU LKM.
d. **APatch**: remove /data/adb contents to avoid leftover root conflicts **after flashing the AnyKernel3 ZIP**.
3. Flash the ZIP to the active slot using Kernel Flasher.
4. Install the ReSukiSU Manager APK, same version as mentioned in the AnyKernel3 ZIP name.
5. Open the ReSukiSU app.
6. Reboot the device if you performed any cleanup in step 2.
---
## πŸ“œ 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
- Droidspaces Support
- NTSync Support
### Previous Releases
See [Releases Page](${{ github.server_url }}/${{ github.repository }}/releases)
---
## πŸ™ Credits
- **ReSukiSU 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
for meta in 00_CHECKSUMS.txt 00_DOWNLOADS.md; do
if [ -f "$meta" ]; then
echo "Uploading $meta..."
gh release upload "${{ env.NEW_TAG }}" "$meta" --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