main.yml 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. name: Build Kernels
  2. permissions:
  3. contents: write
  4. actions: write
  5. on:
  6. workflow_dispatch:
  7. inputs:
  8. release_type:
  9. description: "Release Type"
  10. type: choice
  11. options:
  12. - Action
  13. - Pre-Release
  14. - Release
  15. default: Action
  16. kernel_build_version:
  17. description: "Kernel Version"
  18. type: choice
  19. options:
  20. - All
  21. - android12-5.10
  22. - android13-5.10
  23. - android13-5.15
  24. - android14-5.15
  25. - android14-6.1
  26. - android15-6.6
  27. - android16-6.12
  28. default: All
  29. brand_name:
  30. description: "Kernel Branding"
  31. type: string
  32. default: Wild
  33. root_flavor:
  34. description: "Root Flavor"
  35. type: choice
  36. options:
  37. - KernelSU-Next
  38. - KernelSU
  39. - ReSukiSU
  40. - All
  41. default: KernelSU-Next
  42. use_susfs:
  43. description: "SUSFS"
  44. type: boolean
  45. default: true
  46. use_nomount:
  47. description: "NoMount"
  48. type: boolean
  49. default: true
  50. use_bbg:
  51. description: "Baseband Guard"
  52. type: boolean
  53. default: true
  54. use_net:
  55. description: "Networking"
  56. type: boolean
  57. default: true
  58. use_ds:
  59. description: "DroidSpaces"
  60. type: boolean
  61. default: true
  62. use_ntsync:
  63. description: "NTSync"
  64. type: boolean
  65. default: true
  66. use_ptrace:
  67. description: "Ptrace Patch"
  68. type: boolean
  69. default: true
  70. use_unicode:
  71. description: "Unicode Fix"
  72. type: boolean
  73. default: true
  74. use_bpf:
  75. description: "BPF Stack (BTF + eBPF + FUSE-BPF)"
  76. type: boolean
  77. default: true
  78. use_perf:
  79. description: "Performance"
  80. type: boolean
  81. default: false
  82. jobs:
  83. prepare-ccache:
  84. name: Prepare ccache binary (download once)
  85. runs-on: ubuntu-latest
  86. steps:
  87. - name: Download custom ccache once
  88. if: false
  89. run: |
  90. set -uo pipefail
  91. echo "::group::Download ccache"
  92. url="https://raw.githubusercontent.com/WildKernels/kernel_patches/refs/heads/main/ccache/ccache-x86-64"
  93. ok=0
  94. for attempt in 1 2 3 4 5 6; do
  95. if curl -LfsS --connect-timeout 30 --max-time 120 -H "User-Agent: Mozilla/5.0" "$url" -o ccache && [ -s ccache ]; then
  96. ok=1; break
  97. fi
  98. echo "attempt $attempt failed (GitHub raw 504/throttle); backing off..."
  99. sleep $(( attempt * 5 + RANDOM % 6 ))
  100. done
  101. if [ "$ok" = 1 ]; then
  102. echo "✅ downloaded ccache once for all matrix jobs"
  103. else
  104. echo "::warning::custom ccache download failed; matrix jobs will fall back to the apt ccache"
  105. rm -f ccache
  106. fi
  107. echo "::endgroup::"
  108. - name: Upload ccache artifact
  109. if: false
  110. uses: actions/upload-artifact@v7
  111. with:
  112. name: ccache-binary
  113. path: ccache
  114. retention-days: 1
  115. if-no-files-found: warn
  116. resolve-sources:
  117. runs-on: ubuntu-latest
  118. outputs:
  119. nomount_commit: ${{ steps.resolve.outputs.nomount_commit }}
  120. kernelsu_commit: ${{ steps.resolve.outputs.kernelsu_commit }}
  121. kernelsu_branch: ${{ steps.resolve.outputs.kernelsu_branch }}
  122. root_flavor: ${{ steps.resolve.outputs.root_flavor }}
  123. root_commit: ${{ steps.resolve.outputs.root_commit }}
  124. root_matrix: ${{ steps.resolve.outputs.root_matrix }}
  125. susfs_commit_android12_5_10: ${{ steps.resolve.outputs.susfs_commit_android12_5_10 }}
  126. susfs_commit_android13_5_10: ${{ steps.resolve.outputs.susfs_commit_android13_5_10 }}
  127. susfs_commit_android13_5_15: ${{ steps.resolve.outputs.susfs_commit_android13_5_15 }}
  128. susfs_commit_android14_5_15: ${{ steps.resolve.outputs.susfs_commit_android14_5_15 }}
  129. susfs_commit_android14_6_1: ${{ steps.resolve.outputs.susfs_commit_android14_6_1 }}
  130. susfs_commit_android15_6_6: ${{ steps.resolve.outputs.susfs_commit_android15_6_6 }}
  131. susfs_commit_android16_6_12: ${{ steps.resolve.outputs.susfs_commit_android16_6_12 }}
  132. kernel_patches_commit: ${{ steps.resolve.outputs.kernel_patches_commit }}
  133. anykernel3_commit: ${{ steps.resolve.outputs.anykernel3_commit }}
  134. droidspaces_commit: ${{ steps.resolve.outputs.droidspaces_commit }}
  135. feature_set: ${{ steps.features.outputs.feature_set }}
  136. steps:
  137. - name: Resolve latest component commits
  138. id: resolve
  139. shell: bash
  140. run: |
  141. set -euo pipefail
  142. retry() {
  143. local n=0
  144. until [ "$n" -ge 5 ]; do
  145. "$@" && return 0
  146. n=$((n+1))
  147. echo "retry $n/5 failed for: $*" >&2
  148. sleep 5
  149. done
  150. return 1
  151. }
  152. resolve_sha() { # repo ref
  153. retry git ls-remote "$1" "$2" | awk '{print $1; exit}'
  154. }
  155. emit() { # key value
  156. if [ -z "$2" ] || ! [[ "$2" =~ ^[0-9a-f]{40}$ ]]; then
  157. echo "Failed to resolve $1 (got: ${2:-empty})" >&2
  158. exit 1
  159. fi
  160. echo "$1=$2" >> "$GITHUB_OUTPUT"
  161. }
  162. # NoMount (dev tip)
  163. emit nomount_commit "$(resolve_sha https://github.com/maxsteeel/nomount.git refs/heads/dev)"
  164. # KernelSU-Next manager — always built from the official repo's dev branch.
  165. # pershoot's fork is used only for the kernel SUSFS integration (root_commit below),
  166. # never for the manager.
  167. emit kernelsu_commit "$(resolve_sha https://github.com/KernelSU-Next/KernelSU-Next.git refs/heads/dev)"
  168. echo "kernelsu_branch=dev" >> "$GITHUB_OUTPUT"
  169. # Selected root implementation(s). Maps the human-facing choice
  170. # (KernelSU-Next / KernelSU / ReSukiSU / All) to internal root-setup
  171. # flavors (next / classic / resukisu). Every build always has a root;
  172. # 'All' emits a matrix of all three root flavors plus each pinned commit.
  173. #
  174. # Always resolve all three branch-tip commits so 'All' can fan out.
  175. CLASSIC_COMMIT="$(resolve_sha https://github.com/tiann/KernelSU.git refs/heads/main)"
  176. RESUKISU_COMMIT="$(resolve_sha https://github.com/ReSukiSU/ReSukiSU.git refs/heads/main)"
  177. if [ "${{ inputs.use_susfs }}" = "true" ]; then
  178. NEXT_COMMIT="$(resolve_sha https://github.com/pershoot/KernelSU-Next.git refs/heads/dev-susfs)"
  179. else
  180. NEXT_COMMIT="$(resolve_sha https://github.com/KernelSU-Next/KernelSU-Next.git refs/heads/dev)"
  181. fi
  182. mk_entry() { # flavor variant commit
  183. printf '{"root_flavor":"%s","variant":"%s","root_commit":"%s"}' "$1" "$2" "$3"
  184. }
  185. case "${{ inputs.root_flavor }}" in
  186. KernelSU)
  187. emit root_flavor "classic"
  188. emit root_commit "$CLASSIC_COMMIT"
  189. ROOT_MATRIX="[$(mk_entry classic KernelSU "$CLASSIC_COMMIT")]"
  190. ;;
  191. KernelSU-Next)
  192. emit root_flavor "next"
  193. emit root_commit "$NEXT_COMMIT"
  194. ROOT_MATRIX="[$(mk_entry next KernelSU-Next "$NEXT_COMMIT")]"
  195. ;;
  196. ReSukiSU)
  197. emit root_flavor "resukisu"
  198. emit root_commit "$RESUKISU_COMMIT"
  199. ROOT_MATRIX="[$(mk_entry resukisu ReSukiSU "$RESUKISU_COMMIT")]"
  200. ;;
  201. All)
  202. # Fan out to all three root variants, each with its pinned commit.
  203. ROOT_MATRIX="[$(mk_entry next KernelSU-Next "$NEXT_COMMIT"),$(mk_entry classic KernelSU "$CLASSIC_COMMIT"),$(mk_entry resukisu ReSukiSU "$RESUKISU_COMMIT")]"
  204. echo "root_flavor=" >> "$GITHUB_OUTPUT"
  205. echo "root_commit=" >> "$GITHUB_OUTPUT"
  206. ;;
  207. *)
  208. echo "ERROR: unknown root_flavor '${{ inputs.root_flavor }}'" >&2
  209. exit 1
  210. ;;
  211. esac
  212. echo "root_matrix=$ROOT_MATRIX" >> "$GITHUB_OUTPUT"
  213. # SUSFS per supported Android/kernel branch (gitlab simonpunk/susfs4ksu)
  214. declare -A SUSFS_BRANCHES=(
  215. [susfs_commit_android12_5_10]="gki-android12-5.10"
  216. [susfs_commit_android13_5_10]="gki-android13-5.10"
  217. [susfs_commit_android13_5_15]="gki-android13-5.15"
  218. [susfs_commit_android14_5_15]="gki-android14-5.15"
  219. [susfs_commit_android14_6_1]="gki-android14-6.1"
  220. [susfs_commit_android15_6_6]="gki-android15-6.6"
  221. [susfs_commit_android16_6_12]="gki-android16-6.12"
  222. )
  223. for key in "${!SUSFS_BRANCHES[@]}"; do
  224. emit "$key" "$(resolve_sha https://gitlab.com/simonpunk/susfs4ksu.git refs/heads/${SUSFS_BRANCHES[$key]})"
  225. done
  226. # Tracked build helpers (resolved automatically, before the run)
  227. emit kernel_patches_commit "$(resolve_sha https://github.com/WildKernels/kernel_patches.git refs/heads/main)"
  228. emit anykernel3_commit "$(resolve_sha https://github.com/WildKernels/AnyKernel3.git refs/heads/gki-2.0)"
  229. emit droidspaces_commit "$(resolve_sha https://github.com/ravindu644/Droidspaces-OSS.git refs/heads/main)"
  230. echo "Resolved:"
  231. env | grep -E '^(nomount_commit|kernelsu_commit|kernelsu_branch|root_commit|susfs_commit_|kernel_patches_commit|anykernel3_commit|droidspaces_commit)' | sort || true
  232. - name: Resolve manager builds for selected root flavor(s)
  233. id: manager
  234. shell: bash
  235. run: |
  236. set -euo pipefail
  237. # Every run must ship the EXACT manager build(s) matching the selected
  238. # root's stock/sync commit, downloaded as artifacts. If any exact run
  239. # can't be found, fail NOW (before any kernel builds). For 'All' this
  240. # enumerates all three flavors; otherwise just the one selected.
  241. resolve_one() { # flavor
  242. local flavor="$1"
  243. local owner repo wf stock
  244. case "$flavor" in
  245. next)
  246. owner="KernelSU-Next"; repo="KernelSU-Next"; wf="build-manager-ci.yml"
  247. git clone --depth=1 --branch dev-susfs "https://github.com/pershoot/KernelSU-Next.git" /tmp/ksu-manager-check
  248. cd /tmp/ksu-manager-check
  249. git remote add official "https://github.com/KernelSU-Next/KernelSU-Next.git" || true
  250. git fetch --depth=1 official dev
  251. stock=$(git merge-base HEAD FETCH_HEAD)
  252. ;;
  253. classic)
  254. owner="tiann"; repo="KernelSU"; wf="build-manager.yml"
  255. stock=$(git ls-remote https://github.com/tiann/KernelSU.git refs/heads/main | awk '{print $1; exit}')
  256. ;;
  257. resukisu)
  258. owner="ReSukiSU"; repo="ReSukiSU"; wf="build-manager.yml"
  259. stock=$(git ls-remote https://github.com/ReSukiSU/ReSukiSU.git refs/heads/main | awk '{print $1; exit}')
  260. ;;
  261. *)
  262. echo "skipping unknown flavor: $flavor" >&2
  263. return 0
  264. ;;
  265. esac
  266. run_id=$(curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors \
  267. "https://api.github.com/repos/${owner}/${repo}/actions/workflows/${wf}/runs?head_sha=${stock}" \
  268. | jq -r '.workflow_runs[0].id // empty')
  269. if [[ -z "${run_id}" ]]; then
  270. echo "ERROR: no ${repo} manager build exists for stock commit ${stock}" >&2
  271. exit 1
  272. fi
  273. printf '{"flavor":"%s","owner":"%s","repo":"%s","wf":"%s","stock":"%s","run_id":%s}' \
  274. "$flavor" "$owner" "$repo" "$wf" "$stock" "$run_id"
  275. }
  276. FLAVORS="${{ inputs.root_flavor }}"
  277. echo "Root flavor: ${FLAVORS}"
  278. LIST=""
  279. case "${FLAVORS}" in
  280. All) LIST="$(resolve_one next),$(resolve_one classic),$(resolve_one resukisu)" ;;
  281. KernelSU-Next) LIST="$(resolve_one next)" ;;
  282. KernelSU) LIST="$(resolve_one classic)" ;;
  283. ReSukiSU) LIST="$(resolve_one resukisu)" ;;
  284. *) echo "Skipping manager resolution (no root flavor / none)." ;;
  285. esac
  286. if [ -n "$LIST" ]; then
  287. echo "manager_list=[$LIST]" >> "$GITHUB_OUTPUT"
  288. echo "Exact manager builds resolved."
  289. else
  290. echo "manager_list=" >> "$GITHUB_OUTPUT"
  291. fi
  292. - name: Download manager APK artifacts
  293. if: ${{ steps.manager.outputs.manager_list != '' }}
  294. shell: bash
  295. run: |
  296. set -euo pipefail
  297. mkdir -p release-assets/manager
  298. echo '${{ steps.manager.outputs.manager_list }}' \
  299. | jq -c '.[]' | while read -r M; do
  300. OWNER=$(echo "$M" | jq -r .owner)
  301. REPO=$(echo "$M" | jq -r .repo)
  302. RUN_ID=$(echo "$M" | jq -r .run_id)
  303. echo "Downloading $REPO manager from run $RUN_ID"
  304. mkdir -p "release-assets/manager/$REPO"
  305. cd "release-assets/manager/$REPO"
  306. ARTIFACTS_JSON=$(curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors \
  307. "https://api.github.com/repos/${OWNER}/${REPO}/actions/runs/${RUN_ID}/artifacts")
  308. echo "$ARTIFACTS_JSON" | jq -r '.artifacts[].id' | while read -r AID; do
  309. curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors \
  310. -o "manager-${AID}.zip" \
  311. "https://api.github.com/repos/${OWNER}/${REPO}/actions/artifacts/${AID}/zip"
  312. unzip -o "manager-${AID}.zip" >/dev/null 2>&1 || true
  313. rm -f "manager-${AID}.zip"
  314. done
  315. cd "$(git rev-parse --show-toplevel 2>/dev/null || echo /github/workspace)"
  316. find "release-assets/manager/$REPO" -type f \( -iname '*.apk' -o -iname '*.zip' \) -print
  317. done
  318. ls -la release-assets/manager
  319. - name: Upload manager APK as artifact
  320. if: ${{ steps.manager.outputs.manager_list != '' }}
  321. uses: actions/upload-artifact@v4
  322. with:
  323. name: manager-apk
  324. path: release-assets/manager/
  325. if-no-files-found: error
  326. - name: Compose requested feature set
  327. id: features
  328. shell: bash
  329. run: |
  330. set -euo pipefail
  331. # Every build always has a root implementation (no 'none' option), so
  332. # SUSFS always has a root to hook into -- no extra guard needed.
  333. # A Release/Pre-Release ships whichever manger build(s) were resolved
  334. # in the manager step above (exact match per root flavor). No root
  335. # restriction applies here anymore -- KernelSU-Next, KernelSU, ReSukiSU,
  336. # or 'All' are all valid release targets.
  337. FEATURES=""
  338. [ "${{ inputs.use_susfs }}" = "true" ] && FEATURES="${FEATURES}SUSFS+"
  339. [ "${{ inputs.use_nomount }}" = "true" ] && FEATURES="${FEATURES}NoMount+"
  340. [ "${{ inputs.use_bbg }}" = "true" ] && FEATURES="${FEATURES}BBG+"
  341. [ "${{ inputs.use_net }}" = "true" ] && FEATURES="${FEATURES}NET+"
  342. [ "${{ inputs.use_ds }}" = "true" ] && FEATURES="${FEATURES}DS+"
  343. [ "${{ inputs.use_ntsync }}" = "true" ] && FEATURES="${FEATURES}NTSync+"
  344. [ "${{ inputs.use_ptrace }}" = "true" ] && FEATURES="${FEATURES}Ptrace+"
  345. [ "${{ inputs.use_unicode }}" = "true" ] && FEATURES="${FEATURES}Unicode+"
  346. [ "${{ inputs.use_bpf }}" = "true" ] && FEATURES="${FEATURES}BPF+"
  347. [ "${{ inputs.use_perf }}" = "true" ] && FEATURES="${FEATURES}Perf+"
  348. echo "feature_set=${FEATURES%+}" >> "$GITHUB_OUTPUT"
  349. - name: Clone shared source repos once
  350. if: ${{ inputs.use_nomount }}
  351. shell: bash
  352. run: |
  353. set -euo pipefail
  354. mkdir -p sources
  355. # NoMount kernel sources at the resolved dev tip
  356. git clone --depth=1 --branch dev https://github.com/maxsteeel/nomount.git sources/nomount
  357. [ "$(git -C sources/nomount rev-parse HEAD)" = "${{ steps.resolve.outputs.nomount_commit }}" ] || \
  358. { echo "NoMount clone HEAD mismatch" >&2; exit 1; }
  359. echo "sources_dir=$PWD/sources" >> "$GITHUB_ENV"
  360. - name: Upload sources artifact
  361. if: ${{ inputs.use_nomount }}
  362. uses: actions/upload-artifact@v7
  363. with:
  364. name: sources
  365. path: sources
  366. retention-days: 1
  367. if-no-files-found: error
  368. compression-level: 9
  369. build-nomount-module:
  370. needs: resolve-sources
  371. runs-on: ubuntu-latest
  372. steps:
  373. - uses: actions/checkout@v7
  374. if: ${{ inputs.use_nomount }}
  375. - name: Build NoMount metamodule
  376. id: nomount-metamodule
  377. if: ${{ inputs.use_nomount }}
  378. uses: ./.github/actions/nomount-metamodule
  379. with:
  380. commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  381. - name: Upload NoMount metamodule
  382. if: ${{ inputs.use_nomount }}
  383. uses: actions/upload-artifact@v7
  384. with:
  385. name: NoMount-Metamodule
  386. path: ${{ steps.nomount-metamodule.outputs.module_path }}
  387. if-no-files-found: error
  388. compression-level: 0
  389. build-fuse-bpf:
  390. needs: resolve-sources
  391. runs-on: ubuntu-latest
  392. steps:
  393. - uses: actions/checkout@v7
  394. if: ${{ inputs.use_bpf }}
  395. - name: Sparse-checkout FUSE-BPF source from kernel/common (android16-6.12-lts)
  396. if: ${{ inputs.use_bpf }}
  397. shell: bash
  398. run: |
  399. set -euo pipefail
  400. # Only the daemon source + the headers it compiles against are needed,
  401. # NOT the full ~16GB kernel tree (which includes ~12GB of prebuilts).
  402. git clone --depth 1 --filter=blob:none --sparse \
  403. --branch android16-6.12-lts \
  404. https://android.googlesource.com/kernel/common \
  405. "${{ github.workspace }}/kernel/common"
  406. cd "${{ github.workspace }}/kernel/common"
  407. git sparse-checkout set \
  408. tools/testing/selftests/filesystems/fuse \
  409. include \
  410. arch/arm64/include \
  411. tools/testing/selftests
  412. - name: Build FUSE-BPF Userspace Daemon (Issue #211)
  413. if: ${{ inputs.use_bpf }}
  414. uses: ./.github/actions/fuse-bpf
  415. with:
  416. version: android16-6.12
  417. kernel_version: 6.12
  418. android_version: android16
  419. build-android12-5-10:
  420. needs:
  421. - prepare-ccache
  422. - resolve-sources
  423. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android12-5.10') }}
  424. strategy:
  425. fail-fast: false
  426. matrix:
  427. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  428. uses: ./.github/workflows/prepare.yml
  429. with:
  430. config_file: .github/config/android12-5.10.json
  431. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  432. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android12_5_10 }}
  433. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  434. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  435. root_flavor: ${{ matrix.root.root_flavor }}
  436. root_commit: ${{ matrix.root.root_commit }}
  437. variant: ${{ matrix.root.variant }}
  438. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  439. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  440. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  441. brand_name: ${{ inputs.brand_name }}
  442. secrets: inherit
  443. build-android13-5-10:
  444. needs:
  445. - prepare-ccache
  446. - resolve-sources
  447. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android13-5.10') }}
  448. strategy:
  449. fail-fast: false
  450. matrix:
  451. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  452. uses: ./.github/workflows/prepare.yml
  453. with:
  454. config_file: .github/config/android13-5.10.json
  455. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  456. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android13_5_10 }}
  457. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  458. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  459. root_flavor: ${{ matrix.root.root_flavor }}
  460. root_commit: ${{ matrix.root.root_commit }}
  461. variant: ${{ matrix.root.variant }}
  462. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  463. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  464. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  465. brand_name: ${{ inputs.brand_name }}
  466. secrets: inherit
  467. build-android13-5-15:
  468. needs:
  469. - prepare-ccache
  470. - resolve-sources
  471. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android13-5.15') }}
  472. strategy:
  473. fail-fast: false
  474. matrix:
  475. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  476. uses: ./.github/workflows/prepare.yml
  477. with:
  478. config_file: .github/config/android13-5.15.json
  479. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  480. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android13_5_15 }}
  481. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  482. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  483. root_flavor: ${{ matrix.root.root_flavor }}
  484. root_commit: ${{ matrix.root.root_commit }}
  485. variant: ${{ matrix.root.variant }}
  486. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  487. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  488. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  489. brand_name: ${{ inputs.brand_name }}
  490. secrets: inherit
  491. build-android14-5-15:
  492. needs:
  493. - prepare-ccache
  494. - resolve-sources
  495. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android14-5.15') }}
  496. strategy:
  497. fail-fast: false
  498. matrix:
  499. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  500. uses: ./.github/workflows/prepare.yml
  501. with:
  502. config_file: .github/config/android14-5.15.json
  503. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  504. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android14_5_15 }}
  505. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  506. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  507. root_flavor: ${{ matrix.root.root_flavor }}
  508. root_commit: ${{ matrix.root.root_commit }}
  509. variant: ${{ matrix.root.variant }}
  510. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  511. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  512. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  513. brand_name: ${{ inputs.brand_name }}
  514. secrets: inherit
  515. build-android14-6-1:
  516. needs:
  517. - prepare-ccache
  518. - resolve-sources
  519. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android14-6.1') }}
  520. strategy:
  521. fail-fast: false
  522. matrix:
  523. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  524. uses: ./.github/workflows/prepare.yml
  525. with:
  526. config_file: .github/config/android14-6.1.json
  527. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  528. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android14_6_1 }}
  529. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  530. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  531. root_flavor: ${{ matrix.root.root_flavor }}
  532. root_commit: ${{ matrix.root.root_commit }}
  533. variant: ${{ matrix.root.variant }}
  534. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  535. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  536. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  537. brand_name: ${{ inputs.brand_name }}
  538. secrets: inherit
  539. build-android15-6-6:
  540. needs:
  541. - prepare-ccache
  542. - resolve-sources
  543. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android15-6.6') }}
  544. strategy:
  545. fail-fast: false
  546. matrix:
  547. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  548. uses: ./.github/workflows/prepare.yml
  549. with:
  550. config_file: .github/config/android15-6.6.json
  551. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  552. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android15_6_6 }}
  553. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  554. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  555. root_flavor: ${{ matrix.root.root_flavor }}
  556. root_commit: ${{ matrix.root.root_commit }}
  557. variant: ${{ matrix.root.variant }}
  558. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  559. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  560. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  561. brand_name: ${{ inputs.brand_name }}
  562. secrets: inherit
  563. build-android16-6-12:
  564. needs:
  565. - prepare-ccache
  566. - resolve-sources
  567. if: ${{ !inputs.test_release_notes && (inputs.kernel_build_version == 'All' || inputs.kernel_build_version == 'android16-6.12') }}
  568. strategy:
  569. fail-fast: false
  570. matrix:
  571. root: ${{ fromJson(needs.resolve-sources.outputs.root_matrix) }}
  572. uses: ./.github/workflows/prepare.yml
  573. with:
  574. config_file: .github/config/android16-6.12.json
  575. feature_set: ${{ needs.resolve-sources.outputs.feature_set }}
  576. susfs_commit: ${{ needs.resolve-sources.outputs.susfs_commit_android16_6_12 }}
  577. nomount_commit: ${{ needs.resolve-sources.outputs.nomount_commit }}
  578. kernel_patches_commit: ${{ needs.resolve-sources.outputs.kernel_patches_commit }}
  579. root_flavor: ${{ matrix.root.root_flavor }}
  580. root_commit: ${{ matrix.root.root_commit }}
  581. variant: ${{ matrix.root.variant }}
  582. ksu_branch: ${{ needs.resolve-sources.outputs.kernelsu_commit }}
  583. anykernel3_commit: ${{ needs.resolve-sources.outputs.anykernel3_commit }}
  584. droidspaces_commit: ${{ needs.resolve-sources.outputs.droidspaces_commit }}
  585. brand_name: ${{ inputs.brand_name }}
  586. secrets: inherit
  587. rej:
  588. if: ${{ !inputs.test_release_notes && always() }}
  589. runs-on: ubuntu-latest
  590. permissions:
  591. actions: read
  592. contents: read
  593. needs:
  594. - build-android12-5-10
  595. - build-android13-5-10
  596. - build-android13-5-15
  597. - build-android14-5-15
  598. - build-android14-6-1
  599. - build-android15-6-6
  600. - build-android16-6-12
  601. steps:
  602. - name: Download Misc Artifacts
  603. uses: actions/download-artifact@v7
  604. with:
  605. path: ./downloaded-artifacts
  606. pattern: '*-Rejects'
  607. merge-multiple: false
  608. - name: Process Reject Artifacts
  609. run: |
  610. mkdir -p aio-rejects
  611. # Iterate over Rejects artifacts in downloaded-artifacts
  612. for dir in ./downloaded-artifacts/*-Rejects; do
  613. [ -d "$dir" ] || continue
  614. dirname=$(basename "$dir")
  615. original_name=${dirname%-Rejects}
  616. source_dir="$dir"
  617. if [ -d "$dir/patch-rejects" ]; then
  618. source_dir="$dir/patch-rejects"
  619. fi
  620. mapfile -t rej_files < <(find "$source_dir" -type f -name '*.rej' | sort)
  621. filtered_rej_files=()
  622. for rej_file in "${rej_files[@]}"; do
  623. rel_rej="${rej_file#${source_dir}/}"
  624. rel_name="$(basename "$rel_rej")"
  625. if [ "$rel_rej" = "common/mm/rmap.c.rej" ] || [ "$rel_name" = "i2c-nomadik.c.rej" ]; then
  626. echo "Skipping $rel_rej because it is an ignored reject file"
  627. continue
  628. fi
  629. filtered_rej_files+=("$rej_file")
  630. done
  631. if [ "${#filtered_rej_files[@]}" -eq 0 ]; then
  632. echo "Skipping $dirname because it only contains ignored reject files"
  633. continue
  634. fi
  635. mkdir -p "aio-rejects/$original_name"
  636. if [ -d "$dir/patch-rejects" ]; then
  637. cp -r "$dir/patch-rejects/." "aio-rejects/$original_name/"
  638. else
  639. cp -r "$dir/." "aio-rejects/$original_name/"
  640. fi
  641. find "aio-rejects/$original_name" -type f -name 'i2c-nomadik.c.rej' -delete
  642. done
  643. if [ "$(ls -A aio-rejects)" ]; then
  644. cd aio-rejects
  645. zip -r -q -9 ../AIO-REJ.zip .
  646. cd ..
  647. else
  648. echo "No rejects found to upload."
  649. fi
  650. - name: Rejects Summary
  651. run: |
  652. echo "Reject artifacts were collected locally only and are not uploaded." >> "$GITHUB_STEP_SUMMARY"
  653. - name: Upload AIO-REJ Artifact
  654. uses: actions/upload-artifact@v7
  655. with:
  656. name: AIO-REJ
  657. path: AIO-REJ.zip
  658. if-no-files-found: ignore
  659. summary:
  660. if: ${{ !inputs.test_release_notes && always() }}
  661. runs-on: ubuntu-latest
  662. permissions:
  663. actions: write
  664. contents: read
  665. needs:
  666. - resolve-sources
  667. - build-android12-5-10
  668. - build-android13-5-10
  669. - build-android13-5-15
  670. - build-android14-5-15
  671. - build-android14-6-1
  672. - build-android15-6-6
  673. - build-android16-6-12
  674. steps:
  675. - name: Download All Build Summaries
  676. uses: actions/download-artifact@v7
  677. with:
  678. path: ./summaries
  679. pattern: '*-Summary'
  680. merge-multiple: true
  681. - name: 📋 Workflow Build Summary
  682. run: |
  683. {
  684. echo "# 📋 Build Summary — $(date -u '+%Y-%m-%d %H:%M UTC')"
  685. echo
  686. echo "**Feature Set:** ${{ needs.resolve-sources.outputs.feature_set }}"
  687. echo "**Root Flavor:** ${{ inputs.root_flavor || 'none' }}"
  688. echo "**Brand:** ${{ inputs.brand_name || 'Wild' }}"
  689. echo
  690. # Count successes and failures
  691. SUCCESS_COUNT=0
  692. FAIL_COUNT=0
  693. # Sort summaries so they appear in a consistent order
  694. for f in ./summaries/*.md; do
  695. [ -f "$f" ] || continue
  696. if grep -q "✅ Success" "$f"; then
  697. SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
  698. else
  699. FAIL_COUNT=$((FAIL_COUNT + 1))
  700. fi
  701. done
  702. TOTAL=$((SUCCESS_COUNT + FAIL_COUNT))
  703. echo "**Results:** ${SUCCESS_COUNT}/${TOTAL} succeeded"
  704. if [ "$FAIL_COUNT" -gt 0 ]; then
  705. echo " - ❌ ${FAIL_COUNT} failed"
  706. fi
  707. echo
  708. echo "---"
  709. echo
  710. # Output each summary
  711. for f in ./summaries/*.md; do
  712. [ -f "$f" ] || continue
  713. cat "$f"
  714. echo
  715. echo "---"
  716. echo
  717. done
  718. echo
  719. echo "> **SUSFS Commits:**"
  720. echo "> - android12-5.10: ${{ needs.resolve-sources.outputs.susfs_commit_android12_5_10 }}"
  721. echo "> - android13-5.10: ${{ needs.resolve-sources.outputs.susfs_commit_android13_5_10 }}"
  722. echo "> - android13-5.15: ${{ needs.resolve-sources.outputs.susfs_commit_android13_5_15 }}"
  723. echo "> - android14-5.15: ${{ needs.resolve-sources.outputs.susfs_commit_android14_5_15 }}"
  724. echo "> - android14-6.1: ${{ needs.resolve-sources.outputs.susfs_commit_android14_6_1 }}"
  725. echo "> - android15-6.6: ${{ needs.resolve-sources.outputs.susfs_commit_android15_6_6 }}"
  726. echo "> - android16-6.12: ${{ needs.resolve-sources.outputs.susfs_commit_android16_6_12 }}"
  727. } >> "$GITHUB_STEP_SUMMARY"
  728. - name: 🧹 Cleanup Summary Files
  729. if: ${{ always() }}
  730. run: rm -rf ./summaries
  731. - name: 🧹 Delete Summary Artifacts
  732. if: ${{ always() }}
  733. env:
  734. GH_TOKEN: ${{ github.token }}
  735. run: |
  736. # Delete all *-Summary artifacts from this run since they're now consolidated
  737. RUN_ID="${{ github.run_id }}"
  738. ARTIFACTS=$(gh api "repos/${{ github.repository }}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name | endswith("-Summary")) | .id')
  739. for id in $ARTIFACTS; do
  740. echo "Deleting summary artifact id=$id"
  741. gh api -X DELETE "repos/${{ github.repository }}/actions/artifacts/${id}" || true
  742. done
  743. release:
  744. runs-on: ubuntu-latest
  745. if: ${{ github.event_name == 'workflow_dispatch' && !inputs.test_release_notes && (inputs.release_type == 'Pre-Release' || inputs.release_type == 'Release') }}
  746. permissions:
  747. contents: write
  748. needs:
  749. - build-android12-5-10
  750. - build-android13-5-10
  751. - build-android13-5-15
  752. - build-android14-5-15
  753. - build-android14-6-1
  754. - build-android15-6-6
  755. - build-android16-6-12
  756. - build-nomount-module
  757. env:
  758. GH_TOKEN: ${{ github.token }}
  759. RELEASE_NOTES: .github/config/RELEASE_NOTES.md
  760. outputs:
  761. new_tag: ${{ steps.tag.outputs.new_tag }}
  762. steps:
  763. - name: Validate Selected Builds
  764. run: |
  765. set -euo pipefail
  766. failed=0
  767. current_type="${{ inputs.kernel_build_version }}"
  768. check_selected() {
  769. local target="$1"
  770. local job_name="$2"
  771. local result="$3"
  772. if [[ "$current_type" == "all" || "$current_type" == "$target" ]]; then
  773. if [[ "$result" != "success" ]]; then
  774. echo "Required job $job_name did not succeed (result: $result)"
  775. failed=1
  776. fi
  777. fi
  778. }
  779. check_selected "android12-5.10" "build-android12-5-10" "${{ needs.build-android12-5-10.result }}"
  780. check_selected "android13-5.10" "build-android13-5-10" "${{ needs.build-android13-5-10.result }}"
  781. check_selected "android13-5.15" "build-android13-5-15" "${{ needs.build-android13-5-15.result }}"
  782. check_selected "android14-5.15" "build-android14-5-15" "${{ needs.build-android14-5-15.result }}"
  783. check_selected "android14-6.1" "build-android14-6-1" "${{ needs.build-android14-6-1.result }}"
  784. check_selected "android15-6.6" "build-android15-6-6" "${{ needs.build-android15-6-6.result }}"
  785. check_selected "android16-6.12" "build-android16-6-12" "${{ needs.build-android16-6-12.result }}"
  786. if [[ "$failed" -ne 0 ]]; then
  787. exit 1
  788. fi
  789. - name: Free Disk Space
  790. if: true
  791. uses: endersonmenezes/free-disk-space@v3 # Use @main for latest, @v3 for stable
  792. with:
  793. remove_android: true
  794. remove_dotnet: true
  795. remove_haskell: true
  796. remove_tool_cache: true
  797. remove_swap: true
  798. remove_packages: "azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox postgresql* temurin-* *llvm* mysql* dotnet-sdk-*"
  799. remove_packages_one_command: true
  800. remove_folders: "/usr/share/swift /usr/share/miniconda /usr/share/az* /usr/local/lib/node_modules /usr/local/share/chromium /usr/local/share/powershell /usr/local/julia /usr/local/aws-cli /usr/local/aws-sam-cli /usr/share/gradle"
  801. rm_cmd: "rmz" # Use 'rmz' for faster deletion (default: 'rm')
  802. rmz_version: "3.1.1" # Required when rm_cmd is 'rmz'
  803. testing: false
  804. - name: Checkout code
  805. uses: actions/checkout@v7
  806. - name: Generate New Tag
  807. id: tag
  808. if: inputs.release_type != 'Action'
  809. run: |
  810. mapfile -t RELEASE_TAGS < <(gh api --paginate "repos/${{ github.repository }}/tags?per_page=100" --jq '.[].name' 2>/dev/null | grep -E '^r[0-9]+$' || true)
  811. LATEST_REV=0
  812. for TAG in "${RELEASE_TAGS[@]}"; do
  813. REV="${TAG#r}"
  814. if [[ "$REV" =~ ^[0-9]+$ && "$REV" -gt "$LATEST_REV" ]]; then
  815. LATEST_REV="$REV"
  816. fi
  817. done
  818. if [[ "$LATEST_REV" -gt 0 ]]; then
  819. NEW_TAG="r$((LATEST_REV + 1))"
  820. else
  821. NEW_TAG="r1"
  822. fi
  823. if [[ ${#RELEASE_TAGS[@]} -gt 0 ]]; then
  824. echo "Latest r-tag: r${LATEST_REV}"
  825. else
  826. echo "No existing r-tags found; starting at r1"
  827. fi
  828. echo "New tag: $NEW_TAG"
  829. echo "NEW_TAG=${NEW_TAG}" >> $GITHUB_ENV
  830. echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT
  831. - name: Set Release Title
  832. id: release_meta
  833. run: |
  834. set -euo pipefail
  835. # Format release title: convert rN -> Release N
  836. if [[ "${NEW_TAG}" =~ ^r([0-9]+)$ ]]; then
  837. REV_NUM="${BASH_REMATCH[1]}"
  838. release_title="Release ${REV_NUM}"
  839. else
  840. release_title="Release ${NEW_TAG}"
  841. fi
  842. echo "release_title=$release_title" >> "$GITHUB_OUTPUT"
  843. - name: Extract KernelSU Version Info
  844. id: ksu_version
  845. run: |
  846. set -euo pipefail
  847. KSU_REPO="https://github.com/pershoot/KernelSU-Next.git"
  848. KSU_INPUT="dev-susfs"
  849. # Try as branch first, fall back to commit
  850. if git ls-remote --heads "$KSU_REPO" "$KSU_INPUT" | grep -q .; then
  851. echo "Cloning KernelSU-Next from branch: $KSU_INPUT"
  852. git clone --branch "$KSU_INPUT" "$KSU_REPO" /tmp/kernelsu-next
  853. cd /tmp/kernelsu-next
  854. KSUN_COMMIT=$(git rev-parse HEAD)
  855. else
  856. echo "Cloning KernelSU-Next from dev-susfs branch, then checking out commit: $KSU_INPUT"
  857. git clone --branch dev-susfs "$KSU_REPO" /tmp/kernelsu-next
  858. cd /tmp/kernelsu-next
  859. git fetch --depth=50 origin "$KSU_INPUT"
  860. git checkout "$KSU_INPUT"
  861. KSUN_COMMIT="$KSU_INPUT"
  862. fi
  863. # The manager is built on the OFFICIAL KernelSU-Next repo
  864. # (pershoot's workflows are all disabled). Find the official dev
  865. # commit this fork's branch is synced with (merge-base of our
  866. # branch vs official dev), then link the manager build run for
  867. # that commit. pershoot's own dev branch is intentionally ignored.
  868. retry() {
  869. local n=0
  870. until [ "$n" -ge 5 ]; do
  871. "$@" && return 0
  872. n=$((n+1))
  873. echo "retry $n/5 failed for: $*" >&2
  874. sleep 5
  875. done
  876. return 1
  877. }
  878. OFFICIAL_KSU_REPO="https://github.com/KernelSU-Next/KernelSU-Next.git"
  879. retry git fetch "$OFFICIAL_KSU_REPO" dev
  880. STOCK_COMMIT=$(git merge-base HEAD FETCH_HEAD)
  881. echo "KSU Stock Commit (official sync point): $STOCK_COMMIT"
  882. # Extract version info — anchored to the official sync point
  883. # (STOCK_COMMIT), i.e. the commit the linked manager is built from.
  884. # Mirrors pershoot c0a2c5fd: count the merge-base with origin, not
  885. # the fork's extra commits, so the version shown in the release
  886. # notes matches what the kernel reports and the manager displays.
  887. COMMITS_COUNT=$(git rev-list --count "$STOCK_COMMIT")
  888. BASE_VERSION=30000
  889. KSU_VERSION=$(expr $COMMITS_COUNT "+" $BASE_VERSION)
  890. # Extract git tag at the sync point
  891. KSU_GIT_TAG=$(git describe --tags --abbrev=0 "$STOCK_COMMIT" 2>/dev/null || echo "no-tag")
  892. MANAGER_RUN_ID=$(curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://api.github.com/repos/KernelSU-Next/KernelSU-Next/actions/workflows/build-manager-ci.yml/runs?head_sha=${STOCK_COMMIT}" | jq -r '.workflow_runs[0].id // empty')
  893. if [[ -n "${MANAGER_RUN_ID:-}" ]]; then
  894. KSU_MANAGER="https://github.com/KernelSU-Next/KernelSU-Next/actions/runs/${MANAGER_RUN_ID}"
  895. KSU_MANAGER_NOTE="built from stock dev commit ${STOCK_COMMIT:0:12} (exact match)"
  896. else
  897. # Validated up-front in resolve-sources; a release cannot proceed without
  898. # the exact manager run. Fail rather than link a mismatched manager.
  899. echo "ERROR: exact manager build no longer available for ${STOCK_COMMIT}" >&2
  900. exit 1
  901. fi
  902. echo "Commits count: $COMMITS_COUNT"
  903. echo "KSU Version: $KSU_VERSION"
  904. echo "KSU Git Tag: $KSU_GIT_TAG"
  905. echo "KSU Manager: $KSU_MANAGER"
  906. # Export as environment for next step
  907. echo "KSUN_BRANCH=$KSU_INPUT" >> $GITHUB_ENV
  908. echo "KSUN_COMMIT=$KSUN_COMMIT" >> $GITHUB_ENV
  909. echo "KSU_COMMITS_COUNT=$COMMITS_COUNT" >> $GITHUB_ENV
  910. echo "KSU_VERSION=$KSU_VERSION" >> $GITHUB_ENV
  911. echo "KSU_GIT_TAG=$KSU_GIT_TAG" >> $GITHUB_ENV
  912. echo "KSU_MANAGER=$KSU_MANAGER" >> $GITHUB_ENV
  913. echo "KSU_MANAGER_NOTE=${KSU_MANAGER_NOTE:-}" >> $GITHUB_ENV
  914. echo "MANAGER_RUN_ID=$MANAGER_RUN_ID" >> $GITHUB_ENV
  915. - name: Set release body
  916. run: |
  917. set -e
  918. RELEASE_NOTES_PATH="$RELEASE_NOTES"
  919. : > release_body.md
  920. python3 .github/scripts/render_release_body.py "$RELEASE_NOTES_PATH" > release_body.md
  921. - name: Publish release notes preview
  922. run: |
  923. {
  924. echo "## Release Notes Preview"
  925. echo
  926. cat release_body.md
  927. } >> "$GITHUB_STEP_SUMMARY"
  928. - name: Download KernelSU-Next Manager APK
  929. run: |
  930. set -euo pipefail
  931. echo "Downloading manager artifacts from run ${MANAGER_RUN_ID}"
  932. mkdir -p release-assets/manager
  933. # Official KernelSU-Next repo is public: list the build's artifacts,
  934. # download each zip, and include any APK as a release asset.
  935. curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors \
  936. "https://api.github.com/repos/KernelSU-Next/KernelSU-Next/actions/runs/${MANAGER_RUN_ID}/artifacts" \
  937. | jq -r '.artifacts[]? | .id + " " + (.name|gsub(" "; "_"))' > /tmp/mgr_arts.txt
  938. if [[ ! -s /tmp/mgr_arts.txt ]]; then
  939. echo "ERROR: no artifacts found on manager run ${MANAGER_RUN_ID}" >&2
  940. exit 1
  941. fi
  942. while read -r ART_ID ART_NAME; do
  943. echo "-- downloading ${ART_NAME}
  944. curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors \
  945. -L "https://api.github.com/repos/KernelSU-Next/KernelSU-Next/actions/artifacts/${ART_ID}/zip" \
  946. -o "/tmp/${ART_NAME}.zip"
  947. unzip -o -q "/tmp/${ART_NAME}.zip" -d "release-assets/manager/${ART_NAME}" || true
  948. done < /tmp/mgr_arts.txt
  949. # Normalize APKs so gh-release picks them up via *.apk glob
  950. find release-assets/manager -iname '*.apk' -exec cp -f {} release-assets/ \;
  951. find release-assets -maxdepth 1 -iname '*.apk' -printf 'staged: %f\n'
  952. - name: Download AnyKernel3 Artifacts
  953. uses: actions/download-artifact@v7
  954. with:
  955. path: release-assets
  956. pattern: '*-AnyKernel3'
  957. merge-multiple: false
  958. - name: Download NoMount metamodule
  959. uses: actions/download-artifact@v7
  960. with:
  961. path: release-assets
  962. pattern: '*NoMount-Metamodule'
  963. merge-multiple: false
  964. if-no-files-found: ignore
  965. - name: Zip AnyKernel3 for Release
  966. run: |
  967. set -e
  968. shopt -s nullglob
  969. for dir in release-assets/*-AnyKernel3; do
  970. zip_name="${dir}.zip"
  971. rm -f "$zip_name"
  972. cd "$dir"
  973. zip -r -q -9 "../$(basename "$zip_name")" .
  974. cd - >/dev/null
  975. done
  976. - name: Create Release
  977. uses: softprops/action-gh-release@v1
  978. with:
  979. tag_name: ${{ steps.tag.outputs.new_tag }}
  980. name: ${{ steps.release_meta.outputs.release_title }}
  981. body_path: release_body.md
  982. draft: false
  983. prerelease: ${{ inputs.release_type == 'Pre-Release' }}
  984. files: |
  985. release-assets/**/*.zip
  986. release-assets/**/*-boot*.img
  987. release-assets/*.apk