Skip to content

Kern src pkger#4239

Closed
psachdeva-ms wants to merge 4 commits intomicrosoft:mainfrom
psachdeva-ms:kern_src_pkger
Closed

Kern src pkger#4239
psachdeva-ms wants to merge 4 commits intomicrosoft:mainfrom
psachdeva-ms:kern_src_pkger

Conversation

@psachdeva-ms
Copy link
Collaborator

This adds a new Transformer: KernelSourcePackager - It compliments the existing KernelSourceInstaller, where instead of building and installing the kernel on the node, it just create a .deb package(for now) which can be used to boot from and test the kernel.
The reason for doing this is for a specific use case - where for continuous testing there can be one builder node which takes care of doing all the builds, meanwhile target nodes can be deployed to use the created .deb packages and testing can happen more efficiently saving time spent freshly cloning and building the kernel.
The KernelSourcePackager also makes use of a new RepoWorktree Transformer, that can be used to build different iterations of the same base repository more efficiently by adding them as worktrees.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces the KernelSourcePackager transformer to create .deb packages from kernel source code, complementing the existing KernelSourceInstaller for improved continuous testing workflows. It also adds a RepoWorktree transformer for efficient multi-iteration builds using git worktrees.

Changes:

  • New KernelSourcePackager transformer that builds kernel .deb packages with caching support
  • New RepoWorktree transformer for managing git worktrees efficiently
  • Enhanced kernel_source_installer.py with ccache support and additional build dependencies
  • Registration of new transformer in mixin_modules.py

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 21 comments.

File Description
lisa/transformers/kernel_source_packager.py Implements KernelSourcePackager transformer with build, cache, and package management functionality, plus RepoWorktree for git worktree operations
lisa/transformers/kernel_source_installer.py Adds use_ccache parameter to _build_code, installs packaging dependencies (libdw-dev, fakeroot, dpkg-dev, debhelper-compat), and improves directory permission logic
lisa/mixin_modules.py Registers the new kernel_source_packager module for transformer discovery

Comment on lines 379 to 385
make_args = "CC='ccache gcc'"
node.execute(f"export CCACHE_DIR="
f"{str(code_path.parent)}/.ccache"
)

# set timeout to 2 hours
make.make(arguments="", cwd=code_path, timeout=60 * 60 * 2)
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The execute method is missing the shell=True parameter. The export command requires shell=True to work properly. Additionally, the command will only set the environment variable for that single execute call - it won't persist for the subsequent make.make() call. Consider using a different approach to set CCACHE_DIR, such as passing it as part of the make arguments.

Suggested change
make_args = "CC='ccache gcc'"
node.execute(f"export CCACHE_DIR="
f"{str(code_path.parent)}/.ccache"
)
# set timeout to 2 hours
make.make(arguments="", cwd=code_path, timeout=60 * 60 * 2)
ccache_dir = f"{str(code_path.parent)}/.ccache"
make_args = f"CC='ccache gcc' CCACHE_DIR={ccache_dir}"
# set timeout to 2 hours
if make_args:
# Run make with ccache-related environment variables applied only
# to this command via the shell.
node.execute(
f"{make_args} make",
cwd=code_path,
shell=True,
timeout=60 * 60 * 2,
)
else:
make.make(arguments="", cwd=code_path, timeout=60 * 60 * 2)

Copilot uses AI. Check for mistakes.
package_paths = None

if metadata:
# Remove [any existing entry with the same commit_id
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent bracket placement in the comment. The opening bracket '[' should be removed or the comment should be reformatted for clarity.

Suggested change
# Remove [any existing entry with the same commit_id
# Remove any existing entry with the same commit_id

Copilot uses AI. Check for mistakes.
from lisa.tools import Echo, Git
from lisa.util import subclasses

# from lisa.transformer import Transformer
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out import statement should be removed. If it's not needed, clean it up; if it is needed, uncomment it. Dead code should not be left in the codebase.

Suggested change
# from lisa.transformer import Transformer

Copilot uses AI. Check for mistakes.
self._log.info(f"Verified kernel source is on branch '{current_branch}'.")

