fix: balance conditional nesting when removing spec sections#190
fix: balance conditional nesting when removing spec sections#190trungams wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates RPM spec section/subpackage removal to preserve balanced %if/%endif nesting when removing section ranges, addressing invalid specs caused by boundary conditionals.
Changes:
- Adds conditional pair collection and range balancing before section removal.
- Updates
RemoveSubpackagedocumentation and overlay docs for conditional handling. - Adds tests for conditionalized subpackage removal scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
internal/rpm/spec/edit.go |
Adds conditional balancing logic for section range removal. |
internal/rpm/spec/edit_test.go |
Adds coverage for conditional removal cases. |
docs/user/reference/config/overlays.md |
Documents conditional behavior for subpackage removal overlays. |
Comments suppressed due to low confidence (1)
internal/rpm/spec/edit.go:1004
- This validation only handles straddling
%iflines. When the straddling line is a%endifand there is real content later in the same section range,balanceRangetrims at the%endifand leaves that later section content behind even though the caller requested the whole section/subpackage be removed. Either treat content after a straddling%endifasErrConditionalSpansSections, or use a removal representation that can preserve the%endifwhile still deleting the remaining section body.
if ifInside {
// The %if is inside our range but %endif is outside. Check if there's
// real content between the %if and the range end — that would mean the
// conditional protects section content that spans into the next section.
for i := p.ifLine + 1; i < r.end; i++ {
if !isBlankOrComment(rawLines[i]) && conditionalDepthChange(rawLines[i]) == 0 {
return r, fmt.Errorf(
c20b949 to
d7b1b2e
Compare
d7b1b2e to
26e0634
Compare
| // spec's conditional structure. If a conditional block is interleaved with section | ||
| // content in a way that cannot be resolved by trimming, an [ErrConditionalSpansSections] | ||
| // error is returned. | ||
| func (s *Spec) collectSectionRanges( |
There was a problem hiding this comment.
Can you tell if spec-append-lines et al. suffer from the same problem? I'm assuming we may need to update them to use this code path too. Otherwise, the lines might get appended after an %ifdef that wraps a succeeding section.
IIRC, that was one of the reasons that Tobias had added spec-insert-tag as a workaround.
I'm okay if you want to split that off as a separate PR, but since you're already in this code path / logic would be great if you could sort that out too.
There was a problem hiding this comment.
I think it does, since the limitation initially comes from Spec.Visit not accounting for conditionals in section ranges. I need a bit more time to think of a direct improvement to Spec.Visit, so I'd prefer to do it in a follow-up PR. The solution I'm adding here is meant to go on top of collectSectionRanges to have as little impact on existing spec files as possible.
There was a problem hiding this comment.
Fair point. I'd like to see you continue working in this area after this PR -- to clean it up a bit. If we get more tests in place, we can have the confidence to better unify/factor the logic so it doesn't continue to accrete complexity.
| // If the straddling line is an %if (opener inside, closer outside), | ||
| // check for real content between the %if and the range end. Such content | ||
| // would belong to this section but span into the next via the conditional. | ||
| if ifInside { |
There was a problem hiding this comment.
Maybe I'm missing something but don't we want to use the same logic here when %endif was inside but its starting %if wasn't? I'd only expect that to be okay if there's no content after.
There was a problem hiding this comment.
This is already handled later in the function, on line 1035
There was a problem hiding this comment.
This is already handled later in the function, on line 1035
Understood, but isn't the conditional unneeded complexity? Or is there some other case I'm not thinking of?
There was a problem hiding this comment.
I'm trying to cover the general cases so yes, it's very unlikely this happens and implies a spec is badly written, so we need to throw an error.
| ) | ||
| } | ||
|
|
||
| // Trim trailing blank/comment lines for clean output. |
There was a problem hiding this comment.
I actually do like the trimming, but for consistency's sake can we keep this trimming consistent across both the case where we need to rebalance and we don't? (My preference would be to do it in both places.)
There was a problem hiding this comment.
I'm going to add a post-processing step that cleans up the specs after all overlays are applied
There was a problem hiding this comment.
I re-read the code and realized "for clean output" was a bit misleading and that trimming logic didn't actually do anything useful, so I removed it. Output should look consistent now
I think it's better to remove the trimming at least for this PR, it's sending me down a rabbit hole trying to properly define section boundaries and accounting for comments/whitespaces. I'll try to keep this PR simple for now
26e0634 to
b1b2aeb
Compare
b1b2aeb to
bd57684
Compare
bd57684 to
88e41c2
Compare
88e41c2 to
f579aa7
Compare
| // collapseSpecBlankLines opens a spec file, collapses consecutive blank lines, and | ||
| // writes it back. This is a post-processing step intended to clean up gaps left by | ||
| // section or tag removal overlays. | ||
| func collapseSpecBlankLines(fs opctx.FS, specPath string) error { |
There was a problem hiding this comment.
This will introduce more deviations from input upstream specs than strictly needed; is this necessary? I don't think we should be in the business of rewriting the specs for readability, unless it's scoped to the specific overlay changes we're making.
I think the "trim" you had been doing for section collection was fine because it didn't actually remove any content, it just considered the section to end a bit earlier so that appended lines / removed sections preserve the space between the sections.
(Minor note: it's also quite inconsistent that this logic gets applied unilaterally to all parts of the spec, including sections not overlaid -- but is only triggered if a certain subset of overlays are requested.)
There was a problem hiding this comment.
I was overthinking it - 2am thoughts :(
f579aa7 to
86aaa42
Compare
86aaa42 to
d476ff9
Compare
collectSectionRanges now adjusts each section range to maintain balanced %if/%endif nesting before removal. This prevents producing invalid specs when sections are adjacent to or wrapped in conditional blocks. Three cases are handled automatically: - Balanced conditionals inside a section (no change needed) - Trailing %if openers belonging to the next section (trimmed) - Trailing %endif from a wrapping conditional block (trimmed, leaving an empty but valid conditional wrapper) Conditionals interleaved with section content (spanning case) are detected and reported with an ErrConditionalSpansSections error. After each range removal, consecutive blank lines at the splice boundary are collapsed to prevent multi-blank gaps in the output. Fixes microsoft#144
d476ff9 to
a2d96f6
Compare
collectSectionRangesnow adjusts each section range to maintain balanced %if/%endif nesting before removal. This prevents producing invalid specs when sections are adjacent to or wrapped in conditional blocks.Three cases are handled automatically:
Conditionals interleaved with section content (spanning case) are detected and reported with an [ErrConditionalSpansSections] error.
Fixes #144