diff --git a/deployment/schema.json b/deployment/schema.json index 4ca6683..387300e 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -34,6 +34,11 @@ }, "newLineKind": { "$ref": "#/definitions/newLineKind" + }, + "healthcheckCmdNewLine": { + "description": "Whether to always break a HEALTHCHECK command onto its own continuation line when the instruction has options, even if it would fit on one line.", + "default": false, + "type": "boolean" } } } diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index c3a3aa4..16b49fb 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -64,6 +64,13 @@ impl ConfigurationBuilder { self.insert("newLineKind", value.to_string().into()) } + /// Whether to always break a `HEALTHCHECK` command onto its own continuation + /// line when the instruction has options, even if it would fit on one line. + /// Default: `false` + pub fn healthcheck_cmd_new_line(&mut self, value: bool) -> &mut Self { + self.insert("healthcheckCmdNewLine", value.into()) + } + #[cfg(test)] pub(super) fn get_inner_config(&self) -> ConfigKeyMap { self.config.clone() @@ -85,10 +92,13 @@ mod tests { #[test] fn check_all_values_set() { let mut config = ConfigurationBuilder::new(); - config.new_line_kind(NewLineKind::CarriageReturnLineFeed).line_width(90); + config + .new_line_kind(NewLineKind::CarriageReturnLineFeed) + .line_width(90) + .healthcheck_cmd_new_line(true); let inner_config = config.get_inner_config(); - assert_eq!(inner_config.len(), 2); + assert_eq!(inner_config.len(), 3); let diagnostics = resolve_config(inner_config, &Default::default()).diagnostics; assert_eq!(diagnostics.len(), 0); } diff --git a/src/configuration/configuration.rs b/src/configuration/configuration.rs index 1aa7929..3941244 100644 --- a/src/configuration/configuration.rs +++ b/src/configuration/configuration.rs @@ -7,4 +7,7 @@ use serde::Serialize; pub struct Configuration { pub line_width: u32, pub new_line_kind: NewLineKind, + /// Whether to always break a `HEALTHCHECK` command onto its own continuation + /// line when the instruction has options, even if it would fit on one line. + pub healthcheck_cmd_new_line: bool, } diff --git a/src/configuration/resolve_config.rs b/src/configuration/resolve_config.rs index 664dadf..3fb417d 100644 --- a/src/configuration/resolve_config.rs +++ b/src/configuration/resolve_config.rs @@ -40,6 +40,7 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration) global_config.new_line_kind.unwrap_or(RECOMMENDED_GLOBAL_CONFIGURATION.new_line_kind), &mut diagnostics, ), + healthcheck_cmd_new_line: get_value(&mut config, "healthcheckCmdNewLine", false, &mut diagnostics), }; diagnostics.extend(get_unknown_property_diagnostics(config)); diff --git a/src/generation/context.rs b/src/generation/context.rs index b22596d..7533c5a 100644 --- a/src/generation/context.rs +++ b/src/generation/context.rs @@ -9,7 +9,7 @@ use super::helpers::parse_comments; use crate::configuration::Configuration; pub struct Context<'a> { - pub _config: &'a Configuration, + pub config: &'a Configuration, pub dockerfile: &'a Dockerfile, pub text: &'a str, pub handled_comments: HashSet, @@ -27,7 +27,7 @@ pub struct Context<'a> { impl<'a> Context<'a> { pub fn new(text: &'a str, dockerfile: &'a Dockerfile, config: &'a Configuration) -> Self { Self { - _config: config, + config, text, dockerfile, handled_comments: HashSet::new(), diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 1b9fecd..ca3c07e 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -323,11 +323,13 @@ fn gen_healthcheck_instruction<'a>(node: &'a HealthcheckInstruction, context: &m // group the options and the nested command so they sit on one line when they // fit under the line width and otherwise break onto an aligned continuation - // line (#29). a break the author already wrote forces the multi-line form. + // line (#29). a break the author already wrote forces the multi-line form, as + // does `healthcheckCmdNewLine` when there's a command preceded by options. let first_line = node.span.relative_span(context.dockerfile).0; let command_span = node.cmd.as_ref().map(|c| c.span()).unwrap_or(node.span); let command_line = command_span.relative_span(context.dockerfile).0; - let force_use_new_lines = command_line > first_line; + let proactive_split = context.config.healthcheck_cmd_new_line && node.cmd.is_some() && !node.flags.is_empty(); + let force_use_new_lines = command_line > first_line || proactive_split; items.extend(gen_grouped_values( vec![(flags_items, first_line), (command_items, command_line)], HEALTHCHECK_CONTINUATION_INDENT, diff --git a/tests/specs/Healthcheck/Healthcheck_CmdNewLine.txt b/tests/specs/Healthcheck/Healthcheck_CmdNewLine.txt new file mode 100644 index 0000000..d351e56 --- /dev/null +++ b/tests/specs/Healthcheck/Healthcheck_CmdNewLine.txt @@ -0,0 +1,33 @@ +~~ healthcheckCmdNewLine: true ~~ +== should split CMD onto its own line when there are options == +HEALTHCHECK --interval=30s CMD curl -f http://localhost/ + +[expect] +HEALTHCHECK --interval=30s \ + CMD curl -f http://localhost/ + +== should split before an exec-form command == +HEALTHCHECK --retries=3 CMD ["curl", "-f", "http://localhost/"] + +[expect] +HEALTHCHECK --retries=3 \ + CMD ["curl", "-f", "http://localhost/"] + +== should not split when there are no options == +HEALTHCHECK CMD curl -f http://localhost/ + +[expect] +HEALTHCHECK CMD curl -f http://localhost/ + +== should not split NONE == +HEALTHCHECK --interval=30s NONE + +[expect] +HEALTHCHECK --interval=30s NONE + +== should collapse a continuation before NONE == +HEALTHCHECK --interval=30s \ + NONE + +[expect] +HEALTHCHECK --interval=30s NONE