OP15 β’ RSKSU β’ auto timestamp #23
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: 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 |