已关闭
[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
已关闭
共 8 个文件变更+922-12
| @@ -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 | ## Layout | 9 | ## Layout |
| 8 | 10 | ||
| 9 | - `kernel.spec` — RPM spec for building the kernel package | 11 | - `kernel.spec` — RPM spec for building the kernel package |
| 10 | -- `kernel-aarch64.config` / `kernel-x86_64.config` / `kernel-riscv64.config` — per-architecture kernel configs | 12 | +- `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 | -## Build | 25 | +## 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 | ```bash | 64 | ```bash |
| 17 | -rpmbuild -ba kernel.spec | 65 | +./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 | ## Reference | 81 | ## 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 spec | 10 | - `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 | ```bash | 57 | ```bash |
| 17 | -rpmbuild -ba kernel.spec | 58 | +./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 40 | 42 | %global upstream_sublevel 40 |
| 43 | %global devel_release 0 | 43 | %global devel_release 0 |
| 44 | %global maintenance_release .0.0 | 44 | %global maintenance_release .0.0 |
| 45 | -%global pkg_release .13 | 45 | +%global pkg_release .14 |
| 46 | 46 | ||
| 47 | %global openeuler_lts 0 | 47 | %global openeuler_lts 0 |
| 48 | %global openeuler_major 0 | 48 | %global openeuler_major 0 |
| @@ -1119,6 +1119,13 @@ fi | |||
| 1119 | %endif | 1119 | %endif |
| 1120 | 1120 | ||
| 1121 | %changelog | 1121 | %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.13 | 1129 | * 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 placeholder | 1130 | - Add README and README_cn, drop empty readme placeholder |
| 1124 | - config: enable wifi/bluetooth/touchpad drivers for DevStation | 1131 | - config: enable wifi/bluetooth/touchpad drivers for DevStation |
| @@ -1,4 +1,5 @@ | |||
| 1 | #!/bin/bash | 1 | #!/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 build | 5 | # 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}')" | ||