From c9380b4a6f18f58cfcd39a7f4d7f5b181e9004dd Mon Sep 17 00:00:00 2001 From: Roxana Nicolescu Date: Tue, 18 Nov 2025 16:29:06 +0100 Subject: [PATCH 01/11] net: sched: delete duplicate cleanup of backlog and qlen jira VULN-155243 cve-pre CVE-2022-50356 commit-author Zhengchao Shao commit c19d893fbf3f2f8fa864ae39652c7fee939edde2 qdisc_reset() is clearing qdisc->q.qlen and qdisc->qstats.backlog _after_ calling qdisc->ops->reset. There is no need to clear them again in the specific reset function. Signed-off-by: Zhengchao Shao Link: https://lore.kernel.org/r/20220824005231.345727-1-shaozhengchao@huawei.com Signed-off-by: Paolo Abeni (cherry picked from commit c19d893fbf3f2f8fa864ae39652c7fee939edde2) Signed-off-by: Roxana Nicolescu --- include/net/sch_generic.h | 1 - net/sched/sch_atm.c | 1 - net/sched/sch_cbq.c | 1 - net/sched/sch_choke.c | 2 -- net/sched/sch_drr.c | 2 -- net/sched/sch_dsmark.c | 2 -- net/sched/sch_etf.c | 3 --- net/sched/sch_ets.c | 2 -- net/sched/sch_fq_codel.c | 2 -- net/sched/sch_fq_pie.c | 3 --- net/sched/sch_hfsc.c | 2 -- net/sched/sch_htb.c | 2 -- net/sched/sch_multiq.c | 1 - net/sched/sch_prio.c | 2 -- net/sched/sch_qfq.c | 2 -- net/sched/sch_red.c | 2 -- net/sched/sch_sfb.c | 2 -- net/sched/sch_skbprio.c | 3 --- net/sched/sch_taprio.c | 2 -- net/sched/sch_tbf.c | 2 -- net/sched/sch_teql.c | 1 - 21 files changed, 40 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index d3d11b2b3b18c..0770eeba07a5a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -1137,7 +1137,6 @@ static inline void __qdisc_reset_queue(struct qdisc_skb_head *qh) static inline void qdisc_reset_queue(struct Qdisc *sch) { __qdisc_reset_queue(&sch->q); - sch->qstats.backlog = 0; } static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new, diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 4c8e994cf0a53..816fd0d7ba38a 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -577,7 +577,6 @@ static void atm_tc_reset(struct Qdisc *sch) pr_debug("atm_tc_reset(sch %p,[qdisc %p])\n", sch, p); list_for_each_entry(flow, &p->flows, list) qdisc_reset(flow->q); - sch->q.qlen = 0; } static void atm_tc_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 91a0dc463c482..ba99ce05cd527 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -975,7 +975,6 @@ cbq_reset(struct Qdisc *sch) cl->cpriority = cl->priority; } } - sch->q.qlen = 0; } diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 2adbd945bf15a..25d2daaa81227 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -315,8 +315,6 @@ static void choke_reset(struct Qdisc *sch) rtnl_qdisc_drop(skb, sch); } - sch->q.qlen = 0; - sch->qstats.backlog = 0; if (q->tab) memset(q->tab, 0, (q->tab_mask + 1) * sizeof(struct sk_buff *)); q->head = q->tail = 0; diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 18e4f7a0b2912..4e5b1cf11b858 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -441,8 +441,6 @@ static void drr_reset_qdisc(struct Qdisc *sch) qdisc_reset(cl->qdisc); } } - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static void drr_destroy_qdisc(struct Qdisc *sch) diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 4c100d1052699..7da6dc38a3828 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -409,8 +409,6 @@ static void dsmark_reset(struct Qdisc *sch) pr_debug("%s(sch %p,[qdisc %p])\n", __func__, sch, p); if (p->q) qdisc_reset(p->q); - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static void dsmark_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_etf.c b/net/sched/sch_etf.c index c48f91075b5c6..d96103b0e2bf5 100644 --- a/net/sched/sch_etf.c +++ b/net/sched/sch_etf.c @@ -445,9 +445,6 @@ static void etf_reset(struct Qdisc *sch) timesortedlist_clear(sch); __qdisc_reset_queue(&sch->q); - sch->qstats.backlog = 0; - sch->q.qlen = 0; - q->last = 0; } diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index 836a4b47b3879..0e3f41b0af9e7 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -731,8 +731,6 @@ static void ets_qdisc_reset(struct Qdisc *sch) } for (band = 0; band < q->nbands; band++) qdisc_reset(q->classes[band].qdisc); - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static void ets_qdisc_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 839e1235db053..23a042adb74d8 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -347,8 +347,6 @@ static void fq_codel_reset(struct Qdisc *sch) codel_vars_init(&flow->cvars); } memset(q->backlogs, 0, q->flows_cnt * sizeof(u32)); - sch->q.qlen = 0; - sch->qstats.backlog = 0; q->memory_usage = 0; } diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index d6aba6edd16e5..35c35465226bd 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -521,9 +521,6 @@ static void fq_pie_reset(struct Qdisc *sch) INIT_LIST_HEAD(&flow->flowchain); pie_vars_init(&flow->vars); } - - sch->q.qlen = 0; - sch->qstats.backlog = 0; } static void fq_pie_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 3013932683fc5..95c53df2370d4 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1514,8 +1514,6 @@ hfsc_reset_qdisc(struct Qdisc *sch) } q->eligible = RB_ROOT; qdisc_watchdog_cancel(&q->watchdog); - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static void diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 0297d3c0e56b6..e138d9f5e2ee9 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1008,8 +1008,6 @@ static void htb_reset(struct Qdisc *sch) } qdisc_watchdog_cancel(&q->watchdog); __qdisc_reset_queue(&q->direct_queue); - sch->q.qlen = 0; - sch->qstats.backlog = 0; memset(q->hlevel, 0, sizeof(q->hlevel)); memset(q->row_mask, 0, sizeof(q->row_mask)); } diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 4b7435ac0b28a..6b875f532548f 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -152,7 +152,6 @@ multiq_reset(struct Qdisc *sch) for (band = 0; band < q->bands; band++) qdisc_reset(q->queues[band]); - sch->q.qlen = 0; q->curband = 0; } diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 3b8d7197c06bf..c03a11dd990f4 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -135,8 +135,6 @@ prio_reset(struct Qdisc *sch) for (prio = 0; prio < q->bands; prio++) qdisc_reset(q->queues[prio]); - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static int prio_offload(struct Qdisc *sch, struct tc_prio_qopt *qopt) diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 175e21590372e..e591c3547b12d 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -1459,8 +1459,6 @@ static void qfq_reset_qdisc(struct Qdisc *sch) qdisc_reset(cl->qdisc); } } - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static void qfq_destroy_qdisc(struct Qdisc *sch) diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index b310842b42256..935d90874b1b7 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -178,8 +178,6 @@ static void red_reset(struct Qdisc *sch) struct red_sched_data *q = qdisc_priv(sch); qdisc_reset(q->qdisc); - sch->qstats.backlog = 0; - sch->q.qlen = 0; red_restart(&q->vars); } diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 2829455211f8c..1be8d04d69dc8 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -456,8 +456,6 @@ static void sfb_reset(struct Qdisc *sch) struct sfb_sched_data *q = qdisc_priv(sch); qdisc_reset(q->qdisc); - sch->qstats.backlog = 0; - sch->q.qlen = 0; q->slot = 0; q->double_buffering = false; sfb_zero_all_buckets(q); diff --git a/net/sched/sch_skbprio.c b/net/sched/sch_skbprio.c index 7a5e4c4547156..df72fb83d9c7d 100644 --- a/net/sched/sch_skbprio.c +++ b/net/sched/sch_skbprio.c @@ -213,9 +213,6 @@ static void skbprio_reset(struct Qdisc *sch) struct skbprio_sched_data *q = qdisc_priv(sch); int prio; - sch->qstats.backlog = 0; - sch->q.qlen = 0; - for (prio = 0; prio < SKBPRIO_MAX_PRIORITY; prio++) __skb_queue_purge(&q->qdiscs[prio]); diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 361fe1625e955..324487c3ce9dc 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -1639,8 +1639,6 @@ static void taprio_reset(struct Qdisc *sch) if (q->qdiscs[i]) qdisc_reset(q->qdiscs[i]); } - sch->qstats.backlog = 0; - sch->q.qlen = 0; } static void taprio_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 36079fdde2cb5..e031c1a41ea6b 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -330,8 +330,6 @@ static void tbf_reset(struct Qdisc *sch) struct tbf_sched_data *q = qdisc_priv(sch); qdisc_reset(q->qdisc); - sch->qstats.backlog = 0; - sch->q.qlen = 0; q->t_c = ktime_get_ns(); q->tokens = q->buffer; q->ptokens = q->mtu; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 6af6b95bdb672..79aaab51cbf5c 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -124,7 +124,6 @@ teql_reset(struct Qdisc *sch) struct teql_sched_data *dat = qdisc_priv(sch); skb_queue_purge(&dat->q); - sch->q.qlen = 0; } static void From d44d01dcd5114c5ded92e8ea89ecbcb697de0392 Mon Sep 17 00:00:00 2001 From: Roxana Nicolescu Date: Tue, 18 Nov 2025 16:30:48 +0100 Subject: [PATCH 02/11] net: sched: sfb: fix null pointer access issue when sfb_init() fails jira VULN-155243 cve CVE-2022-50356 commit-author Zhengchao Shao commit 2a3fc78210b9f0e85372a2435368962009f480fc When the default qdisc is sfb, if the qdisc of dev_queue fails to be inited during mqprio_init(), sfb_reset() is invoked to clear resources. In this case, the q->qdisc is NULL, and it will cause gpf issue. The process is as follows: qdisc_create_dflt() sfb_init() tcf_block_get() --->failed, q->qdisc is NULL ... qdisc_put() ... sfb_reset() qdisc_reset(q->qdisc) --->q->qdisc is NULL ops = qdisc->ops The following is the Call Trace information: general protection fault, probably for non-canonical address 0xdffffc0000000003: 0000 [#1] PREEMPT SMP KASAN KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f] RIP: 0010:qdisc_reset+0x2b/0x6f0 Call Trace: sfb_reset+0x37/0xd0 qdisc_reset+0xed/0x6f0 qdisc_destroy+0x82/0x4c0 qdisc_put+0x9e/0xb0 qdisc_create_dflt+0x2c3/0x4a0 mqprio_init+0xa71/0x1760 qdisc_create+0x3eb/0x1000 tc_modify_qdisc+0x408/0x1720 rtnetlink_rcv_msg+0x38e/0xac0 netlink_rcv_skb+0x12d/0x3a0 netlink_unicast+0x4a2/0x740 netlink_sendmsg+0x826/0xcc0 sock_sendmsg+0xc5/0x100 ____sys_sendmsg+0x583/0x690 ___sys_sendmsg+0xe8/0x160 __sys_sendmsg+0xbf/0x160 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 RIP: 0033:0x7f2164122d04 Fixes: e13e02a3c68d ("net_sched: SFB flow scheduler") Signed-off-by: Zhengchao Shao Signed-off-by: David S. Miller (cherry picked from commit 2a3fc78210b9f0e85372a2435368962009f480fc) Signed-off-by: Roxana Nicolescu --- net/sched/sch_sfb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 1be8d04d69dc8..0490eb5b98dee 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -455,7 +455,8 @@ static void sfb_reset(struct Qdisc *sch) { struct sfb_sched_data *q = qdisc_priv(sch); - qdisc_reset(q->qdisc); + if (likely(q->qdisc)) + qdisc_reset(q->qdisc); q->slot = 0; q->double_buffering = false; sfb_zero_all_buckets(q); From 857282572c099dcccdb1798ca371bf1ead7d3223 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 21 Oct 2025 12:02:39 +0000 Subject: [PATCH 03/11] Add automated kernel CI workflow with kselftest and PR creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements a 5-stage GitHub Actions pipeline for automated kernel testing and PR creation. Uses kernel-container-build automated-testing-v1 branch for build/test tooling. Stage 1: Build (15-30 min) - Checkout kernel source + kernel-container-build repo (automated-testing-v1) - Build kernel in CIQ builder container with kABI checking - Convert built container to QCOW2 VM image - Upload: kernel-build.log, QCOW2 image Stage 2: Boot Verification (2-5 min) - Download QCOW2 image - Boot kernel in QEMU (KVM or TCG) and validate login prompt appears - Upload: boot logs Stage 3: Kernel Selftests (40-60 min) - Download QCOW2 image - Execute comprehensive kselftests in QEMU with dual serial consoles - Upload: kselftest TAP logs, dmesg output Stage 4: Compare Results (1-2 min) Purpose: Detect test regressions by comparing against base branch Steps: 1. Checkout with full history (fetch-depth: 0) 2. Download current kselftest logs 3. Fetching base branch: - For pushes: Fetches the base branch where the patches needs to be merged using the user branch name. Example :- If user branch pushed is {shreeya}_ciqlts9_2 then ciqlts9_2 is the base branch. - For force-pushes: Doesn't do the base branch fetching again, instead uses the same base branch as detected during the first PR creation and also compares the result against that base branch. - Outputs: base_branch (reused by PR stage) 4. Download baseline logs from base branch (searches last 5 successful runs) 5. Compare results: - Counts passing/failing tests (before/after) - Fails if >±3 tests changed - Outputs: comparison_status, comparison_message Stage 5: Create Pull Request (1-2 min) Purpose: Auto-create/update PR after all tests pass Prerequisites: Only runs if build + boot + kselftest passed, no regressions detected Steps: 1. Check all stages passed and comparison_status != failed 2. Checkout (shallow: fetch-depth: 100) for commit messages 3. Download all artifacts (build/boot/test logs) 4. Extract statistics (pass/fail counts, build times) 5. Get commit info: - Single commit: Use commit message - Multiple commits: Create summary 6. Create/Update PR: - Reuses base_branch from compare-results (no duplication!) - Generate PR body with test results via create-pr-body.sh - Creates new PR or updates existing one in case of force pushes Note :- 1. To skip this github action workflow, add [ci skip] or [skip ci] into the head commit when pushing the patches. 2. For the CI to generate automatic PRs against a valid branch, one needs to have the user branch name in the format of {any_name}_basebranch. 3. Braces and base branch name in the user branch is necessary or else PR creation stage will fai}_basebranch. 3. Braces and base branch name in the user branch is necessary or else PR creation stage will fail. Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 558 ++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 .github/workflows/kernel-build-and-test-x86_64.yml diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml new file mode 100644 index 0000000000000..84599aa60ec0b --- /dev/null +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -0,0 +1,558 @@ +name: Automated kernel build and test (x86_64) +on: + push: + branches: + - '*_ciqlts9_2' + +permissions: + contents: read + actions: read + packages: read + pull-requests: write + +jobs: + build: + name: Build kernel + runs-on: kernel-build + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + + steps: + - name: Checkout kernel source + uses: actions/checkout@v4 + with: + fetch-depth: 1 + path: kernel-src-tree + + - name: Checkout kernel-container-build (test branch) + uses: actions/checkout@v4 + with: + repository: ctrliq/kernel-container-build + ref: automated-testing-v1 + path: kernel-container-build + token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + + # Host deps + KVM / FUSE validation + - name: Install host dependencies & verify KVM/FUSE + run: | + set -euxo pipefail + sudo apt-get update + sudo apt-get install -y fuse3 cpu-checker podman + sudo modprobe fuse # guarantee /dev/fuse + if ! sudo kvm-ok ; then + echo "::warning::KVM acceleration not available on this runner." + fi + if [ -e /dev/kvm ]; then + sudo chmod 0666 /dev/kvm + fi + + # Kernel build inside CIQ builder (build only, no test) + - name: Build kernel inside CIQ builder container + run: | + set -euxo pipefail + mkdir -p output + df -h + cat /proc/cpuinfo + chmod +x kernel-container-build/build-container/*.sh + podman run --rm --pull=always \ + --privileged \ + --device=/dev/fuse \ + $([ -e /dev/kvm ] && echo "--device=/dev/kvm") \ + -v "$PWD/kernel-src-tree":/src \ + -v "$PWD/output":/output \ + -v "$PWD/kernel-container-build/build-container":/usr/local/build-scripts:ro \ + -v "$PWD/kernel-container-build/container/kernel_build.sh":/usr/libexec/kernel_build.sh:ro \ + -v "$PWD/kernel-container-build/container/check_kabi.sh":/usr/libexec/check_kabi.sh:ro \ + --security-opt label=disable \ + pulp.prod.ciq.dev/ciq/cicd/lts-images/builder \ + /usr/local/build-scripts/build_kernel.sh 2>&1 | tee output/kernel-build.log + sudo dmesg + + # Upload kernel compilation logs + - name: Upload kernel compilation logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: kernel-compilation-logs-x86_64 + path: output/kernel-build.log + retention-days: 7 + + # Upload qcow2 image for next stages + - name: Upload qcow2 image + uses: actions/upload-artifact@v4 + if: always() + with: + name: kernel-qcow2-image-x86_64 + path: | + output/*.qcow2 + output/last_build_image.txt + retention-days: 7 + + boot: + name: Boot verification + runs-on: kernel-build + needs: build + + steps: + - name: Checkout kernel-container-build (test branch) + uses: actions/checkout@v4 + with: + repository: ctrliq/kernel-container-build + ref: automated-testing-v1 + path: kernel-container-build + token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + + - name: Install host dependencies + run: | + set -euxo pipefail + sudo apt-get update + sudo apt-get install -y fuse3 cpu-checker podman + sudo modprobe fuse + if [ -e /dev/kvm ]; then + sudo chmod 0666 /dev/kvm + fi + + - name: Download qcow2 image + uses: actions/download-artifact@v4 + with: + name: kernel-qcow2-image-x86_64 + path: output + + # Boot verification test + - name: Boot kernel and verify + run: | + set -euxo pipefail + chmod +x kernel-container-build/build-container/*.sh + podman run --rm --pull=always \ + --privileged \ + --device=/dev/fuse \ + $([ -e /dev/kvm ] && echo "--device=/dev/kvm") \ + -v "$PWD/output":/output \ + -v "$PWD/kernel-container-build/build-container":/usr/local/build-scripts:ro \ + --security-opt label=disable \ + pulp.prod.ciq.dev/ciq/cicd/lts-images/builder \ + /usr/local/build-scripts/boot_kernel.sh + + # Upload boot logs + - name: Upload boot logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: boot-logs-x86_64 + path: output/boot-*.log + retention-days: 7 + + test-kselftest: + name: Run kselftests + runs-on: kernel-build + needs: boot + + steps: + - name: Checkout kernel-container-build (test branch) + uses: actions/checkout@v4 + with: + repository: ctrliq/kernel-container-build + ref: automated-testing-v1 + path: kernel-container-build + token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + + - name: Install host dependencies + run: | + set -euxo pipefail + sudo apt-get update + sudo apt-get install -y fuse3 cpu-checker podman + sudo modprobe fuse + if [ -e /dev/kvm ]; then + sudo chmod 0666 /dev/kvm + fi + + - name: Download qcow2 image + uses: actions/download-artifact@v4 + with: + name: kernel-qcow2-image-x86_64 + path: output + + # Run kselftests + - name: Execute kselftests + run: | + set -euxo pipefail + chmod +x kernel-container-build/build-container/*.sh + podman run --rm --pull=always \ + --privileged \ + --device=/dev/fuse \ + $([ -e /dev/kvm ] && echo "--device=/dev/kvm") \ + -v "$PWD/output":/output \ + -v "$PWD/kernel-container-build/build-container":/usr/local/build-scripts:ro \ + --security-opt label=disable \ + pulp.prod.ciq.dev/ciq/cicd/lts-images/builder \ + /usr/local/build-scripts/test_kselftests.sh + + # Upload kselftest logs + - name: Upload kselftest logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: kselftest-logs-x86_64 + path: | + output/kselftests-*.log + output/dmesg-*.log + retention-days: 7 + + compare-results: + name: Compare with previous run + runs-on: kernel-build + needs: test-kselftest + if: success() || failure() + outputs: + base_branch: ${{ steps.base_branch.outputs.base_branch }} + comparison_status: ${{ steps.comparison.outputs.comparison_status }} + + steps: + - name: Checkout kernel source + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history needed for reliable merge-base detection + + - name: Download current kselftest logs + uses: actions/download-artifact@v4 + with: + name: kselftest-logs-x86_64 + path: output-current + + - name: Install GitHub CLI + run: | + set -euxo pipefail + # Install gh CLI if not already available + if ! command -v gh &> /dev/null; then + sudo apt-get update + sudo apt-get install -y gh + fi + + - name: Determine base branch for comparison + id: base_branch + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BASE_BRANCH="" + BRANCH_NAME="${{ github.ref_name }}" + + # Define whitelist of valid base branches + # TODO: Use a centralized place to get the base branches + VALID_BASES="ciqlts9_2 ciqlts9_4 ciqlts8_6" + + echo "Current branch: $BRANCH_NAME" + + # First, check if an open PR already exists from this head branch + echo "Checking for existing open PR from branch: $BRANCH_NAME" + EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --state open --json number,baseRefName --jq '.[0]' || echo "") + + if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then + # PR exists - use its existing base branch + BASE_BRANCH=$(echo "$EXISTING_PR" | jq -r '.baseRefName') + PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number') + echo "Found existing PR #$PR_NUMBER, using existing base: $BASE_BRANCH" + elif [ -n "${{ github.base_ref }}" ]; then + # For PRs, use the base branch directly + BASE_BRANCH="${{ github.base_ref }}" + echo "Using PR base branch: $BASE_BRANCH" + else + # Extract base branch from branch name pattern: {name}_base or {name}-base + # Match patterns like {shreeya}_ciqlts9_2 or {shreeya}-ciqlts9_2 + if [[ "$BRANCH_NAME" =~ \{[^}]+\}[_-](.+) ]]; then + EXTRACTED_BASE="${BASH_REMATCH[1]}" + echo "Extracted base branch from branch name: $EXTRACTED_BASE" + + # Validate against whitelist + if echo "$VALID_BASES" | grep -wq "$EXTRACTED_BASE"; then + BASE_BRANCH="$EXTRACTED_BASE" + echo "Base branch validated: $BASE_BRANCH" + else + echo "::error::Extracted base '$EXTRACTED_BASE' is not in whitelist: $VALID_BASES" + echo "::error::Valid base branches are: $VALID_BASES" + exit 1 + fi + else + echo "::error::Branch name does not match expected pattern {name}_base or {name}-base" + echo "::error::Branch name must be in format {name}_base or {name}-base where base is one of: $VALID_BASES" + exit 1 + fi + fi + + if [ -z "$BASE_BRANCH" ]; then + echo "::error::Could not determine base branch" + exit 1 + fi + + echo "base_branch=$BASE_BRANCH" >> $GITHUB_OUTPUT + echo "Base branch for comparison: $BASE_BRANCH" + + - name: Download baseline kselftest logs from base branch + if: steps.base_branch.outputs.base_branch != '' + uses: dawidd6/action-download-artifact@v11 + with: + workflow: kernel-build-and-test-x86_64.yml + name: kselftest-logs-x86_64 + path: output-previous + branch: ${{ steps.base_branch.outputs.base_branch }} + workflow_conclusion: success + search_artifacts: true + skip_unpack: false + if_no_artifact_found: warn + # Only search the last 5 successful runs for better performance + run_number: ${{ github.run_number }} + search_depth: 5 + continue-on-error: true + timeout-minutes: 3 + + - name: Compare test results + id: comparison + run: | + # Check if we have a base branch to compare against + if [ -z "${{ steps.base_branch.outputs.base_branch }}" ]; then + echo "::warning::No base branch found for comparison" + echo "::warning::Kselftest comparison will be skipped" + echo "comparison_status=skipped" >> $GITHUB_OUTPUT + echo "comparison_message=No base branch found - unable to determine merge target" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check if baseline logs exist + if ls output-previous/kselftests-*.log 1> /dev/null 2>&1; then + # Compare passing tests (ok) + BEFORE_PASS=$(grep -a '^ok' output-previous/kselftests-*.log | wc -l || echo "0") + AFTER_PASS=$(grep -a '^ok' output-current/kselftests-*.log | wc -l || echo "0") + + # Compare failing tests (not ok) + BEFORE_FAIL=$(grep -a '^not ok' output-previous/kselftests-*.log | wc -l || echo "0") + AFTER_FAIL=$(grep -a '^not ok' output-current/kselftests-*.log | wc -l || echo "0") + + echo "### Kselftest Comparison" + echo "Baseline (from ${{ steps.base_branch.outputs.base_branch }}): $BEFORE_PASS passing, $BEFORE_FAIL failing" + echo "Current (${{ github.ref_name }}): $AFTER_PASS passing, $AFTER_FAIL failing" + + # Calculate differences + PASS_DIFF=$((AFTER_PASS - BEFORE_PASS)) + FAIL_DIFF=$((AFTER_FAIL - BEFORE_FAIL)) + + echo "Pass difference: $PASS_DIFF" + echo "Fail difference: $FAIL_DIFF" + + # Check for regression (more than 3 tests difference) + REGRESSION=0 + + if [ $PASS_DIFF -lt -3 ]; then + echo "::error::Regression detected: $PASS_DIFF passing tests (threshold: -3)" + REGRESSION=1 + fi + + if [ $FAIL_DIFF -gt 3 ]; then + echo "::error::Regression detected: +$FAIL_DIFF failing tests (threshold: +3)" + REGRESSION=1 + fi + + if [ $REGRESSION -eq 1 ]; then + echo "::error::Test regression exceeds acceptable threshold of 3 tests" + echo "comparison_status=failed" >> $GITHUB_OUTPUT + echo "comparison_message=Regression detected: Pass diff: $PASS_DIFF, Fail diff: $FAIL_DIFF (threshold: ±3)" >> $GITHUB_OUTPUT + exit 1 + else + echo "::notice::Test results within acceptable range (threshold: ±3 tests)" + echo "comparison_status=passed" >> $GITHUB_OUTPUT + echo "comparison_message=Baseline: $BEFORE_PASS passing, $BEFORE_FAIL failing | Current: $AFTER_PASS passing, $AFTER_FAIL failing" >> $GITHUB_OUTPUT + fi + else + echo "::warning::No baseline test results found for branch ${{ steps.base_branch.outputs.base_branch }}" + echo "::notice::Cannot compare against base branch - artifacts may not exist or have expired (7-day retention)" + echo "::notice::Skipping comparison - PR will still be created with warning" + echo "comparison_status=skipped" >> $GITHUB_OUTPUT + echo "comparison_message=No baseline results available from ${{ steps.base_branch.outputs.base_branch }}" >> $GITHUB_OUTPUT + fi + create-pr: + name: Create Pull Request + runs-on: kernel-build + needs: [build, boot, test-kselftest, compare-results] + if: success() || failure() + + steps: + - name: Check if branch name contains curly brackets + run: | + BRANCH_NAME="${{ github.ref_name }}" + if [[ ! "$BRANCH_NAME" =~ \{ ]] || [[ ! "$BRANCH_NAME" =~ \} ]]; then + echo "Branch name '$BRANCH_NAME' does not contain curly brackets, skipping PR creation" + exit 1 + fi + echo "Branch name contains curly brackets, proceeding with PR creation checks" + + - name: Check if tests passed and no regressions + run: | + # Skip PR if any test stage failed + if [ "${{ needs.build.result }}" != "success" ] || \ + [ "${{ needs.boot.result }}" != "success" ] || \ + [ "${{ needs.test-kselftest.result }}" != "success" ]; then + echo "One or more test stages failed, skipping PR creation" + exit 1 + fi + + # Skip PR if regression was detected (but allow if comparison was skipped/unavailable) + if [ "${{ needs.compare-results.outputs.comparison_status }}" = "failed" ]; then + echo "Test regression detected, skipping PR creation" + exit 1 + fi + + echo "All test stages passed and no regressions detected, proceeding with PR creation" + + - name: Checkout kernel source + uses: actions/checkout@v4 + with: + fetch-depth: 100 # Fetch more history for commit counting + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Fetch base branch for commit comparison + run: | + BASE_BRANCH="${{ needs.compare-results.outputs.base_branch }}" + if [ -n "$BASE_BRANCH" ]; then + # Fetch base branch with enough history to find common ancestor + git fetch --depth=200 origin "$BASE_BRANCH:refs/remotes/origin/$BASE_BRANCH" || true + echo "Fetched base branch: $BASE_BRANCH" + fi + + - name: Download kernel compilation logs + uses: actions/download-artifact@v4 + with: + name: kernel-compilation-logs-x86_64 + path: artifacts/build + + - name: Download boot logs + uses: actions/download-artifact@v4 + with: + name: boot-logs-x86_64 + path: artifacts/boot + + - name: Download kselftest logs + uses: actions/download-artifact@v4 + with: + name: kselftest-logs-x86_64 + path: artifacts/test + + - name: Extract test statistics + id: stats + run: | + PASSED=$(grep -a '^ok' artifacts/test/kselftests-*.log | wc -l || echo "0") + FAILED=$(grep -a '^not ok' artifacts/test/kselftests-*.log | wc -l || echo "0") + echo "passed=$PASSED" >> $GITHUB_OUTPUT + echo "failed=$FAILED" >> $GITHUB_OUTPUT + + - name: Extract build timers + id: build_info + run: | + BUILD_TIME=$(grep -oP '\[TIMER\]\{BUILD\}:\s*\K[0-9]+' artifacts/build/kernel-build.log | head -1 || echo "N/A") + TOTAL_TIME=$(grep -oP '\[TIMER\]\{TOTAL\}\s*\K[0-9]+' artifacts/build/kernel-build.log | head -1 || echo "N/A") + echo "build_time=${BUILD_TIME}s" >> $GITHUB_OUTPUT + echo "total_time=${TOTAL_TIME}s" >> $GITHUB_OUTPUT + + - name: Get commit information + id: commit_msg + run: | + # Use the base branch determined by compare-results stage + BASE_BRANCH="${{ needs.compare-results.outputs.base_branch }}" + + if [ -z "$BASE_BRANCH" ]; then + echo "::error::Base branch not determined by compare-results stage" + exit 1 + fi + + if ! git rev-parse origin/$BASE_BRANCH >/dev/null 2>&1; then + echo "::error::Base branch origin/$BASE_BRANCH does not exist" + exit 1 + fi + + COMMIT_COUNT=$(git rev-list --count origin/$BASE_BRANCH..HEAD 2>/dev/null || echo "1") + + if [ "$COMMIT_COUNT" -eq "1" ]; then + # Single commit: use commit subject + git log -1 --pretty=%s > /tmp/commit_subject.txt + COMMIT_SUBJECT=$(cat /tmp/commit_subject.txt) + echo "commit_subject=$COMMIT_SUBJECT" >> $GITHUB_OUTPUT + else + # Multiple commits: create summary + echo "commit_subject=Multiple patches tested ($COMMIT_COUNT commits)" >> $GITHUB_OUTPUT + fi + + # Get all commit messages and save to file (in reverse order) + for commit in $(git log origin/$BASE_BRANCH..HEAD --format=%h | tac); do + git log -1 $commit --format=%B | awk 'BEGIN{print "```"} /^$/{empty++} empty==2{exit} {print} END{print "```"}' >> /tmp/commit_message.txt + done + + - name: Create Pull Request + env: + GH_TOKEN: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + run: | + # Reuse base branch from compare-results stage (already computed) + BASE_BRANCH="${{ needs.compare-results.outputs.base_branch }}" + + if [ -z "$BASE_BRANCH" ]; then + echo "ERROR: Could not determine base branch for PR (compare-results did not find one)" + exit 1 + fi + + echo "Creating/updating PR from ${{ github.ref_name }} to $BASE_BRANCH" + + # Determine comparison status message + COMPARISON_STATUS="${{ needs.compare-results.outputs.comparison_status }}" + if [ "$COMPARISON_STATUS" = "passed" ]; then + COMPARISON_SECTION="### ✅ Test Comparison + - Status: Passed - Within acceptable threshold (±3 tests) + - Compared against: $BASE_BRANCH" + elif [ "$COMPARISON_STATUS" = "skipped" ]; then + COMPARISON_SECTION="### ⚠️ Test Comparison + - Status: Skipped + - Reason: No baseline test results available from $BASE_BRANCH + - **Note:** Manual review recommended to ensure no regressions" + else + COMPARISON_SECTION="### ❌ Test Comparison + - Status: Failed - Regression detected + - Compared against: $BASE_BRANCH + - **Action Required:** Review test differences before merging" + fi + + # Create PR body using script + chmod +x .github/scripts/create-pr-body.sh + .github/scripts/create-pr-body.sh \ + "${{ steps.build_info.outputs.build_time }}" \ + "${{ steps.build_info.outputs.total_time }}" \ + "${{ steps.stats.outputs.passed }}" \ + "${{ steps.stats.outputs.failed }}" \ + "${{ github.run_id }}" \ + "$COMPARISON_SECTION" \ + "${{ github.repository }}" \ + "/tmp/commit_message.txt" \ + > pr_body.md + + # Check if any open PR already exists from this head branch (regardless of base) + EXISTING_PR=$(gh pr list --head "${{ github.ref_name }}" --state open --json number,baseRefName --jq '.[0]' || echo "") + + if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then + PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number') + CURRENT_BASE=$(echo "$EXISTING_PR" | jq -r '.baseRefName') + + echo "Found existing PR #$PR_NUMBER (current base: $CURRENT_BASE)" + + # Update PR title and body + gh pr edit "$PR_NUMBER" \ + --title "[$BASE_BRANCH] ${{ steps.commit_msg.outputs.commit_subject }}" \ + --body-file pr_body.md + + echo "Updated PR #$PR_NUMBER" + + # Note: We don't change the base branch even if it differs from $BASE_BRANCH + # because compare-results already used the existing PR's base for comparison + if [ "$CURRENT_BASE" != "$BASE_BRANCH" ]; then + echo "::notice::PR base remains $CURRENT_BASE (comparison was done against this base)" + fi + else + echo "Creating new PR from ${{ github.ref_name }} to $BASE_BRANCH" + gh pr create \ + --base "$BASE_BRANCH" \ + --head "${{ github.ref_name }}" \ + --title "[$BASE_BRANCH] ${{ steps.commit_msg.outputs.commit_subject }}" \ + --body-file pr_body.md + fi From 39725c895f21f0341480576eb82c12a87455d51a Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 21 Oct 2025 12:02:49 +0000 Subject: [PATCH 04/11] Add PR body generation script Script to generate detailed PR descriptions with kselftest results. Signed-off-by: Shreeya Patel --- .github/scripts/create-pr-body.sh | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 .github/scripts/create-pr-body.sh diff --git a/.github/scripts/create-pr-body.sh b/.github/scripts/create-pr-body.sh new file mode 100755 index 0000000000000..04fe61ac6fa50 --- /dev/null +++ b/.github/scripts/create-pr-body.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Script to create PR body +# Arguments: build_time total_time passed failed run_id comparison_section repo commit_message_file + +set -euo pipefail + +# Check number of arguments +if [ $# -lt 7 ] || [ $# -gt 8 ]; then + echo "Error: Expected 7 or 8 arguments, got $#" >&2 + echo "Usage: $0 [commit_message_file]" >&2 + exit 1 +fi + +BUILD_TIME="$1" +TOTAL_TIME="$2" +PASSED="$3" +FAILED="$4" +RUN_ID="$5" +COMPARISON_SECTION="$6" +REPO="$7" +COMMIT_MESSAGE_FILE="${8:-/tmp/commit_message.txt}" + +# Validate required arguments are not empty +if [ -z "$BUILD_TIME" ] || [ -z "$TOTAL_TIME" ] || [ -z "$PASSED" ] || [ -z "$FAILED" ] || [ -z "$RUN_ID" ] || [ -z "$COMPARISON_SECTION" ] || [ -z "$REPO" ]; then + echo "Error: One or more required arguments are empty" >&2 + echo "Usage: $0 [commit_message_file]" >&2 + exit 1 +fi + +# Check if commit message file exists +if [ ! -f "$COMMIT_MESSAGE_FILE" ]; then + echo "Error: Commit message file not found: $COMMIT_MESSAGE_FILE" >&2 + exit 1 +fi + +# Convert seconds to minutes for better readability +convert_time() { + local seconds="${1%s}" # Remove 's' suffix if present + local minutes=$((seconds / 60)) + local remaining_seconds=$((seconds % 60)) + echo "${minutes}m ${remaining_seconds}s" +} + +BUILD_TIME_READABLE=$(convert_time "$BUILD_TIME") +TOTAL_TIME_READABLE=$(convert_time "$TOTAL_TIME") + +cat << EOF +## Summary +This PR has been automatically created after successful completion of all CI stages. + +## Commit Message(s) + +EOF + +cat "$COMMIT_MESSAGE_FILE" +echo "" + +cat << EOF + +## Test Results + +### ✅ Build Stage +- Status: Passed +- Build Time: ${BUILD_TIME_READABLE} +- Total Time: ${TOTAL_TIME_READABLE} +- [View build logs](https://github.com/${REPO}/actions/runs/${RUN_ID}) + +### ✅ Boot Verification +- Status: Passed +- [View boot logs](https://github.com/${REPO}/actions/runs/${RUN_ID}) + +### ✅ Kernel Selftests +- **Passed:** ${PASSED} +- **Failed:** ${FAILED} +- [View kselftest logs](https://github.com/${REPO}/actions/runs/${RUN_ID}) + +${COMPARISON_SECTION} + +--- +🤖 This PR was automatically generated by GitHub Actions +Run ID: ${RUN_ID} +EOF From d6b9cebf7130cddfc34d7cf286ae63bdd0a0e63f Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 25 Nov 2025 19:45:38 +0000 Subject: [PATCH 05/11] github actions: Fix for triggering this workflow from specific branches Signed-off-by: Shreeya Patel --- .github/workflows/kernel-build-and-test-x86_64.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 84599aa60ec0b..0c931453aac93 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -1,8 +1,7 @@ name: Automated kernel build and test (x86_64) + on: - push: - branches: - - '*_ciqlts9_2' + workflow_call: permissions: contents: read From 7bd91bc86d65c05ab53f2fd0c1c9238c26be97a9 Mon Sep 17 00:00:00 2001 From: Roxana Nicolescu Date: Wed, 26 Nov 2025 14:00:09 +0100 Subject: [PATCH 06/11] github actions: Fetch the github scripts from main Otherwise, creating the pull request fails because the scripts are not present in that specific branch. Signed-off-by: Roxana Nicolescu --- .github/workflows/kernel-build-and-test-x86_64.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 0c931453aac93..eb9e075ea373d 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -481,6 +481,12 @@ jobs: git log -1 $commit --format=%B | awk 'BEGIN{print "```"} /^$/{empty++} empty==2{exit} {print} END{print "```"}' >> /tmp/commit_message.txt done + - name: Fetch PR body script from main + run: | + git fetch origin main:main + git checkout origin/main -- .github/scripts/create-pr-body.sh + chmod +x .github/scripts/create-pr-body.sh + - name: Create Pull Request env: GH_TOKEN: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} @@ -514,7 +520,6 @@ jobs: fi # Create PR body using script - chmod +x .github/scripts/create-pr-body.sh .github/scripts/create-pr-body.sh \ "${{ steps.build_info.outputs.build_time }}" \ "${{ steps.build_info.outputs.total_time }}" \ From 77b6760335d384afb078ea1be5f1ec142c3e2c78 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Wed, 3 Dec 2025 13:14:15 +0000 Subject: [PATCH 07/11] github actions: Use Github APP authentication instead of personal tokens PRs that were created by this workflow were using my name due to the personal github token being used here. Use the latest Github App Authentication method set up by TJ for us. Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index eb9e075ea373d..3a58da202c0c9 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -16,6 +16,13 @@ jobs: if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" steps: + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout kernel source uses: actions/checkout@v4 with: @@ -28,7 +35,7 @@ jobs: repository: ctrliq/kernel-container-build ref: automated-testing-v1 path: kernel-container-build - token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + token: ${{ steps.generate_token.outputs.token }} # Host deps + KVM / FUSE validation - name: Install host dependencies & verify KVM/FUSE @@ -92,13 +99,20 @@ jobs: needs: build steps: + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout kernel-container-build (test branch) uses: actions/checkout@v4 with: repository: ctrliq/kernel-container-build ref: automated-testing-v1 path: kernel-container-build - token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + token: ${{ steps.generate_token.outputs.token }} - name: Install host dependencies run: | @@ -146,13 +160,20 @@ jobs: needs: boot steps: + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout kernel-container-build (test branch) uses: actions/checkout@v4 with: repository: ctrliq/kernel-container-build ref: automated-testing-v1 path: kernel-container-build - token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + token: ${{ steps.generate_token.outputs.token }} - name: Install host dependencies run: | @@ -226,10 +247,17 @@ jobs: sudo apt-get install -y gh fi + - name: Generate GitHub App token for comparison + id: generate_token_compare + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Determine base branch for comparison id: base_branch env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate_token_compare.outputs.token }} run: | BASE_BRANCH="" BRANCH_NAME="${{ github.ref_name }}" @@ -487,9 +515,16 @@ jobs: git checkout origin/main -- .github/scripts/create-pr-body.sh chmod +x .github/scripts/create-pr-body.sh + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Create Pull Request env: - GH_TOKEN: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} + GH_TOKEN: ${{ steps.generate_token.outputs.token }} run: | # Reuse base branch from compare-results stage (already computed) BASE_BRANCH="${{ needs.compare-results.outputs.base_branch }}" From 53bad780cc2b2ebfd1946d1f2beb2112d22c6320 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Fri, 5 Dec 2025 16:06:15 +0000 Subject: [PATCH 08/11] Add secrets declaration to reusable workflow for GitHub App auth --- .github/workflows/kernel-build-and-test-x86_64.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 3a58da202c0c9..9f89e99f459fb 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -2,6 +2,11 @@ name: Automated kernel build and test (x86_64) on: workflow_call: + secrets: + APP_ID: + required: true + APP_PRIVATE_KEY: + required: true permissions: contents: read From 104392200637de59d5cbfff79ac31428e5ab6372 Mon Sep 17 00:00:00 2001 From: "T.J. Gohl" Date: Fri, 5 Dec 2025 12:57:31 -0500 Subject: [PATCH 09/11] Fix GitHub App token scope for internal repository access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow was failing because the GitHub App token generated by actions/create-github-app-token@v1 was only scoped to the current repository (kernel-src-tree) by default. When it tried to checkout kernel-container-build (which has INTERNAL visibility), the token didn't have access, resulting in "Repository not found" errors. Added explicit repositories parameter to all token generation steps to grant access to all required repositories: - kernel-tools - kernel-src-tree - kernel-src-tree-tools - kernel-container-build This fix applies to all 5 token generation steps in the workflow: - build job - boot job - test-kselftest job - compare-results job - create-pr job Fixes: https://github.com/ctrliq/kernel-src-tree/actions/runs/19970287958 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/kernel-build-and-test-x86_64.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 9f89e99f459fb..ba3c6e457b00f 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -27,6 +27,8 @@ jobs: with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + repositories: | + kernel-container-build - name: Checkout kernel source uses: actions/checkout@v4 @@ -110,6 +112,8 @@ jobs: with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + repositories: | + kernel-container-build - name: Checkout kernel-container-build (test branch) uses: actions/checkout@v4 @@ -171,6 +175,8 @@ jobs: with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + repositories: | + kernel-container-build - name: Checkout kernel-container-build (test branch) uses: actions/checkout@v4 @@ -258,6 +264,9 @@ jobs: with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + repositories: | + kernel-src-tree + kernel-container-build - name: Determine base branch for comparison id: base_branch @@ -526,6 +535,9 @@ jobs: with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + repositories: | + kernel-src-tree + kernel-container-build - name: Create Pull Request env: From 2cc839a8de18021c38379611b6ad64f6a27c7962 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Mon, 8 Dec 2025 14:08:40 +0000 Subject: [PATCH 10/11] Fix the kselftest comparison step Signed-off-by: Shreeya Patel --- .github/workflows/kernel-build-and-test-x86_64.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index ba3c6e457b00f..b39b78fcb9492 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -338,8 +338,7 @@ jobs: search_artifacts: true skip_unpack: false if_no_artifact_found: warn - # Only search the last 5 successful runs for better performance - run_number: ${{ github.run_number }} + # Search the last 5 successful runs for better performance search_depth: 5 continue-on-error: true timeout-minutes: 3 From 34fc6034c452b9e226abf90a6ef948ab8f4af525 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 9 Dec 2025 12:28:06 +0000 Subject: [PATCH 11/11] Fix kselftest comparison step Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 76 +++++++++++++++---- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index b39b78fcb9492..d34cf7d2d4b85 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -326,20 +326,65 @@ jobs: echo "base_branch=$BASE_BRANCH" >> $GITHUB_OUTPUT echo "Base branch for comparison: $BASE_BRANCH" - - name: Download baseline kselftest logs from base branch + - name: Download baseline kselftest logs from last successful run targeting same base if: steps.base_branch.outputs.base_branch != '' - uses: dawidd6/action-download-artifact@v11 - with: - workflow: kernel-build-and-test-x86_64.yml - name: kselftest-logs-x86_64 - path: output-previous - branch: ${{ steps.base_branch.outputs.base_branch }} - workflow_conclusion: success - search_artifacts: true - skip_unpack: false - if_no_artifact_found: warn - # Search the last 5 successful runs for better performance - search_depth: 5 + env: + GH_TOKEN: ${{ steps.generate_token_compare.outputs.token }} + run: | + set +e # Don't exit on error, we handle it with continue-on-error + BASE_BRANCH="${{ steps.base_branch.outputs.base_branch }}" + CURRENT_RUN_ID="${{ github.run_id }}" + + echo "Searching for baseline from last successful run targeting base branch: $BASE_BRANCH" + echo "Current run ID: $CURRENT_RUN_ID (will be excluded from search)" + + # Get last 50 successful workflow runs (cast a wider net to find PRs targeting this base) + # We need to check each run to see if it targets the same base branch + SUCCESSFUL_RUNS=$(gh run list \ + --workflow kernel-build-and-test-x86_64.yml \ + --status success \ + --limit 50 \ + --json databaseId,headBranch,createdAt) + + if [ -z "$SUCCESSFUL_RUNS" ] || [ "$SUCCESSFUL_RUNS" = "[]" ]; then + echo "::warning::No successful workflow runs found" + exit 0 + fi + + # Parse runs and check each one's base branch by examining branch name pattern + echo "$SUCCESSFUL_RUNS" | jq -c '.[]' | while read -r run; do + RUN_ID=$(echo "$run" | jq -r '.databaseId') + HEAD_BRANCH=$(echo "$run" | jq -r '.headBranch') + + # Skip current run + if [ "$RUN_ID" = "$CURRENT_RUN_ID" ]; then + continue + fi + + # Extract base from branch name pattern {name}_base or {name}-base + EXTRACTED_BASE="" + if [[ "$HEAD_BRANCH" =~ \{[^}]+\}[_-](.+) ]]; then + EXTRACTED_BASE="${BASH_REMATCH[1]}" + fi + + # Check if this run targets the same base branch + if [ "$EXTRACTED_BASE" = "$BASE_BRANCH" ]; then + echo "Found candidate run $RUN_ID from branch $HEAD_BRANCH (targets: $BASE_BRANCH)" + + # Try to download artifact from this run + if gh run download "$RUN_ID" --name kselftest-logs-x86_64 --dir output-previous 2>/dev/null; then + echo "Successfully downloaded baseline from run $RUN_ID (branch: $HEAD_BRANCH)" + echo "BASELINE_RUN_ID=$RUN_ID" >> $GITHUB_ENV + echo "BASELINE_BRANCH=$HEAD_BRANCH" >> $GITHUB_ENV + exit 0 + else + echo "Run $RUN_ID has no kselftest artifacts or they expired" + fi + fi + done + + echo "::warning::No baseline test results found in recent successful runs targeting $BASE_BRANCH" + echo "::notice::This may be the first run targeting this base branch, or artifacts have expired (7-day retention)" continue-on-error: true timeout-minutes: 3 @@ -357,6 +402,9 @@ jobs: # Check if baseline logs exist if ls output-previous/kselftests-*.log 1> /dev/null 2>&1; then + # Get baseline source info + BASELINE_SOURCE="${BASELINE_BRANCH:-last successful run}" + # Compare passing tests (ok) BEFORE_PASS=$(grep -a '^ok' output-previous/kselftests-*.log | wc -l || echo "0") AFTER_PASS=$(grep -a '^ok' output-current/kselftests-*.log | wc -l || echo "0") @@ -366,7 +414,7 @@ jobs: AFTER_FAIL=$(grep -a '^not ok' output-current/kselftests-*.log | wc -l || echo "0") echo "### Kselftest Comparison" - echo "Baseline (from ${{ steps.base_branch.outputs.base_branch }}): $BEFORE_PASS passing, $BEFORE_FAIL failing" + echo "Baseline (from $BASELINE_SOURCE targeting ${{ steps.base_branch.outputs.base_branch }}): $BEFORE_PASS passing, $BEFORE_FAIL failing" echo "Current (${{ github.ref_name }}): $AFTER_PASS passing, $AFTER_FAIL failing" # Calculate differences