# 4. Build the kernel (reuse SourceInstaller, but do NOT install)
kconfig_file = getattr(runbook, "kernel_config_file", None)
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code attempts to access 'kernel_config_file' attribute on the runbook object, but the runbook is of type KernelSourcePackagerSchema which doesn't have this attribute. This will raise an AttributeError. The 'kernel_config_file' field exists in SourceInstallerSchema, not in KernelSourcePackagerSchema. You need to either add this field to KernelSourcePackagerSchema or access it through the appropriate schema.

Suggested change
kconfig_file = getattr(runbook, "kernel_config_file", None)
source_runbook = getattr(source_installer, "runbook", None)
kconfig_file = (
getattr(source_runbook, "kernel_config_file", None)
if source_runbook is not None
else None
)

Copilot uses AI. Check for mistakes.
Comment on lines +437 to +441
remotes = git.remote_list(code_path)
self._log.debug(f"existing remotes: {remotes}")
for remote in remotes:
if runbook.worktree_repo == git.remote_get_url(code_path, remote):
remote_exists = True
break

if not remote_exists:
remote = runbook.worktree_name
self._log.info(f"adding remote {remote} for {runbook.worktree_repo}")
git.remote_add(cwd=code_path, name=remote, url=runbook.worktree_repo)
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code calls git.remote_list(), git.remote_get_url(), and git.remote_add() methods that do not exist in the Git tool class. These methods are not implemented in lisa/tools/git.py, which will cause AttributeError exceptions at runtime. You need to either implement these methods in the Git class or use direct git commands via node.execute().

Suggested change
remotes = git.remote_list(code_path)
self._log.debug(f"existing remotes: {remotes}")
for remote in remotes:
if runbook.worktree_repo == git.remote_get_url(code_path, remote):
remote_exists = True
break
if not remote_exists:
remote = runbook.worktree_name
self._log.info(f"adding remote {remote} for {runbook.worktree_repo}")
git.remote_add(cwd=code_path, name=remote, url=runbook.worktree_repo)
remotes_result = self._node.execute(
"git remote",
cwd=str(code_path),
)
remotes = [
r.strip()
for r in remotes_result.stdout.splitlines()
if r.strip()
]
self._log.debug(f"existing remotes: {remotes}")
for remote_name in remotes:
remote_url_result = self._node.execute(
f"git remote get-url {remote_name}",
cwd=str(code_path),
)
if runbook.worktree_repo == remote_url_result.stdout.strip():
remote_exists = True
remote = remote_name
break
if not remote_exists:
remote = runbook.worktree_name
self._log.info(f"adding remote {remote} for {runbook.worktree_repo}")
self._node.execute(
f"git remote add {remote} {runbook.worktree_repo}",
cwd=str(code_path),
)

Copilot uses AI. Check for mistakes.
Comment on lines +456 to +472
worktree_path = code_path.parent / runbook.worktree_name
git.worktree_prune(cwd=code_path)
if not git.worktree_exists(cwd=code_path, path=str(worktree_path)):
self._log.info(
f"creating a new worktree at {worktree_path} "
f"pointing at {remote}/{runbook.worktree_ref}"
)
git.worktree_add(
cwd=code_path,
path=worktree_path,
remote=remote,
remote_ref=runbook.worktree_ref,
new_branch=runbook.worktree_local_branch,
track=True,
)

latest_commit_id = git.get_latest_commit_id(cwd=worktree_path)
self._log.info(f"Kernel HEAD is now at : {latest_commit_id}")
return worktree_path

# worktree exists
target_ref = runbook.worktree_ref
target_path = worktree_path
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code calls git.worktree_prune(), git.worktree_exists(), and git.worktree_add() methods that do not exist in the Git tool class. These methods are not implemented in lisa/tools/git.py, which will cause AttributeError exceptions at runtime. You need to either implement these methods in the Git class or use alternative approaches to manage worktrees.

