fix(templating): SplitManifests must preserve line endings for downstream YAML parsers#31952
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Helm’s manifest splitting and post-renderer merge logic to preserve trailing line endings so downstream YAML parsing/round-tripping (via kio.ParseAll) behaves consistently with non-post-renderer code paths—especially around YAML block scalars and document separators.
Changes:
- Update
releaseutil.SplitManifeststo trim only leading whitespace (preserving trailing newlines/whitespace). - Rework
action.annotateAndMergeto split per-file content viareleaseutil.SplitManifests, sort by in-file order, and then parse/annotate each document. - Expand and restructure tests to cover block-scalar chomping and glued-separator behaviors.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| pkg/release/v1/util/manifest.go | Preserve trailing line endings by switching from TrimSpace to left-trim only; adds Chart API v3 note. |
| pkg/release/v1/util/manifest_test.go | Replaces single-case test with a table-driven suite covering separators and trailing newline preservation. |
| pkg/action/action.go | Uses SplitManifests + deterministic ordering before YAML parsing/annotation in post-renderer merge path. |
| pkg/action/action_test.go | Removes fixDocSeparators tests and greatly expands annotateAndMerge test coverage (including block scalars and separator edge cases). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| postrenderer.helm.sh/postrender-filename: 'templates/cm.yaml' | ||
| data: | ||
| key: value | ||
| key: |- |
There was a problem hiding this comment.
As noted in #31941 (comment), this is an example of where the regex utilized by SplitManifests causes issues again and behaviour differs from Helm 3. Helm 3 would produce this file with clip block scalar mode (i.e. just |, no modifier), not with the chomping like it's returned here.
If it is desired to fully restore Helm 3's behaviour, that's an issue. Playing around some more with Helm 3, there are further differences surrounding trailing newline/whitespace handling related to this. Take the following template as an example:
apiVersion: v1
kind: ConfigMap
metadata:
name: test-cm
name: test
data:
key: |+
hello
Helm 3 produces the following output:
apiVersion: v1
kind: ConfigMap
metadata:
name: test-cm
name: test
data:
key: |
hello
That is it strips all but the first trailing newline.
This suggests to me that the "more correct" fix (to align with Helm 3) would be to perform both-sided trimming in SplitManifests after all, but always append a newline at the end of each document that is split produced. That would lead kio parsing to leave | and |- alone, but transform |+ into |. Furthermore, templates that originally didn't end in a newline (a test case that is missing here, I think, at least with a block scalar) would also match Helm 3's output.
That fix could be entirely localized to action.annotateAndMerge, leaving SplitManifests alone otherwise.
YAML block scalars are a mess, dealing with them correctly involves a ton of edge cases.
There was a problem hiding this comment.
I guess this is a bug in Helm 3 (that was carried over to Helm 4). |+ is specifically to preserve multiple newlines. I vote for fixing this as a long-time bug, without this fix Helm basically does not support |+.
There was a problem hiding this comment.
Very much agreed. That's why I emphasized the if on restoring Helm 3 behaviour. Arguably, Helm 4 with a post renderer is most compliant with the YAML or at least best at preserving user intent. Both Helm 3 and 4 without post renderers have deficiencies in that regard.
That notwithstanding, the test case on the line I've put this comment on is an indicator of where this PR would produce incorrect output/change user intent. If I have a document before a separator that ends with a "clip" block scalar, that ought to be preserved and not changed into a chomp modifier.
I should've left the other remark as a separate comment, got a bit carried away there 😅
There was a problem hiding this comment.
@scottrigby Arguably this could be backported to Helm 3.20, but at the same time I don't think the impact in Helm 3 users of this bug is wide, it would have surfaced years ago if it was. So while this is a bug from Helm 3, I think we could fix it only in Helm 4.
There was a problem hiding this comment.
@marvin-roesch We are planning to backport this PR to our fork and making a Flux patch release. Do you think merging this one as is will make it worse? Do you have a suggestion for fixing this test case?
There was a problem hiding this comment.
I don't have an easy way to fix this in mind. If Go's regex engine supported lookaheads, we could just use that for the preceding whitespace from the splitting regex and be fine, I think. Without that, I think a more "manual" approach to splitting like you implemented in the previous fix would be required.
As for the impact of merging this iteration: I guess multi document templates are rare enough as is, and trimmed whitespace at the end only causes problems in certain situations, too. Since we're templating Kubernetes resources, it's also always an option to move e.g. the kind: ... line to the end of the document to preserve any whitespace there. Documenting this edge case with a suggested workaround in the release notes might be wise.
…ream YAML parsers Signed-off-by: Matheus Pimenta <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code reviewFound 1 issue:
helm/internal/release/v2/util/manifest.go Lines 45 to 58 in 2202747 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Since at this point rendering Charts v2 and v3 is pretty much the same, I suggest making sure this fix is there as well so as not to diverge |
Signed-off-by: Matheus Pimenta <[email protected]>
Signed-off-by: Matheus Pimenta <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
pkg/release/v1/util/manifest.go:47
- The doc comment for SplitManifests contains informal/unprofessional phrasing (e.g., “kill this regex with fire”, “Helm should say a big NO”). Since this is effectively public documentation, please reword it in a neutral/professional tone while keeping the same technical intent.
// **Note for Chart API v3**: This function (due to the regex above) has allowed _WRONG_
// Go templates to be defined inside charts across the years. The generated text from Go
// templates may contain `---apiVersion: v1`, and this function magically splits this back
// to `---\napiVersion: v1`. This has caused issues recently after Helm 4 introduced
// kio.ParseAll to inject annotations when post-renderers are used. In Chart API v3,
// we should kill this regex with fire (or change it) and expose charts doing the wrong
// thing Go template-wise. Helm should say a big _NO_ to charts doing the wrong thing,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Matheus Pimenta <[email protected]>
TerryHowe
left a comment
There was a problem hiding this comment.
/lgtm
Are we concerned about the change in yaml output format with the extra line?
| // kio.ParseAll to inject annotations when post-renderers are used. In Chart API v3, | ||
| // we should kill this regex with fire (or change it) and expose charts doing the wrong |
There was a problem hiding this comment.
I don't like regex for the performance hit. I'd like to see it gone.
I think it's ok, it's a direct consequence of the change, which is a bugfix for |
|
@marvin-roesch The PR already has two approvals, do you have any major concerns? Otherwise I'll ping the maintainers to merge it and we'll ship it in our Flux fork |
|
I think all my concerns have been addressed, thanks! I agree that this should be dealt with ultimately in Chart V3. Looking forward to have this shipped in Flux! We have a few charts with workarounds right now 😅 |
…ream YAML parsers (helm#31952) * fix(templating): SplitManifests must preserve line endings for downstream YAML parsers Signed-off-by: Matheus Pimenta <[email protected]> * Address copilot comment about skipping empty docs Signed-off-by: Matheus Pimenta <[email protected]> * Port fix to release v2 Signed-off-by: Matheus Pimenta <[email protected]> * Address copilot comments Signed-off-by: Matheus Pimenta <[email protected]> --------- Signed-off-by: Matheus Pimenta <[email protected]>
Fixes: #31948
Builds on: #31941
Please review and merge #31941 first.