From e28d0ca8edd4c5e9f8429421cf4ab57c414c059b Mon Sep 17 00:00:00 2001 From: Malte Poll <1780588+malt3@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:42:08 +0100 Subject: [PATCH] pkl.project: check symlink targets The pkl module extension uses symlinks to refer to the PklProject and PklProject.deps.json files. The rctx.symlink function does not guarantee that the symlink points at a real file on the filesystem and it doesn't watch the file for changes. With this change, we ensure the file exists before symlinking to it and watch it for changes (in case it's deleted by the user). --- pkl/private/pkl_project.bzl | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/pkl/private/pkl_project.bzl b/pkl/private/pkl_project.bzl index 4693354..60fb1f2 100644 --- a/pkl/private/pkl_project.bzl +++ b/pkl/private/pkl_project.bzl @@ -89,17 +89,35 @@ def convert_dict_to_options(option_name, items_dict): list.append("{key}={value}".format(key = key, value = value)) return list +def _watch_symlink_target(rctx, label): + """Validates that a label points to an existing file and watches it for changes. + + Args: + rctx: The repository context. + label: The label to validate. + Returns: + The resolved path. + """ + path = rctx.path(label) + if not path.exists: + fail("Cannot symlink '{}': file does not exist.".format(label)) + rctx.watch(path) + return path + def _pkl_project_impl(rctx): - packages = parse_pkl_project_deps_json(rctx.read(rctx.attr.pkl_project_deps)) + pkl_project_path = _watch_symlink_target(rctx, rctx.attr.pkl_project) + pkl_project_deps_path = _watch_symlink_target(rctx, rctx.attr.pkl_project_deps) + + packages = parse_pkl_project_deps_json(rctx.read(pkl_project_deps_path)) targets_for_all = [] for package in packages: targets_for_all.append("@%s//:item" % package.workspace_name) - rctx.symlink(rctx.attr.pkl_project, "PklProject") - rctx.symlink(rctx.path(rctx.attr.pkl_project_deps), "PklProject.deps.json") + rctx.symlink(pkl_project_path, "PklProject") + rctx.symlink(pkl_project_deps_path, "PklProject.deps.json") env_vars = convert_dict_to_options("--env-var", rctx.attr.environment) - pkl_project_metadata = _eval_pkl_project(rctx, "PklProject", extra_args = env_vars + rctx.attr.extra_flags) + pkl_project_metadata = _eval_pkl_project(rctx, pkl_project_path, extra_args = env_vars + rctx.attr.extra_flags) has_package = "package" in pkl_project_metadata build_bazel_content = "" @@ -138,7 +156,8 @@ pkl_cache( rctx.file("BUILD.bazel", content = build_bazel_content, executable = False) def _pkl_project_http_rewrites_impl(rctx): - rules = _eval_pkl_project(rctx, rctx.path(rctx.attr.pkl_project), extra_args = [ + pkl_project_path = _watch_symlink_target(rctx, rctx.attr.pkl_project) + rules = _eval_pkl_project(rctx, pkl_project_path, extra_args = [ "--expression", # Render the rewrite rules as list, to make sure the order is preserved. """