pub fn push_error(&mut self, task: Option<&str>, field: Option<&str>, message: impl Into<String>) {
pub fn push_warning(&mut self, task: Option<&str>, field: Option<&str>, message: impl Into<String>) {
self.validate_legacy_secret_settings_usage(None, &self.validation_legacy_secret_settings(), report);
if task.cache.as_ref().map(|cache| cache.enabled).unwrap_or(false) && task.outputs.is_empty() {
"Cached task depends_on other tasks but declares no inputs; dependency side effects may bypass cache invalidation",
"Cached task contains shell-derived or runtime-derived command inputs but declares no inputs; cache invalidation may miss external changes",
fn validate_labels(&self, task_name: &str, task: &super::TaskArgs, report: &mut ValidationReport) {
let effective = merge_optional_secret_settings(Some(legacy.clone()), secrets_block.cloned());
fn validate_command(&self, task_name: &str, command: &CommandRunner, report: &mut ValidationReport) {
&& !has_default_containerfile(&self.resolve_from_config(&container_build.container_build.context))
"No explicit containerfile set and no Dockerfile or Containerfile was found in the build context",
fn validate_command_outputs(&self, task_name: &str, task: &super::TaskArgs, report: &mut ValidationReport) {
CommandRunner::ContainerRun(_) | CommandRunner::ContainerBuild(_) | CommandRunner::TaskRun(_) => {},
CommandRunner::ContainerRun(_) | CommandRunner::ContainerBuild(_) | CommandRunner::TaskRun(_) => false,
fn test_validate_allows_pgp_backend_without_keys_location_when_default_applies() -> anyhow::Result<()> {
fn test_validate_rejects_conflicting_legacy_and_secrets_block_values() -> anyhow::Result<()> {
"Cached task contains shell-derived or runtime-derived command inputs but declares no inputs; cache invalidation may miss external changes"
fn test_validate_does_not_warn_for_cached_dynamic_command_with_inputs() -> anyhow::Result<()> {
"Cached task contains shell-derived or runtime-derived command inputs but declares no inputs; cache invalidation may miss external changes"