已关闭
[sync] PR-2768: build: Add self-service EulerMaker EBS cloud build & local manual build #2771
openeuler-ci-bot创建于 8月14日关闭于 17 天前
[sync] PR-2768: build: Add self-service EulerMaker EBS cloud build & local manual build #2771
已关闭
openeuler-ci-bot创建于 8月14日关闭于 17 天前
共 8 个文件变更+922-12
MREADME.md+65-6
@@ -2,22 +2,81 @@
2 2 
3[中文版](README_cn.md)3[中文版](README_cn.md)
4 4 
5-RPM packaging repository for the openEuler kernel. It builds openEuler kernel RPM packages from Linux kernel source, out-of-tree patches, and per-architecture kernel configs.5+RPM packaging repository for the openEuler kernel. It builds openEuler kernel RPM
6+packages from Linux kernel source, out-of-tree patches, and per-architecture
7+kernel configs.
6 8 
7## Layout9## Layout
8 10 
9- `kernel.spec` — RPM spec for building the kernel package11- `kernel.spec` — RPM spec for building the kernel package
10-- `kernel-aarch64.config` / `kernel-x86_64.config` / `kernel-riscv64.config` — per-architecture kernel configs12+- `kernel-aarch64.config` / `kernel-x86_64.config` / `kernel-riscv64.config`
11-- `scripts/clean-config` — strips toolchain auto-detected options from a kernel `.config`13+ — per-architecture kernel configs
14+- `scripts/clean-config` — strips toolchain auto-detected options from
15+ a kernel `.config`
16+- `scripts/ebs-build.sh` — self-service cloud build helper for EulerMaker EBS
17+- `scripts/gen-source-tarball.sh` — generates `kernel.tar.gz` from the tag in
18+ `SOURCE` (for local builds; EulerMaker does this automatically)
19+- `ebs-build.conf.example` — template for `scripts/ebs-build.sh` version/branch
20+ settings; on first run the script copies it into a local (gitignored)
21+ `ebs-build.conf` for you to edit
22+- `docs/ebs-build.md` — newcomer guide for cloud building via EBS
12- `SOURCE` — openEuler kernel tag (from https://atomgit.com/openeuler/kernel)23- `SOURCE` — openEuler kernel tag (from https://atomgit.com/openeuler/kernel)
13 24 
14-## Build25+## Build locally
26+ 
27+The kernel source tarball `kernel.tar.gz` is not stored in this repository —
28+the openEuler build system (EulerMaker) generates it from the tag in `SOURCE`
29+at build time. For a local manual build you need to generate it yourself:
30+ 
31+1. Set up an openEuler environment (container or host) with the `BuildRequires`
32+ dependencies from `kernel.spec`.
33+2. Tell the script where the kernel source lives. In your local (gitignored)
34+ `ebs-build.conf` add a line like `KERNEL_REPO=~/git/openeuler/kernel` (a path
35+ or a git URL), or override per-invocation with `KERNEL_REPO=...` — no need to
36+ edit your shell rc files.
37+3. Generate the source tarball from the `SOURCE` tag:
38+ 
39+ ```bash
40+ ./scripts/gen-source-tarball.sh
41+ ```
42+ 
43+4. Build:
44+ 
45+ ```bash
46+ rpmbuild -ba kernel.spec
47+ ```
48+ 
49+## Build on EulerMaker EBS (cloud)
50+ 
51+EBS (EulerMaker) is openEuler's cloud build service: it builds the kernel for
52+both aarch64 and x86_64, using the kernel version from the tag in `SOURCE`.
53+ 
54+It builds from the remote repo/branch set in the local `ebs-build.conf`
55+(`SPEC_URL` / `SPEC_BRANCH`) — e.g. the official `openEuler-26.09-DevStation`
56+branch. EBS pulls the spec and configs from that remote branch, so **push your
57+changes there first**; your local checkout is not built. The spec repo must be
58+named `kernel` — EBS only auto-generates `Source0: kernel.tar.gz` for a repo
59+named exactly that (details in docs/ebs-build.md).
60+ 
61+No admin role needed — each developer creates their own inherited sub-project.
62+Quick start:
15 63 
16```bash64```bash
17-rpmbuild -ba kernel.spec65+./scripts/ebs-build.sh --install-ccb # one-time: install ccb client
66+./scripts/ebs-build.sh --config # one-time: set account (password stays in env)
67+./scripts/ebs-build.sh --setup # one-time: create sub-project + kernel repo + snapshot
68+./scripts/ebs-build.sh build # trigger build and poll until both arches finish
69+./scripts/ebs-build.sh log <job_id> --check # fetch log and grep for errors
70+./scripts/ebs-build.sh repo-url # print repo dir URL of last successful build
18```71```
19 72 
20-The openEuler build system (EulerMaker) builds from this repository automatically. In a normal build it generates the kernel source tarball (`kernel.tar.gz`) from the tag recorded in `SOURCE`, so the tarball is not stored in this repository.73+The kernel repo, branch and inherited official project are set in a local
74+`ebs-build.conf`; on first run the script auto-copies the committed template
75+`ebs-build.conf.example` into it. Edit that local file (not the script or
76+template) for your version. Environment variables override it for one-off
77+changes.
78+ 
79+Full guide: [docs/ebs-build.md](docs/ebs-build.md)
21 80 
22## Reference81## Reference
23 82 
@@ -2,22 +2,72 @@
2 2 
3[English](README.md)3[English](README.md)
4 4 
5-openEuler 内核 RPM 打包仓库。基于 Linux 内核源码、out-of-tree 补丁和按架构划分的内核配置构建 openEuler 内核 RPM 包。5+openEuler 内核 RPM 打包仓库。基于 Linux 内核源码、out-of-tree 补丁和按架构划分的
6+内核配置构建 openEuler 内核 RPM 包。
6 7 
7## 目录结构8## 目录结构
8 9 
9- `kernel.spec` — 内核包的 RPM spec10- `kernel.spec` — 内核包的 RPM spec
10-- `kernel-aarch64.config` / `kernel-x86_64.config` / `kernel-riscv64.config` — 各架构内核配置11+- `kernel-aarch64.config` / `kernel-x86_64.config` / `kernel-riscv64.config`
12+ — 各架构内核配置
11- `scripts/clean-config` — 从内核 `.config` 中剥离工具链自动检测选项13- `scripts/clean-config` — 从内核 `.config` 中剥离工具链自动检测选项
14+- `scripts/ebs-build.sh` — EulerMaker EBS 自助云端构建脚本
15+- `scripts/gen-source-tarball.sh` — 根据 `SOURCE` 中的 tag 生成 `kernel.tar.gz`
16+ (本地构建用;EBS 构建会自动生成)
17+- `ebs-build.conf.example` — `scripts/ebs-build.sh` 的版本/分支配置模板;首次运行
18+ 会自动复制成本地 `ebs-build.conf`(gitignore,不入库)供你编辑
19+- `docs/ebs-build.md` — EBS 云端构建新手指南
12- `SOURCE` — openEuler 内核 tag(来自 https://atomgit.com/openeuler/kernel)20- `SOURCE` — openEuler 内核 tag(来自 https://atomgit.com/openeuler/kernel)
13 21 
14-## 构建22+## 本地构建
23+ 
24+内核源码 tarball `kernel.tar.gz` 不存放在本仓库——openEuler 构建系统(EulerMaker)
25+在构建时按 `SOURCE` 中的 tag 自动生成。本地手动构建需要自己生成:
26+ 
27+1. 准备 openEuler 开发环境(容器或主机),装好 `kernel.spec` 中 `BuildRequires`
28+ 声明的依赖。
29+2. 告诉脚本内核源码在哪里:在本地(gitignore)`ebs-build.conf` 里加一行
30+ `KERNEL_REPO=~/git/openeuler/kernel`(路径或 git URL),或临时用
31+ `KERNEL_REPO=...` 环境变量覆盖——无需改 shell rc 配置。
32+3. 按 `SOURCE` 的 tag 生成源码包:
33+ 
34+ ```bash
35+ ./scripts/gen-source-tarball.sh
36+ ```
37+ 
38+4. 构建:
39+ 
40+ ```bash
41+ rpmbuild -ba kernel.spec
42+ ```
43+ 
44+## EulerMaker EBS 云端构建
45+ 
46+EBS(EulerMaker)是 openEuler 的云端构建服务:按 `SOURCE` 里的内核 tag 对应的
47+版本,同时构建 aarch64 和 x86_64 内核。
48+ 
49+构建对象是本地 `ebs-build.conf` 里 `SPEC_URL` / `SPEC_BRANCH` 指定的**远程
50+仓库分支**(如官方 `openEuler-26.09-DevStation` 分支)。EBS 拉取的是远程分支
51+上的 spec 与配置,所以**修改必须先 push 到该远程分支**,本地工作区不会被构建。
52+spec 仓库名必须叫 `kernel`——EBS 只对名为 kernel 的仓库自动生成
53+`Source0: kernel.tar.gz`(详见 docs/ebs-build.md)。
54+ 
55+无需向管理员申请角色——每个开发者创建自己的继承子工程即可。快速上手:
15 56 
16```bash57```bash
17-rpmbuild -ba kernel.spec58+./scripts/ebs-build.sh --install-ccb # 一次性:安装 ccb 客户端
59+./scripts/ebs-build.sh --config # 一次性:配置账号(密码仅存环境变量)
60+./scripts/ebs-build.sh --setup # 一次性:创建子工程 + kernel 包源 + 快照
61+./scripts/ebs-build.sh build # 触发构建并轮询至两架构完成
62+./scripts/ebs-build.sh log <job_id> --check # 拉取日志并 grep 错误
63+./scripts/ebs-build.sh repo-url # 显示最后一次成功构建的产物仓库 URL
18```64```
19 65 
20-openEuler 构建系统(EulerMaker)会自动基于本仓库进行构建。正常构建时,EulerMaker 会根据 `SOURCE` 中记录的 tag 自动生成内核源码包(`kernel.tar.gz`),因此该 tarball 不存放在本仓库中。66+版本/分支(kernel 包源、分支、继承的官方工程)在本地 `ebs-build.conf` 里配置(首次
67+运行自动从模板 `ebs-build.conf.example` 复制),换版本改它即可,不用改脚本;临时覆盖
68+可用环境变量。
69+ 
70+完整指南:[docs/ebs-build.md](docs/ebs-build.md)
21 71 
22## 参考72## 参考
23 73 
@@ -0,0 +1,180 @@
1+# 用 EulerMaker EBS 自助构建内核
2+ 
3+EBS(EulerMaker)是 openEuler 的云端包构建服务。它构建的对象是本地
4+`ebs-build.conf` 里 `SPEC_URL` / `SPEC_BRANCH` 指定的**远程仓库分支**
5+(本仓库默认 `openEuler-26.09-DevStation`),按 `SOURCE` 的 tag 取内核版本,
6+同时产出 aarch64 / x86_64。EBS 拉取的是远程分支内容,**改动要先 push 到该分支**。
7+ 
8+本指南面向**首次接触 EulerMaker / EBS 的开发者**:按下面的清单操作,即可在云端
9+构建 openEuler 内核,无需向任何管理员申请权限。
10+ 
11+一切操作由仓库里的 [`scripts/ebs-build.sh`](../scripts/ebs-build.sh) 封装,只依赖
12+bash + curl + grep + ruby(仅安装 ccb 时需要 ruby/gem)。
13+ 
14+---
15+ 
16+## 新手清单
17+ 
18+| 步骤 | 命令 | 频率 | 说明 |
19+|------|------|------|------|
20+| 1. 注册账号 | 浏览器访问 https://eulermaker.openeuler.openatom.cn 注册 | 一次性 | 社区账号,构建需要 |
21+| 2. 安装 ccb | `./scripts/ebs-build.sh --install-ccb` | 一次性 | 装到 `~/.local`,自动检测 ruby/gem 依赖 |
22+| 3. 配置账号 | `./scripts/ebs-build.sh --config` | 一次性 | 生成 `~/.config/cli/defaults/config.yaml`,**密码不进文件** |
23+| 4. 准备工程 | `./scripts/ebs-build.sh --setup` | 一次性 / 改配置后重跑 | 首次运行自动生成本地 `ebs-build.conf`;创建你的私有继承子工程 + 配置好 kernel 包源 + 打快照 |
24+| 5. 触发构建 | `./scripts/ebs-build.sh build` | 每次 | 双架构(aarch64/x86_64)构建并自动轮询到完成 |
25+| 6. 查日志 | `./scripts/ebs-build.sh log <job_id> --check` | 构建失败时 | 拉取完整日志,自动 grep 真实错误与签名状态 |
26+ 
27+> 首次运行时如果提示需要登录,先执行 `export PASSWORD='你的社区密码'`(只存在于
28+> 本次会话环境变量,不会写入任何文件),再重跑对应命令。
29+ 
30+---
31+ 
32+## 关键概念:继承子工程,无需找管理员
33+ 
34+以当前 openEuler 版本为例,EBS 上有个官方工程
35+`openEuler-26.09-DevStation:everything`,负责该版本 DevStation 分支全部包的构建。
36+ 
37+你**不需要**成为它的维护者。`--setup` 会在它下面创建一个**你自己的继承子工程**
38+(名字默认 = `你的账号` + 配置文件里的 `PROJECT_SUFFIX`,当前版本即
39+`${你的账号}-26.09-Devstation`),创建者天然就是该子工程的 owner/maintainer,
40+拥有构建权限。
41+ 
42+继承子工程自动带来:
43+ 
44+- **build_targets**:aarch64 / x86_64 两架构 + os_variant + 依赖的基础仓库
45+- **构建环境宏**:与官方工程一致(签名、压缩、python 版本等)
46+ 
47+继承**不带** package_repos(包源定义),所以 `--setup` 会把 kernel 包源配置到
48+你的子工程(按本地 `ebs-build.conf` 的 `SPEC_URL`/`SPEC_BRANCH` 配置,已配置好
49+则跳过)。
50+ 
51+```bash
52+# 底层命令示意(脚本已封装,无需手敲;工程名/版本号按你的实际情况)
53+ccb create projects <你的账号>-26.09-Devstation inherited_from=openEuler-26.09-DevStation:everything
54+ccb update projects <你的账号>-26.09-Devstation --json '{"package_repos+":[{"spec_name":"kernel","spec_url":"https://atomgit.com/src-openeuler/kernel.git","spec_branch":"openEuler-26.09-DevStation"}]}'
55+ccb create snapshots <你的账号>-26.09-Devstation
56+```
57+ 
58+**构建自己的 fork**:想用你自己的代码/配置构建,改**本地** `ebs-build.conf` 里的
59+`SPEC_URL`(如 `https://gitcode.com/<你的账号>/kernel.git`)和 `SPEC_BRANCH`(指向
60+你工作的分支),再重跑 `--setup` 刷新快照。
61+ 
62+**注意:fork 仓库名必须保持为 `kernel`**(如上例末段 `kernel.git`)。EBS 只对名为
63+kernel 的 spec 仓库自动生成 `Source0: kernel.tar.gz`;仓库名带前缀/后缀(如
64+`myfork/kernel-dev`)不会触发生成,`%prep` 会报
65+`error: File .../kernel.tar.gz: No such file or directory`。
66+ 
67+---
68+ 
69+## 配置文件:ebs-build.conf(模板 ebs-build.conf.example)
70+ 
71+版本/分支等构建参数写在**本地文件** `ebs-build.conf`(已 gitignore,不入库),仓库
72+提交的是模板 `ebs-build.conf.example`。首次运行 `--setup` / `build` / `download`
73+时,脚本会自动把模板复制成本地 `ebs-build.conf` 并提示你编辑,然后继续。
74+ 
75+| 键 | 含义 | 模板里的示例 |
76+|------|------|------|
77+| `SPEC_URL` | kernel 包源地址(**仓库名必须叫 kernel**,否则 EBS 不自动生成 Source0 kernel.tar.gz) | `https://atomgit.com/src-openeuler/kernel.git` |
78+| `SPEC_BRANCH` | 分支 | `openEuler-26.09-DevStation` |
79+| `INHERIT_FROM` | 继承的官方工程 | `openEuler-26.09-DevStation:everything` |
80+| `PROJECT_SUFFIX` | 子工程名后缀(工程名 = 账号 + 后缀) | `26.09-Devstation` |
81+| `PACKAGE` | 构建的包名 | `kernel` |
82+| `KERNEL_REPO` | 内核源码仓库路径或 git URL(本地构建用:gen-source-tarball.sh 据此从 SOURCE tag 生成 kernel.tar.gz;EBS 构建无需) | `~/git/openeuler/kernel`(模板中为注释示例,需自行取消注释) |
83+ 
84+取值优先级:**命令行环境变量 > 本地 ebs-build.conf**。
85+ 
86+- 换版本/分支:改**你自己的** `ebs-build.conf` 即可,不用改脚本、也不用动模板
87+- 想手动生成:`cp ebs-build.conf.example ebs-build.conf`,然后编辑
88+- 临时覆盖(不改文件):`export SPEC_BRANCH=你的分支` 后再跑命令
89+- 用别的配置文件:`export EBS_CONFIG=/path/to/my.conf`(需自己准备一份完整的配置)
90+ 
91+> 注意和 ccb 的 `~/.config/cli/defaults/config.yaml` 区分开:那个管**账号登录**,
92+> 本文件管**版本/分支**(及本地构建时的源码位置 `KERNEL_REPO`)。
93+ 
94+---
95+ 
96+## 快照(snapshot)是什么
97+ 
98+快照是**依赖基线的可复现版本号**。构建时后端根据快照把整棵依赖树固定到一组可复现
99+的版本,保证每次构建的环境一致、产物可复现。
100+ 
101+- `--setup` 会给你的子工程打一个快照,构建时按它派单
102+- 一旦改过包源(比如切到你的 fork / 换分支),**必须重跑 `--setup`** 重新打快照,
103+ 否则构建会卡住(EBS 报 203)或仍用旧代码
104+ 
105+---
106+ 
107+## ccb 客户端安装
108+ 
109+`--install-ccb` 会从官方仓库 `gitcode.com/src-openeuler/ccb` 下载 ccb(v2.0.1),
110+安装到 `~/.local/libexec/ccb-2.0.1/` 并软链到 `~/.local/bin/ccb`。
111+ 
112+前置依赖(脚本会自动检测并给出安装命令):
113+ 
114+```bash
115+dnf install -y ruby rubygem-activesupport rubygem-rest-client wget
116+```
117+ 
118+> docker 只有本地构建才需要,纯云端构建可不装。ccb 官方也提供 rpm 包(ccb.spec,
119+> 可在仓库内 `rpmbuild -ba` 自建私有源),团队如需统一分发可按此方式打 rpm。
120+ 
121+---
122+ 
123+## 下载构建产物
124+ 
125+构建成功后,可以把 rpm 下载到当前目录(默认双架构全下载,`-a` 指定单架构):
126+ 
127+```bash
128+./scripts/ebs-build.sh download # aarch64 + x86_64 全部 rpm
129+./scripts/ebs-build.sh download -a aarch64 # 仅 aarch64
130+```
131+ 
132+> 该命令需要 ccb 的下载配置(`SRV_HTTP_REPOSITORIES_*`),若提示缺失,在
133+> `~/.config/cli/defaults/config.yaml` 里补上即可。
134+ 
135+---
136+ 
137+## 查看产物仓库 URL
138+ 
139+构建成功后,可以用 `repo-url` 直接查看**最后一次成功构建**的产物仓库目录 URL
140+(不下载、不触发构建,只显示路径;默认 aarch64 + x86_64,可指定架构):
141+ 
142+```bash
143+./scripts/ebs-build.sh repo-url # aarch64 + x86_64 各一行 URL
144+./scripts/ebs-build.sh repo-url aarch64 # 仅 aarch64
145+```
146+ 
147+> 查询的是 rpms 索引里最近一次成功发布的产物路径,所以展示的一定是**成功**构建的
148+> 仓库,而非最近一次(可能失败)的构建。
149+ 
150+---
151+ 
152+## 常见问题
153+ 
154+**Q: 第一次跑提示「已从 ebs-build.conf.example 复制」?**
155+正常,那是脚本为你生成了本地配置 `ebs-build.conf`(gitignore,不入库)。按你的
156+版本/分支编辑它即可,不用改模板。
157+ 
158+**Q: 提示 "未找到 ccb"?**
159+先执行 `./scripts/ebs-build.sh --install-ccb`。若 `~/.local/bin` 不在 PATH,按脚本
160+提示加入。
161+ 
162+**Q: 提示需要登录 / JWT 过期?**
163+`export PASSWORD='你的密码'` 后重试。脚本会在需要时自动重新获取令牌并缓存到
164+`~/.config/cli/jwt`。
165+ 
166+**Q: 构建一直卡在 status 203?**
167+203 通常表示卡住/失败。先 `log <job_id> --check` 看日志;若是"未打快照",重跑
168+`./scripts/ebs-build.sh --setup`。
169+ 
170+**Q: 构建报 `error: File .../kernel.tar.gz: No such file or directory`?**
171+spec 仓库名不是 kernel。EBS 只对名为 `kernel` 的仓库自动生成 Source0 kernel.tar.gz;
172+请把 fork 建成纯 `kernel` 名的仓库(如 `https://gitcode.com/kernel-source/kernel`),
173+把 `SPEC_URL` 指过去后重跑 `./scripts/ebs-build.sh --setup`。
174+ 
175+**Q: 日志里模块未签名(openEuler_has_sign_perm=0)?**
176+正常现象。EBS 的签名服务目前对该类账户返回 `SIGN_PERMISSION_DENIED`,本地与云端
177+构建产物均未签名,不影响构建与 rpm 发布,只影响 Secure Boot 强校验环境加载模块。
178+ 
179+**Q: 如何在本地构建?**
180+仓库里另有本地构建流程(见 README),云端构建侧重可复现与团队协作。
@@ -0,0 +1,19 @@
1+# ebs-build.sh 配置文件模板
2+#
3+# 首次运行 setup / build / download 时,脚本会自动把本模板复制成
4+# 本地文件 ebs-build.conf(已 gitignore,不入库),请按你的版本/分支编辑。
5+#
6+# 取值优先级:命令行环境变量 > 本地 ebs-build.conf > 本模板
7+# 下面是当前 openEuler 版本的示例值,按实际版本/分支修改即可:
8+ 
9+# 远端仓库名必须叫 kernel:EBS 只对名为 kernel 的 spec 仓库自动生成
10+# Source0 kernel.tar.gz,否则 %prep 报 No such file。fork 请用纯 kernel 名。
11+SPEC_URL=https://atomgit.com/src-openeuler/kernel.git
12+SPEC_BRANCH=openEuler-26.09-DevStation
13+INHERIT_FROM=openEuler-26.09-DevStation:everything
14+PROJECT_SUFFIX=26.09-Devstation
15+PACKAGE=kernel
16+ 
17+# 本地手动构建用(EBS 构建无需):内核源码仓库路径或 git URL,
18+# gen-source-tarball.sh 用它从 `SOURCE` 的 tag 生成 kernel.tar.gz。
19+# KERNEL_REPO=~/git/openeuler/kernel
@@ -42,7 +42,7 @@ rm -f test_openEuler_sign.ko test_openEuler_sign.ko.sig
42%global upstream_sublevel 4042%global upstream_sublevel 40
43%global devel_release 043%global devel_release 0
44%global maintenance_release .0.044%global maintenance_release .0.0
45-%global pkg_release .1345+%global pkg_release .14
46 46 
47%global openeuler_lts 047%global openeuler_lts 0
48%global openeuler_major 048%global openeuler_major 0
@@ -1119,6 +1119,13 @@ fi
1119%endif1119%endif
1120 1120 
1121%changelog1121%changelog
1122+* Tue Aug 11 2026 Xie XiuQi <xiexiuqi@huawei.com> - 6.18.40-0.0.0.14
1123+- Add self-service EulerMaker EBS cloud build (scripts/ebs-build.sh)
1124+- Add scripts/gen-source-tarball.sh for local kernel.tar.gz generation
1125+- Add ebs-build.conf.example and newcomer guide docs/ebs-build.md
1126+- Update README/README_cn with cloud and local build instructions
1127+- Add SPDX license header to scripts/clean-config
1128+ 
1122* Tue Aug 11 2026 Xie XiuQi <xiexiuqi@huawei.com> - 6.18.40-0.0.0.131129* Tue Aug 11 2026 Xie XiuQi <xiexiuqi@huawei.com> - 6.18.40-0.0.0.13
1123- Add README and README_cn, drop empty readme placeholder1130- Add README and README_cn, drop empty readme placeholder
1124- config: enable wifi/bluetooth/touchpad drivers for DevStation1131- config: enable wifi/bluetooth/touchpad drivers for DevStation
@@ -1,4 +1,5 @@
1#!/bin/bash1#!/bin/bash
2+# SPDX-License-Identifier: GPL-2.0-only
2# Strip toolchain auto-detection options from a kernel .config file.3# Strip toolchain auto-detection options from a kernel .config file.
3#4#
4# These options are automatically detected by Kconfig from the build5# These options are automatically detected by Kconfig from the build
@@ -0,0 +1,521 @@
1+#!/usr/bin/env bash
2+# SPDX-License-Identifier: GPL-2.0-only
3+#
4+# ebs-build.sh — 用 EulerMaker EBS (ccb) 自助构建内核
5+#
6+# 依赖: bash >= 4, curl, grep, sed, ruby (仅 --install-ccb 需要 ruby/gem), python3 (配置 package_repos 时需,缺失时降级为只追加/提示)
7+# 所有参数可默认,用环境变量覆盖;密码只从 PASSWORD 环境变量读取,绝不写入文件。
8+#
9+# 用法:
10+# ebs-build.sh --install-ccb 安装 ccb 客户端到 ~/.local(方案 A: git clone)
11+# ebs-build.sh --config 交互生成 ~/.config/cli/defaults/config.yaml
12+# ebs-build.sh --setup 准备工程: 创建继承子工程 + 配置好 kernel 包源 + snapshot(重复运行安全)
13+# ebs-build.sh build 触发构建并轮询至 aarch64/x86_64 完成(需确认,或 FORCE=1 跳过)
14+# ebs-build.sh log <job_id> [--check] 打印构建日志 URL;--check 拉取并 grep 错误/签名状态
15+# ebs-build.sh download [-a <arch>] 下载构建产出的 rpm 到当前目录
16+# ebs-build.sh repo-url [arch...] 显示最后一次成功构建的产物仓库 URL(不触发构建)
17+# ebs-build.sh --help
18+#
19+# 配置:版本/分支等从本地 ebs-build.conf 读取,首次运行自动从模板
20+# ebs-build.conf.example 复制;环境变量可覆盖配置值
21+# PROJECT 默认 ${USER}-<PROJECT_SUFFIX,取自 ebs-build.conf>
22+# SPEC_URL / SPEC_BRANCH / INHERIT_FROM 默认取 ebs-build.conf,可用环境变量覆盖
23+# EBS_CONFIG 配置文件路径(默认 <脚本上级目录>/ebs-build.conf)
24+# PASSWORD 社区账号密码(--config / build 首次运行时需要)
25+# MAX_POLLS 轮询次数上限(默认 90,配合 POLL_INTERVAL=60 即 90 分钟)
26+# FORCE=1 跳过 build 前的确认提示(供 CI 使用)
27+#
28+# 详见 docs/ebs-build.md
29+ 
30+set -euo pipefail
31+ 
32+# ---------------- 参数与配置 ----------------
33+# 版本/分支等从本地配置文件 ebs-build.conf 读取,首次运行自动从模板
34+# ebs-build.conf.example 复制。取值优先级:环境变量 > 配置文件。
35+# 用 EBS_CONFIG 可指定别的配置文件路径。
36+CONF="${EBS_CONFIG:-$(cd "$(dirname "$0")/.." && pwd)/ebs-build.conf}"
37+ 
38+# 读取配置值:环境变量优先,其次配置文件(不内置版本默认值)
39+conf_get() {
40+ local key="$1" fallback="$2"
41+ local val="$fallback"
42+ if [ -f "$CONF" ]; then
43+ local fv
44+ fv="$(sed -nE "s/^[[:space:]]*${key}[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$/\\1/p" "$CONF" | head -1)"
45+ [ -n "$fv" ] && val="$fv"
46+ fi
47+ [ -n "${!key:-}" ] && val="${!key}"
48+ printf '%s' "$val"
49+}
50+ 
51+# 读取并校验配置(setup / build / download 开头调用)
52+load_config() {
53+ if [ ! -f "$CONF" ]; then
54+ if [ -f "$CONF.example" ]; then
55+ cp "$CONF.example" "$CONF"
56+ info "未找到 $CONF,已从 $CONF.example 复制。"
57+ info "请编辑 $CONF 里的版本/分支(当前为模板示例值),临时覆盖可用环境变量。"
58+ else
59+ die "未找到配置文件 $CONF,也没有模板 $CONF.example。请复制模板或设置 EBS_CONFIG。"
60+ fi
61+ fi
62+ if [ -z "${PROJECT:-}" ]; then
63+ local sfx
64+ sfx="$(conf_get PROJECT_SUFFIX "")"
65+ PROJECT="${USER}${sfx:+-${sfx}}"
66+ fi
67+ SPEC_URL="$(conf_get SPEC_URL "")"
68+ SPEC_BRANCH="$(conf_get SPEC_BRANCH "")"
69+ INHERIT_FROM="$(conf_get INHERIT_FROM "")"
70+ PACKAGE="$(conf_get PACKAGE kernel)"
71+ if [ -z "$SPEC_URL" ] || [ -z "$SPEC_BRANCH" ] || [ -z "$INHERIT_FROM" ]; then
72+ die "配置文件 $CONF 缺少 SPEC_URL / SPEC_BRANCH / INHERIT_FROM,请补全后重试。"
73+ fi
74+}
75+ 
76+# 校验 SPEC_URL 的远端仓库名等于 PACKAGE(kernel)。EBS 只对名为 kernel 的
77+# spec 仓库自动生成 Source0 kernel.tar.gz;名字不符不会触发生成,%prep 报
78+# No such file,构建快速失败。
79+check_spec_repo_name() {
80+ local repo
81+ repo="$(basename "${SPEC_URL%.git}")"
82+ [ "$repo" = "$PACKAGE" ] || die "SPEC_URL 仓库名 '$repo' 必须为 '$PACKAGE'(EBS 只对名为 kernel 的 spec 仓库自动生成 Source0 kernel.tar.gz)。fork 请用 gitcode.com/kernel-source/kernel 这类纯 kernel 名,不要加前缀。"
83+}
84+ 
85+ARCHS="${ARCHS:-aarch64 x86_64}"
86+MAX_POLLS="${MAX_POLLS:-90}"
87+POLL_INTERVAL="${POLL_INTERVAL:-60}"
88+ 
89+CCB_VERSION="${CCB_VERSION:-2.0.1}"
90+INSTALL_DIR="${CCB_INSTALL_DIR:-$HOME/.local/libexec/ccb-$CCB_VERSION}"
91+LOCAL_BIN="$HOME/.local/bin"
92+CONFIG_DIR="$HOME/.config/cli/defaults"
93+CONFIG_FILE="$CONFIG_DIR/config.yaml"
94+JWT_FILE="$HOME/.config/cli/jwt"
95+GATEWAY="https://eulermaker.openeuler.openatom.cn"
96+ 
97+# ---------------- 工具函数 ----------------
98+die() { echo "[ebs] $*" >&2; exit 1; }
99+info() { echo "[ebs] $*"; }
100+ 
101+need_cmd() { command -v "$1" >/dev/null 2>&1 || die "缺少命令: $1"; }
102+ 
103+# 定位 ccb 可执行文件(支持 --install-ccb 生成的软链与系统安装)
104+find_ccb() {
105+ if command -v ccb >/dev/null 2>&1; then echo ccb; return 0; fi
106+ if [ -x "$LOCAL_BIN/ccb" ]; then echo "$LOCAL_BIN/ccb"; return 0; fi
107+ if [ -x /usr/sbin/ccb ]; then echo /usr/sbin/ccb; return 0; fi
108+ return 1
109+}
110+CCB="$(find_ccb 2>/dev/null || true)"
111+ 
112+require_ccb() {
113+ [ -n "$CCB" ] || die "未找到 ccb,先运行: $0 --install-ccb"
114+}
115+ 
116+require_jwt() {
117+ if [ ! -f "$JWT_FILE" ]; then
118+ info "未找到已保存的登录令牌 (~/.config/cli/jwt),需要重新登录"
119+ info "请确认已运行 --config 且已 export PASSWORD 环境变量,然后重试"
120+ return 1
121+ fi
122+}
123+ 
124+# ccb 因缺少 PASSWORD / JWT 失效报错时,输出解决方法提示
125+jwt_password_hint() {
126+ grep -qE "ENV\['PASSWORD'\]|undefined method .strip. for nil|PASSWORD" <<<"$1" \
127+ && die "登录令牌(JWT)已失效且未设置密码环境变量。
128+ 
129+ 请先 export PASSWORD 后重试:
130+ export PASSWORD='你的社区账号密码'
131+ $0 build
132+ 脚本不会把密码写入任何文件;若尚未配置账号,先执行 $0 --config。"
133+ return 1
134+}
135+ 
136+# ---------------- --install-ccb ----------------
137+cmd_install_ccb() {
138+ info "检查依赖..."
139+ need_cmd git
140+ need_cmd wget
141+ for c in ruby; do need_cmd "$c"; done
142+ local miss=0
143+ for gem in activesupport rest-client; do
144+ if ! ruby -e "require '$gem'" >/dev/null 2>&1; then
145+ echo " 缺少 ruby gem: $gem"
146+ miss=1
147+ fi
148+ done
149+ if [ "$miss" -eq 1 ]; then
150+ echo " 请先安装: dnf install -y ruby rubygem-activesupport rubygem-rest-client wget"
151+ echo " (docker 仅本地构建需要,此处不检查)"
152+ exit 1
153+ fi
154+ 
155+ info "安装 ccb v${CCB_VERSION} 到 $INSTALL_DIR"
156+ if [ ! -d "$INSTALL_DIR/sbin/cli" ]; then
157+ mkdir -p "$(dirname "$INSTALL_DIR")"
158+ info " git clone https://gitcode.com/src-openeuler/ccb ..."
159+ git clone --depth 1 "https://gitcode.com/src-openeuler/ccb" "$INSTALL_DIR" 2>&1 \
160+ || die "git clone 失败,请检查网络或改用官方 tarball"
161+ else
162+ info " 已存在,跳过下载"
163+ fi
164+ 
165+ mkdir -p "$LOCAL_BIN"
166+ ln -sfn "$INSTALL_DIR/sbin/cli/ccb" "$LOCAL_BIN/ccb"
167+ info "已软链: $LOCAL_BIN/ccb -> $INSTALL_DIR/sbin/cli/ccb"
168+ 
169+ if ! find_ccb >/dev/null 2>&1; then
170+ echo " 警告: ~/.local/bin 不在 PATH,可执行以下命令加入:"
171+ echo " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc && source ~/.bashrc"
172+ fi
173+ info "验证: $($LOCAL_BIN/ccb -h 2>&1 | head -1)"
174+ info "下一步: $0 --config"
175+}
176+ 
177+# ---------------- --config ----------------
178+cmd_config() {
179+ mkdir -p "$CONFIG_DIR"
180+ if [ -f "$CONFIG_FILE" ]; then
181+ info "已有 $CONFIG_FILE,将覆盖。Ctrl-C 可取消。"
182+ fi
183+ read -rp "社区账号 (ACCOUNT): " ACCOUNT
184+ : "${ACCOUNT:?账号不能为空}"
185+ 
186+ cat > "$CONFIG_FILE" <<EOF
187+GATEWAY_IP: ${GATEWAY_IP:-eulermaker.openeuler.openatom.cn}
188+GATEWAY_PORT: ${GATEWAY_PORT:-443}
189+SRV_URL: ${SRV_URL:-https://eulermaker.openeuler.openatom.cn}
190+ACCOUNT: ${ACCOUNT}
191+# PASSWORD 不写入本文件,构建时通过环境变量 PASSWORD 提供:
192+# export PASSWORD='你的社区密码'
193+OAUTH_TOKEN_URL: ${OAUTH_TOKEN_URL:-https://omapi.osinfra.cn/oneid/oidc/token}
194+OAUTH_REDIRECT_URL: ${OAUTH_REDIRECT_URL:-https://eulermaker.openeuler.openatom.cn/oauth}
195+PUBLIC_KEY_URL: ${PUBLIC_KEY_URL:-https://omapi.osinfra.cn/oneid/public/key?community=openeuler}
196+EOF
197+ chmod 600 "$CONFIG_FILE"
198+ info "已生成 $CONFIG_FILE"
199+ info "提示: 使用前请先 export PASSWORD 环境变量(不会写入任何文件)"
200+ info "验证: $0 --setup 会检查登录令牌,必要时自动重新获取"
201+}
202+ 
203+# ---------------- --setup ----------------
204+cmd_setup() {
205+ require_ccb
206+ load_config
207+ check_spec_repo_name
208+ info "准备工程: $PROJECT"
209+ 
210+ # 1. 工程是否存在(不存在则创建继承子工程)
211+ if "$CCB" select projects os_project="$PROJECT" --size 1 2>/dev/null | grep -q '"_id"'; then
212+ info " 工程已存在,跳过创建"
213+ else
214+ info " 创建继承子工程: inherited_from=$INHERIT_FROM"
215+ "$CCB" create projects "$PROJECT" inherited_from="$INHERIT_FROM"
216+ fi
217+ 
218+ # 2. 配置 package_repos:与 ebs-build.conf 的 SPEC_URL/SPEC_BRANCH 一致则跳过,否则更新
219+ # spec_url 需以 .git 结尾(云端 schema 强制),缺则补,避免 update 被拒
220+ case "$SPEC_URL" in
221+ *.git) : ;;
222+ *) SPEC_URL="${SPEC_URL}.git" ;;
223+ esac
224+ local cur_json
225+ cur_json="$("$CCB" select projects os_project="$PROJECT" --field package_repos --size 1 2>/dev/null)"
226+ 
227+ # 已配置一致(spec_name/spec_url/spec_branch 三要素同时命中)则跳过
228+ if printf '%s' "$cur_json" | grep -Fq "\"spec_name\": \"${PACKAGE}\"" \
229+ && printf '%s' "$cur_json" | grep -Fq "\"spec_url\": \"${SPEC_URL}\"" \
230+ && printf '%s' "$cur_json" | grep -Fq "\"spec_branch\": \"${SPEC_BRANCH}\""; then
231+ info " package_repos 已与配置一致,跳过"
232+ elif command -v python3 >/dev/null 2>&1; then
233+ info " 配置 package_repos: $SPEC_URL ($SPEC_BRANCH)"
234+ local tmp
235+ tmp="$(mktemp)"
236+ SPEC_URL="$SPEC_URL" SPEC_BRANCH="$SPEC_BRANCH" PACKAGE="$PACKAGE" python3 - "$cur_json" > "$tmp" <<'PYEOF'
237+import json, os, sys
238+doc = json.loads(sys.argv[1])
239+cur = doc[0]['_source'].get('package_repos', []) if doc else []
240+target = {"spec_name": os.environ["PACKAGE"], "spec_url": os.environ["SPEC_URL"], "spec_branch": os.environ["SPEC_BRANCH"]}
241+new = [e for e in cur if e.get("spec_name") != target["spec_name"]] + [target]
242+print(json.dumps({"package_repos": new}))
243+PYEOF
244+ "$CCB" update projects "$PROJECT" -j "$tmp"
245+ rm -f "$tmp"
246+ elif printf '%s' "$cur_json" | grep -Fq "\"spec_name\": \"${PACKAGE}\""; then
247+ info " WARN: 缺少 python3 且 package_repos 已有 ${PACKAGE},无法精确更新 spec_url"
248+ info " 请手动执行: $CCB update projects $PROJECT -j '{\"package_repos\": [...]}'"
249+ else
250+ info " 追加 package_repos: $SPEC_URL ($SPEC_BRANCH)"
251+ local tmp
252+ tmp="$(mktemp)"
253+ cat > "$tmp" <<EOF
254+{"package_repos+":[{"spec_name":"${PACKAGE}","spec_url":"${SPEC_URL}","spec_branch":"${SPEC_BRANCH}"}]}
255+EOF
256+ "$CCB" update projects "$PROJECT" -j "$tmp"
257+ rm -f "$tmp"
258+ fi
259+ 
260+ # 3. snapshot 是否存在(没有则创建,作为依赖基线/可复现版本号)
261+ if "$CCB" select snapshots os_project="$PROJECT" --size 1 2>/dev/null | grep -q '"_id"'; then
262+ info " snapshot 已存在,跳过"
263+ else
264+ info " 创建 snapshot(固定依赖基线)"
265+ "$CCB" create snapshots "$PROJECT"
266+ fi
267+ 
268+ info "完成。下一步: $0 build"
269+}
270+ 
271+# ---------------- build ----------------
272+cmd_build() {
273+ require_ccb
274+ load_config
275+ check_spec_repo_name
276+ require_jwt || { "$CCB" -h >/dev/null 2>&1 || true; exit 1; }
277+ 
278+ info "将触发真实云端构建: os_project=$PROJECT packages=$PACKAGE"
279+ if [ "${FORCE:-0}" != "1" ]; then
280+ read -rp "确认构建? [y/N] " ans
281+ case "$ans" in
282+ y|Y) : ;;
283+ *) die "已取消" ;;
284+ esac
285+ fi
286+ 
287+ local out
288+ out="$("$CCB" build-single os_project="$PROJECT" packages="$PACKAGE" 2>&1)" || {
289+ jwt_password_hint "$out"
290+ die "build-single 失败,输出如下:
291+$out"
292+ }
293+ echo "$out"
294+ 
295+ local ba
296+ ba="$(parse_build_arch <<<"$out")"
297+ if [ -z "$ba" ]; then
298+ info "未能从响应中解析 build_id/架构,请在上方输出中查找 build_id 后用 'log' 查看进度"
299+ exit 0
300+ fi
301+ info "触发成功,build_id -> arch:"
302+ sed 's/^/ /' <<<"$ba"
303+ 
304+ poll_builds "$ba"
305+}
306+ 
307+# 从 build-single 输出解析 "build_id arch" 每行一对
308+parse_build_arch() {
309+ awk '
310+ /"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"/ {
311+ match($0, /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/)
312+ bid = substr($0, RSTART, RLENGTH)
313+ }
314+ /"architecture"[[:space:]]*:[[:space:]]*"/ {
315+ if (bid != "") {
316+ arch = $0
317+ sub(/.*"architecture"[[:space:]]*:[[:space:]]*"/, "", arch)
318+ sub(/".*/, "", arch)
319+ print bid, arch
320+ bid = ""
321+ }
322+ }
323+ '
324+}
325+ 
326+# 状态码 → 中文描述(204=准备 200=构建中 201=成功 202=已发布 203=失败)
327+status_label() {
328+ case "$1" in
329+ 200) echo "构建中" ;;
330+ 201) echo "成功" ;;
331+ 202) echo "已发布" ;;
332+ 203) echo "失败" ;;
333+ 204) echo "准备中" ;;
334+ *) echo "状态?" ;;
335+ esac
336+}
337+ 
338+# 秒数格式化为 "Xm Ys"
339+fmt_elapsed() {
340+ echo "$(( $1 / 60 ))m $(( $1 % 60 ))s"
341+}
342+ 
343+# 查 builds 索引的 status 数字(204=准备 200=构建中 201=成功 202=已发布 203=卡住/失败)
344+get_build_status() {
345+ "$CCB" select builds build_id="$1" --field status --size 1 2>/dev/null \
346+ | grep -oE '"status":[[:space:]]*[0-9]+' | grep -oE '[0-9]+' | head -1
347+}
348+ 
349+poll_builds() {
350+ local ba="$1"
351+ local i=0 start elapsed labels
352+ start="$(date +%s)"
353+ while [ "$i" -lt "$MAX_POLLS" ]; do
354+ i=$((i + 1))
355+ elapsed=$(( $(date +%s) - start ))
356+ labels=""
357+ local all_done=1
358+ local bid arch status
359+ while read -r bid arch; do
360+ status="$(get_build_status "$bid")"
361+ [ -n "$status" ] || status="?"
362+ labels="${labels:+$labels, }${arch}: $(status_label "$status")"
363+ case "$status" in
364+ 201|202) : ;; # 成功 / 已发布
365+ 203) info " [${arch}] 构建失败(status=203)"; exit 1 ;;
366+ *) all_done=0 ;; # 204/200/空 → 继续等
367+ esac
368+ done <<<"$ba"
369+ 
370+ if [ "$all_done" -eq 1 ]; then
371+ info " 全部架构构建成功: ${labels}"
372+ print_log_hint "$ba"
373+ while read -r bid arch; do print_repo_url "$arch"; done <<<"$ba"
374+ return 0
375+ fi
376+ 
377+ info " [第 ${i}/${MAX_POLLS} 次 · 已用 $(fmt_elapsed "$elapsed")]${labels:+ ${labels}}(${POLL_INTERVAL}s 后重试)"
378+ [ "$i" -lt "$MAX_POLLS" ] && sleep "$POLL_INTERVAL"
379+ done
380+ info "轮询超时(已等待 $(( MAX_POLLS * POLL_INTERVAL / 60 )) 分钟)。最近状态: ${labels}"
381+ info "仍可能在后台上构建,可用 'log <job_id> --check' 或 Web 构建历史查看"
382+ exit 1
383+}
384+ 
385+print_log_hint() {
386+ local ba="$1" bid arch
387+ info "各架构 job 日志(可用 $0 log <job_id> 查看):"
388+ while read -r bid arch; do
389+ "$CCB" select jobs build_id="$bid" --field id,arch --size 20 2>/dev/null \
390+ | grep -oE '"id": "[^"]+"|"arch": "[^"]+"' | paste - - | sed 's/^/ /'
391+ done <<<"$ba"
392+}
393+ 
394+# 查某架构最新产物 rpm 仓库路径(复用 ccb select)
395+get_rpm_path() {
396+ local arch="$1"
397+ "$CCB" select rpms "$PROJECT" repo_name="${PACKAGE%%:*}" architecture="$arch" \
398+ --field 'rpm_path' --sort 'submit_time:desc' --size 1 2>/dev/null \
399+ | python3 -c 'import sys,json
400+try:
401+ d=json.load(sys.stdin)
402+ print(d[0]["_source"]["rpm_path"] if d else "")
403+except Exception:
404+ print("")'
405+}
406+ 
407+# 查项目产物服务器标识 emsx
408+get_emsx() {
409+ "$CCB" select projects "$PROJECT" --field 'emsx' --size 1 2>/dev/null \
410+ | python3 -c 'import sys,json
411+try:
412+ d=json.load(sys.stdin)
413+ print(d[0]["_source"].get("emsx","ems1") if d else "ems1")
414+except Exception:
415+ print("ems1")'
416+}
417+ 
418+# 从 ccb 配置读取扁平键(~/.config/cli/defaults/config.yaml,KEY: value 格式)
419+ccb_cfg() {
420+ sed -nE "s/^${1//./\\.}: *//p" "$HOME/.config/cli/defaults/config.yaml" | tr -d ' \r' | head -1
421+}
422+ 
423+# 产物仓库 URL 前缀
424+repo_prefix() {
425+ local proto host port
426+ proto="$(ccb_cfg SRV_HTTP_REPOSITORIES_PROTOCOL)"
427+ host="$(ccb_cfg SRV_HTTP_REPOSITORIES_HOST)"
428+ port="$(ccb_cfg SRV_HTTP_REPOSITORIES_PORT)"
429+ printf '%s%s:%s' "${proto:-https://}" "${host}" "${port}"
430+}
431+ 
432+# 打印某架构产物仓库 URL
433+print_repo_url() {
434+ local arch="$1" path emsx
435+ path="$(get_rpm_path "$arch")" || return 1
436+ if [ -z "$path" ]; then
437+ info " [${arch}] 未查到产物记录(rpms 索引无匹配)"
438+ return 1
439+ fi
440+ emsx="$(get_emsx)"
441+ info " [${arch}] 产物仓库: $(repo_prefix)/api/${emsx}${path}"
442+}
443+ 
444+# ---------------- log ----------------
445+cmd_log() {
446+ require_ccb
447+ local job_id="${1:-}"
448+ [ -n "$job_id" ] || die "用法: $0 log <job_id> [--check]"
449+ local check="${2:-}"
450+ 
451+ local rr
452+ rr="$("$CCB" select jobs id="$job_id" --field result_root --size 1 2>/dev/null \
453+ | sed -nE 's/.*"result_root"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' | head -1)"
454+ [ -n "$rr" ] || die "未找到 job: $job_id"
455+ 
456+ local url
457+ url="${GATEWAY}/api/result${rr#/result}/dmesg"
458+ echo "构建日志: $url"
459+ 
460+ if [ "$check" = "--check" ]; then
461+ need_cmd curl
462+ info "拉取日志并检查...(可能较大,请耐心)"
463+ local log
464+ log="$(curl -fsSL "$url" 2>/dev/null)" || die "拉取日志失败: $url"
465+ 
466+ info "--- 真实失败模式 ---"
467+ if grep -E 'make: \*\*\*|undefined reference|No rule to make target|Out of memory|Killed|PANIC|internal compiler' <<<"$log"; then
468+ exit 1
469+ else
470+ echo " (未发现上述失败模式)"
471+ fi
472+ 
473+ info "--- 模块签名状态 ---"
474+ grep -oE 'openEuler_has_sign_perm=[01]' <<<"$log" | sort -u || echo " (日志中未找到签名探测输出)"
475+ fi
476+}
477+ 
478+# ---------------- download ----------------
479+cmd_download() {
480+ require_ccb
481+ load_config
482+ local arch=""
483+ while [ "$#" -gt 0 ]; do
484+ case "$1" in
485+ -a|--arch) arch="$2"; shift 2 ;;
486+ *) die "未知参数: $1(用法: $0 download [-a aarch64|x86_64])" ;;
487+ esac
488+ done
489+ 
490+ local args=(os_project="$PROJECT" packages="$PACKAGE")
491+ if [ -n "$arch" ]; then args+=(architecture="$arch"); fi
492+ info "下载 rpm: ${args[*]}"
493+ "$CCB" download "${args[@]}"
494+}
495+ 
496+# 显示最后一次成功构建的产物仓库 URL(不触发构建)
497+cmd_repo_url() {
498+ require_ccb
499+ load_config
500+ local archs=("$@")
501+ [ "${#archs[@]}" -eq 0 ] && archs=($ARCHS)
502+ for arch in "${archs[@]}"; do print_repo_url "$arch" || true; done
503+}
504+ 
505+# ---------------- help ----------------
506+cmd_help() {
507+ sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'
508+}
509+ 
510+# ---------------- 入口 ----------------
511+case "${1:-}" in
512+ --install-ccb) cmd_install_ccb ;;
513+ --config) cmd_config ;;
514+ --setup) cmd_setup ;;
515+ build) shift; cmd_build ;;
516+ log) shift; cmd_log "$@" ;;
517+ download) shift; cmd_download "$@" ;;
518+ repo-url) shift; cmd_repo_url "$@" ;;
519+ --help|-h|"") cmd_help ;;
520+ *) die "未知命令: ${1:-}(--help 查看用法)" ;;
521+esac
@@ -0,0 +1,73 @@
1+#!/bin/bash
2+# SPDX-License-Identifier: GPL-2.0-only
3+# Generate kernel.tar.gz (Source0) from the git tag recorded in SOURCE.
4+#
5+# The source tarball is NOT stored in this repository — EulerMaker (EBS)
6+# auto-generates it from the SOURCE tag at build time. Local manual builds
7+# need this script to produce kernel.tar.gz first.
8+#
9+# The kernel source repo is configured (layered, no bashrc editing):
10+# 1. environment variable: KERNEL_REPO=/path/to/kernel
11+# 2. local gitignored file: KERNEL_REPO=~/git/openeuler/kernel in ebs-build.conf
12+# 3. error with example (see below)
13+#
14+# KERNEL_REPO may be a local git checkout or a git URL. A missing tag in a
15+# local checkout is fetched from its origin; a URL is shallow-cloned at the tag.
16+#
17+# Usage:
18+# scripts/gen-source-tarball.sh
19+# → creates kernel.tar.gz in repo root (top dir is kernel/)
20+ 
21+set -euo pipefail
22+ 
23+repo_root="$(git rev-parse --show-toplevel)"
24+cd "$repo_root"
25+ 
26+if [ ! -f SOURCE ]; then
27+ echo "ERROR: SOURCE not found in $repo_root" >&2
28+ exit 1
29+fi
30+ 
31+tag="$(head -1 SOURCE)"
32+if [ -z "$tag" ]; then
33+ echo "ERROR: SOURCE is empty (expected a git tag on the first line)" >&2
34+ exit 1
35+fi
36+ 
37+# 配置读取分层:环境变量 > 本地 ebs-build.conf(与 ebs-build.sh conf_get 一致)
38+KERNEL_REPO="${KERNEL_REPO:-}"
39+if [ -z "$KERNEL_REPO" ] && [ -f ebs-build.conf ]; then
40+ KERNEL_REPO="$(sed -nE 's/^[[:space:]]*KERNEL_REPO[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$/\1/p' ebs-build.conf | head -1)"
41+fi
42+if [ -z "$KERNEL_REPO" ]; then
43+ echo "ERROR: KERNEL_REPO not set." >&2
44+ echo "Add a line to your local ebs-build.conf (gitignored):" >&2
45+ echo " KERNEL_REPO=~/git/openeuler/kernel # or a git URL" >&2
46+ echo "or override per-invocation:" >&2
47+ echo " KERNEL_REPO=/path/to/kernel bash scripts/gen-source-tarball.sh" >&2
48+ exit 1
49+fi
50+ 
51+work="$KERNEL_REPO"
52+tmp_clone=""
53+if git -C "$KERNEL_REPO" rev-parse --git-dir >/dev/null 2>&1; then
54+ if ! git -C "$KERNEL_REPO" rev-parse -q --verify "refs/tags/$tag" >/dev/null 2>&1; then
55+ echo "Fetching tag $tag from origin ..."
56+ git -C "$KERNEL_REPO" fetch origin "refs/tags/$tag:refs/tags/$tag"
57+ fi
58+else
59+ tmp_clone="$(mktemp -d)"
60+ echo "Cloning $KERNEL_REPO at $tag (shallow) ..."
61+ if ! git clone --depth 1 --branch "$tag" "$KERNEL_REPO" "$tmp_clone" >/dev/null 2>&1; then
62+ echo "ERROR: failed to fetch $KERNEL_REPO at tag $tag" >&2
63+ rm -rf "$tmp_clone"
64+ exit 1
65+ fi
66+ work="$tmp_clone"
67+fi
68+ 
69+echo "Archiving $tag -> kernel.tar.gz ..."
70+git -C "$work" archive --format=tar.gz --prefix=kernel/ "$tag" -o "$repo_root/kernel.tar.gz"
71+[ -n "$tmp_clone" ] && rm -rf "$tmp_clone"
72+ 
73+echo "Done: $(ls -lh kernel.tar.gz | awk '{print $5}')"