Suggested change
worktree_path = code_path.parent / runbook.worktree_name
git.worktree_prune(cwd=code_path)
if not git.worktree_exists(cwd=code_path, path=str(worktree_path)):
self._log.info(
f"creating a new worktree at {worktree_path} "
f"pointing at {remote}/{runbook.worktree_ref}"
)
git.worktree_add(
cwd=code_path,
path=worktree_path,
remote=remote,
remote_ref=runbook.worktree_ref,
new_branch=runbook.worktree_local_branch,
track=True,
)
latest_commit_id = git.get_latest_commit_id(cwd=worktree_path)
self._log.info(f"Kernel HEAD is now at : {latest_commit_id}")
return worktree_path
# worktree exists
target_ref = runbook.worktree_ref
target_path = worktree_path
worktree_prune = getattr(git, "worktree_prune", None)
worktree_exists = getattr(git, "worktree_exists", None)
worktree_add = getattr(git, "worktree_add", None)
if not (
callable(worktree_prune)
and callable(worktree_exists)
and callable(worktree_add)
):
self._log.warning(
"Git worktree operations are not supported by the current "
"Git tool implementation. Proceeding without using a "
"separate worktree."
)
else:
worktree_path = code_path.parent / runbook.worktree_name
worktree_prune(cwd=code_path)
if not worktree_exists(cwd=code_path, path=str(worktree_path)):
self._log.info(
f"creating a new worktree at {worktree_path} "
f"pointing at {remote}/{runbook.worktree_ref}"
)
worktree_add(
cwd=code_path,
path=worktree_path,
remote=remote,
remote_ref=runbook.worktree_ref,
new_branch=runbook.worktree_local_branch,
track=True,
)
latest_commit_id = git.get_latest_commit_id(cwd=worktree_path)
self._log.info(f"Kernel HEAD is now at : {latest_commit_id}")
return worktree_path
# worktree exists
target_ref = runbook.worktree_ref
target_path = worktree_path

Copilot uses AI. Check for mistakes.
"""
node = self._node
runbook: KernelSourcePackagerSchema = self.runbook
parent_dir = str(self._code_path.parent)
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable parent_dir is not used.

Suggested change
parent_dir = str(self._code_path.parent)

Copilot uses AI. Check for mistakes.
from datetime import datetime
from dataclasses import dataclass, field
from dataclasses_json import dataclass_json
from pathlib import Path, PurePath
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'Path' is not used.

Suggested change
from pathlib import Path, PurePath
from pathlib import PurePath

Copilot uses AI. Check for mistakes.
BaseLocationSchema,
SourceInstaller,
SourceInstallerSchema,
RepoLocation,
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'RepoLocation' is not used.

Suggested change
RepoLocation,

Copilot uses AI. Check for mistakes.
import lisa.transformers.dump_variables # noqa: F401
import lisa.transformers.file_uploader # noqa: F401
import lisa.transformers.kernel_source_installer # noqa: F401
import lisa.transformers.kernel_source_packager # noqa: F401
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'lisa' is not used.

Copilot uses AI. Check for mistakes.
…el package

Like kenel_source_installer this transformer pulls and kernel repo, builds it
and creates a kernel debian package(for now) out of it. This package can be
installed using the deb_package_installer transformer.

Signed-off-by: Piyush Sachdeva <[email protected]>
Signed-off-by: Piyush Sachdeva <[email protected]>
The RepoWorktree transformers gives the user the ability to work with
git-worktree when maintaining kernel repo on a node. In case of an existing
source, it uses the git-worktree functionality to effectively maintain multiple
copies of the code. In case the fields necessary for a worktree are not specified
the functionality falls back to `source` installer transformer.

Signed-off-by: Piyush Sachdeva <[email protected]>
Signed-off-by: Piyush Sachdeva <[email protected]>
Only conditionally create the code folder.

Signed-off-by: Piyush Sachdeva <[email protected]>
Signed-off-by: Piyush Sachdeva <[email protected]>
make_args = "CC='ccache gcc'"
node.execute(f"export CCACHE_DIR="
f"{str(code_path.parent)}/.ccache"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you tested it? probably it needs shell=True

)
else:
self._log.info("No-cache mode: building and packaging kernel.")
ret = self._build_and_package(source_installer, commit_id, kernel_version)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need_build = True

if runbook.use_cache:
    self._log.info("Checking for cached kernel packages...")
    if self._check_cache(commit_id, kernel_version):
        self._log.info("Cache hit: using cached package.")
        ret = self._update_cache(commit_id=commit_id)
        need_build = False
    else:
        self._log.info("Cache miss: building and packaging kernel.")
else:
    self._log.info("No-cache mode: building and packaging kernel.")

if need_build:
    ret = self._build_and_package(source_installer, commit_id, kernel_version)

ret = self._build_and_package(source_installer, commit_id, kernel_version)

if ret is None:
raise Exception("No images retrieved. kernel_source_packager_error")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the meaning of the exception message.

package_paths,
)

def _build_and_package(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

give a meaningful name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants