diff --git a/.github/workflows/build-kernel-deb.yml b/.github/workflows/build-kernel-deb.yml index e205a0d..73bf999 100644 --- a/.github/workflows/build-kernel-deb.yml +++ b/.github/workflows/build-kernel-deb.yml @@ -46,6 +46,10 @@ on: description: 'Advanced: extra config fragments applied on top of all of debian/config-available/ (e.g. intree:arch/arm64/configs/qcom_debug.config)' required: false default: '' + dkms: + description: 'Advanced: comma-separated out-of-tree DKMS modules to build and bundle, without the -dkms suffix (e.g. kgsl)' + required: false + default: '' debian-version-stub: description: 'Advanced: Debian version stub; the selected suite''s mapped suffix and a Daily-style trailing ~ are applied automatically' required: false @@ -121,6 +125,10 @@ on: description: 'Extra config fragments applied on top of all of debian/config-available/ (e.g. intree:arch/arm64/configs/qcom_debug.config)' type: string default: '' + dkms: + description: 'Comma-separated out-of-tree DKMS modules to build and bundle, without the -dkms suffix (e.g. kgsl)' + type: string + default: '' debian-revision: description: 'Debian revision component of the package version' type: string @@ -424,6 +432,7 @@ jobs: BINPKG_INPUT: ${{ inputs.binpkg || 'linux-image-qcom-next' }} KVER_EXTRA_INPUT: ${{ inputs.kver-extra }} KERNEL_CONFIG_INPUT: ${{ inputs.kernel-config }} + DKMS_INPUT: ${{ inputs.dkms }} DEBUG_BUILD_INPUT: ${{ inputs.debug-build }} run: | # No -x here: keep xtrace off for this step as it assembles the @@ -441,6 +450,7 @@ jobs: [[ -n "$LOCALVERSION" ]] && ARGS+=(--localversion "$LOCALVERSION") [[ -n "$KVER_EXTRA_INPUT" ]] && ARGS+=(--kver-extra "$KVER_EXTRA_INPUT") [[ -n "$KERNEL_CONFIG_INPUT" ]] && ARGS+=(--kernel-config "$KERNEL_CONFIG_INPUT") + [[ -n "$DKMS_INPUT" ]] && ARGS+=(--dkms "$DKMS_INPUT") [[ "$DEBUG_BUILD_INPUT" == "true" ]] && ARGS+=(--debug) docker run -i --rm \ diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index 8d82e1b..e5aaf1c 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -9,7 +9,7 @@ name: daily # Matrix schema: ci/build-matrix.json # Each row with "type": "Daily" is expanded by ci/scripts/resolve-matrix.sh: # the "suites" array is flattened into one job per suite. All other fields -# (kernel_variant, srcpkg, binpkg, kernel_config, debian_revision, +# (kernel_variant, srcpkg, binpkg, kernel_config, dkms, debian_revision, # git_clone, branch_or_tag, ref_strategy) are passed through to # build-kernel-deb.yml as inputs. # @@ -105,7 +105,7 @@ jobs: # build: one parallel job per flat matrix entry. # # Each entry calls build-kernel-deb.yml as a reusable workflow. The full - # set of matrix fields (suite, srcpkg, binpkg, kernel_config, etc.) is + # set of matrix fields (suite, srcpkg, binpkg, kernel_config, dkms, etc.) is # threaded through as inputs. fail-fast is disabled so a failed trixie # build does not cancel the forky build. # --------------------------------------------------------------------------- @@ -127,6 +127,7 @@ jobs: srcpkg: ${{ matrix.srcpkg }} binpkg: ${{ matrix.binpkg }} kernel-config: ${{ matrix.kernel_config }} + dkms: ${{ matrix.dkms }} debian-revision: ${{ matrix.debian_revision }} localversion: ${{ matrix.localversion || '' }} kver-extra: ${{ matrix.kver_extra || '' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b478ec..5952eb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,6 +109,7 @@ jobs: srcpkg: ${{ matrix.srcpkg }} binpkg: ${{ matrix.binpkg }} kernel-config: ${{ matrix.kernel_config }} + dkms: ${{ matrix.dkms }} debian-revision: ${{ matrix.debian_revision }} localversion: ${{ matrix.localversion || '' }} kver-extra: ${{ matrix.kver_extra || '' }} diff --git a/README.md b/README.md index 9f1e56c..4bf4f45 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ The final Production matrix is conceptually: "srcpkg": "linux-qcom-next", "binpkg": "linux-image-qcom-next", "kernel_config": [], + "dkms": { "trixie": ["kgsl"], "forky": ["kgsl"] }, "debian_version_stub": "0qli", "debian_version_suffix": "~", "pkg_linux_qcom_ref": "qcom/debian/latest" @@ -75,6 +76,7 @@ The final Production matrix is conceptually: "srcpkg": "linux-qcom-next", "binpkg": "linux-image-qcom-next", "kernel_config": [], + "dkms": { "trixie": ["kgsl"], "forky": ["kgsl"] }, "debian_version_stub": "0qli", "debian_version_suffix": "", "pkg_linux_qcom_ref": "qcom/debian/latest", @@ -184,6 +186,7 @@ its own values for: | `srcpkg` | Debian source package name. | | `binpkg` | Kernel image metapackage name. | | `kernel_config` | Extra fragments applied on top of `debian/config-available/`, all of which is applied to every build, one per array element. A bare name selects `debian/config-available/.config`; an `intree:` entry names a fragment shipped by the kernel source, as a path relative to the kernel source root (e.g. `intree:arch/arm64/configs/qcom_debug.config`), so it stays versioned with the kernel it targets. Empty for variants that need nothing beyond `config-available/`; today it carries only `intree:` fragments. `resolve-matrix.sh` joins it into the comma-separated `kernel-config` workflow input. | +| `dkms` | Out-of-tree DKMS modules built against the packaged kernel and bundled into its image package, named without the `-dkms` suffix (e.g. `kgsl` selects `kgsl-dkms`). Keyed by suite rather than flat, because a module need not build on every suite a row targets: `{"trixie": ["kgsl"], "forky": ["kgsl"]}` on a row that also targets `resolute` bundles `kgsl-dkms` on the first two and nothing on the third. A suite with no key builds no out-of-tree modules; a key naming a suite the row does not build is rejected. `resolve-matrix.sh` picks each leg's own list and joins it into the comma-separated `dkms` workflow input, which reaches `prepare-source.sh` as `--dkms`; a leg with no modules is built with no `--dkms` at all. | | `debian_version_stub` | Base Debian revision, shared by a variant's Daily and Release rows. Must not end in `~`; the suite suffix is derived, not stored here. | | `debian_version_suffix` | `~` for Daily rows, empty for Release rows. Documents the delivery-type half of the revision formula on the row itself; `resolve-matrix.sh` rejects a row where this disagrees with `type`, but derivation always computes this suffix from `type`, never reads this field. | | `localversion`, `kver_extra` | Optional version overrides forwarded to packaging. | @@ -198,9 +201,10 @@ duplicate suites and malformed variant identifiers before any build jobs start. It also rejects a matrix where any configured suite has no `suite_suffix_mapping` entry, where two suites share the same suffix, where a suffix is non-empty and doesn't start with `~`, where a variant's Daily -and Release rows disagree on `debian_version_stub`, or where a row's -`debian_version_suffix` doesn't match what its `type` implies — all before -any build job starts. +and Release rows disagree on `debian_version_stub`, where a row's +`debian_version_suffix` doesn't match what its `type` implies, or where a +row's `dkms` is keyed by a suite that row does not build — all before any +build job starts. Each flattened leg's final `debian_revision` is derived by `ci/scripts/derive-debian-revision.sh` from `debian_version_stub`, @@ -425,6 +429,7 @@ The available inputs are: | `srcpkg` | `linux-qcom-next` | Advanced source package identity override. | | `binpkg` | `linux-image-qcom-next` | Advanced image metapackage identity override. | | `kernel-config` | Empty | Advanced extra fragments applied on top of all of `debian/config-available/`, e.g. `intree:arch/arm64/configs/qcom_debug.config`. | +| `dkms` | Empty | Advanced comma-separated out-of-tree DKMS modules to build and bundle, without the `-dkms` suffix, e.g. `kgsl`. | | `debian-version-stub` | `0qli` | Advanced Debian version stub. The selected suite's mapped suffix and a Daily-style trailing `~` are applied automatically; direct builds always use Daily semantics since they are build-only and non-promoting. | | `localversion` | Auto-derived | Advanced explicit `LOCALVERSION` override. | | `kver-extra` | Empty | Advanced kernel-release suffix. | @@ -475,7 +480,8 @@ To add a kernel variant: 4. Give the variant distinct `srcpkg` and `binpkg` values. Set the Release `target_workspace` explicitly. 5. Confirm suite-family routing: Debian suites use Debusine; Ubuntu suites use - the Docker path. + the Docker path. Key `dkms` by the suites whose builds should bundle + out-of-tree modules, and leave out the suites that cannot build them. 6. Run a filtered Daily validation for the new variant, then its full Daily and Release flows. @@ -486,7 +492,9 @@ To add a new suite (for an existing or new variant): 2. Add the suite to the `suites` array of the relevant Daily and/or Release rows. `resolve-matrix.sh` rejects any configured suite with no mapping entry before any build job starts. -3. Choose the suffix so the suite sorts where it belongs relative to the +3. Add a `dkms` entry keyed by the new suite to those rows if its builds + should bundle out-of-tree modules. Without one the suite builds none. +4. Choose the suffix so the suite sorts where it belongs relative to the others for the same delivery type (see the ordering discussion in [Overview](#overview)). diff --git a/ci/build-matrix.json b/ci/build-matrix.json index cbb8390..2760c9d 100644 --- a/ci/build-matrix.json +++ b/ci/build-matrix.json @@ -20,6 +20,14 @@ "srcpkg": "linux-qcom-next", "binpkg": "linux-image-qcom-next", "kernel_config": [], + "dkms": { + "trixie": [ + "kgsl" + ], + "forky": [ + "kgsl" + ] + }, "debian_version_stub": "0qli", "debian_version_suffix": "~", "pkg_linux_qcom_ref": "qcom/debian/latest" @@ -38,6 +46,14 @@ "srcpkg": "linux-qcom-next", "binpkg": "linux-image-qcom-next", "kernel_config": [], + "dkms": { + "trixie": [ + "kgsl" + ], + "forky": [ + "kgsl" + ] + }, "debian_version_stub": "0qli", "debian_version_suffix": "", "pkg_linux_qcom_ref": "qcom/debian/latest" @@ -59,6 +75,14 @@ "intree:arch/arm64/configs/qcom_debug.config", "intree:kernel/configs/debug.config" ], + "dkms": { + "trixie": [ + "kgsl" + ], + "forky": [ + "kgsl" + ] + }, "debian_version_stub": "0qli", "debian_version_suffix": "~", "pkg_linux_qcom_ref": "qcom/debian/latest" @@ -80,6 +104,14 @@ "intree:arch/arm64/configs/qcom_debug.config", "intree:kernel/configs/debug.config" ], + "dkms": { + "trixie": [ + "kgsl" + ], + "forky": [ + "kgsl" + ] + }, "debian_version_stub": "0qli", "debian_version_suffix": "", "pkg_linux_qcom_ref": "qcom/debian/latest" diff --git a/ci/scripts/resolve-matrix.sh b/ci/scripts/resolve-matrix.sh index c2f63be..a66b0e5 100755 --- a/ci/scripts/resolve-matrix.sh +++ b/ci/scripts/resolve-matrix.sh @@ -11,12 +11,19 @@ set -euo pipefail # - "deliveries": the matrix rows. Each kernel_variant owns exactly one # Daily row and one Release row. A row declares every input needed by # that delivery, including a debian_version_stub. Two fields are -# list-valued: suites, which is expanded into isolated legs, and +# list-valued: suites, which is expanded into isolated legs; and # kernel_config, which is one config fragment per element. A fragment is # either a bare name from debian/config-available/ or an "intree:" entry # naming a path relative to the kernel source root # (e.g. intree:arch/arm64/configs/qcom_debug.config). # +# dkms is keyed one level deeper, by suite: an out-of-tree module is not +# buildable on every suite a row targets (kgsl has no resolute build), so +# a flat list could not say "these suites, not that one". Each suite maps +# to a list of module names without the -dkms suffix (e.g. {"trixie": +# ["kgsl"], "forky": ["kgsl"]}). A suite with no key builds no +# out-of-tree modules at all. +# # Each flattened leg's final debian_revision is derived from # debian_version_stub, suite_suffix_mapping[suite], and the delivery type via # ci/scripts/derive-debian-revision.sh, so the formula has exactly one @@ -48,7 +55,10 @@ set -euo pipefail # and a suite-specific debian_revision (debian_version_stub and # debian_version_suffix are consumed and removed). kernel_config is joined # into the comma-separated string that build-kernel-deb.yml's kernel-config -# input and prepare-source.sh's --kernel-config expect. +# input and prepare-source.sh's --kernel-config expect. dkms collapses to +# the same kind of string for the dkms input and prepare-source.sh's --dkms, +# carrying only the list keyed by that leg's own suite, so a leg whose suite +# has no key gets "" and its build skips --dkms entirely. # # Exit codes: # 0 Success, at least one entry emitted. @@ -138,6 +148,51 @@ validation_errors=$(jq -r ' else empty end; + # Each dkms entry names an out-of-tree module built against the kernel being + # packaged, given without the -dkms suffix (e.g. kgsl selects kgsl-dkms), so + # it must look like a Debian package name stem. + def dkms_module_errors($suite): + ("dkms." + $suite) as $field | + .dkms[$suite] as $modules | + if ($modules | type) != "array" + then $field + " must be an array" + elif any($modules[]; type != "string" or length == 0) + then $field + " must contain only non-empty strings" + elif any($modules[]; test(",")) + then $field + " entries must not contain commas; use one array element per module" + elif any($modules[]; test("^[a-z0-9][a-z0-9+.-]*$") | not) + then $field + " entries must be a module name without the -dkms suffix (lowercase letters, digits, and + . -)" + elif any($modules[]; endswith("-dkms")) + then $field + " entries must omit the -dkms suffix (e.g. kgsl, not kgsl-dkms)" + elif ($modules | unique | length) != ($modules | length) + then $field + " must not contain duplicates" + else empty + end; + + # dkms is keyed by suite, not flat, because a module need not be buildable + # on every suite a row targets and one row can target trixie, forky and + # resolute at once. A suite with no key builds no out-of-tree modules, so + # keys are only checked against the suites the row actually targets: a key + # for some other suite is dead config and is rejected here rather than + # sitting around looking effective. + def dkms_valid: + if (.dkms | type) != "object" + then "dkms must be an object keyed by suite (e.g. {\"trixie\": [\"kgsl\"], \"forky\": [\"kgsl\"]})" + else + . as $row + | ([$row.suites | arrays | .[] | strings]) as $row_suites + | ($row.dkms | keys) as $declared + | ( + [ + $declared[] as $suite + | select(($row_suites | index($suite)) == null) + | "dkms declares a \"" + $suite + "\" list but the row does not build suite " + $suite + ] + + + [ $declared[] as $suite | $row | dkms_module_errors($suite) ] + ) | .[] + end; + def suites_valid: if (.suites | type) != "array" or (.suites | length) == 0 then "suites must be a non-empty array" @@ -171,6 +226,7 @@ validation_errors=$(jq -r ' variant_name_valid, suites_valid, kernel_config_valid, + dkms_valid, if (.debian_version_stub | type) == "string" and (.debian_version_stub | test("~$")) then "debian_version_stub must not end in ~" else empty end, @@ -331,7 +387,11 @@ result=$(jq -c \ )[] as $suite | $row | del(.suites) - | . + {"suite": $suite, "kernel_config": ($row.kernel_config | join(","))} + | . + { + "suite": $suite, + "kernel_config": ($row.kernel_config | join(",")), + "dkms": (($row.dkms[$suite] // []) | join(",")) + } ] | if length == 0 then error(