Skip to content

fix(cobra): escape newlines, tabs, and carriage returns in kdlQuoteAlways#539

Merged
jdx merged 3 commits intojdx:mainfrom
thecodesmith:main
Mar 13, 2026
Merged

fix(cobra): escape newlines, tabs, and carriage returns in kdlQuoteAlways#539
jdx merged 3 commits intojdx:mainfrom
thecodesmith:main

Conversation

@thecodesmith
Copy link
Copy Markdown
Contributor

This change fixes the issue where Cobra help text fields with newlines would be rendered as literal newlines in the output KDL spec, which was invalid and failed to parse.

Copilot AI and others added 3 commits March 12, 2026 21:22
…ecial-characters

fix(cobra): escape newlines, tabs, and carriage returns in kdlQuoteAlways
Copilot AI review requested due to automatic review settings March 12, 2026 21:50
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves an issue where Cobra-generated KDL specifications would become invalid and fail to parse if help text fields contained special characters like newlines, tabs, or carriage returns. By correctly escaping these characters during KDL generation, the change ensures robust and parsable output, improving the reliability of the Cobra integration.

Highlights

  • KDL Escaping Fix: Implemented escaping for newline, tab, and carriage return characters in the kdlQuoteAlways function to prevent invalid KDL output.
  • Test Coverage: Added comprehensive test cases to cobra_usage_test.go to validate the correct escaping of various special characters and newlines in Cobra command and flag help text.
Changelog
  • integrations/cobra/cobra_usage_test.go
    • Added TestSpecialCharacterEscaping to verify kdlQuoteAlways handles newlines, tabs, carriage returns, backslashes, and double quotes.
    • Added TestNewlineInCommandHelp to ensure newlines in Long help text are correctly escaped in KDL output.
    • Added TestSpecialCharsInFlagHelp to confirm newlines in flag help text are properly escaped.
  • integrations/cobra/kdl.go
    • Modified kdlQuoteAlways to replace \n with \n, \t with \t, and \r with \r before quoting.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 12, 2026

Greptile Summary

This PR fixes a bug in kdlQuoteAlways where literal newline (\n), tab (\t), and carriage return (\r) characters in Cobra help text were being embedded raw into KDL output, producing invalid KDL that failed to parse. The fix adds three strings.ReplaceAll calls to escape these characters as their two-character escape sequences before wrapping the string in double quotes.

  • Root cause addressed: kdlQuoteAlways in integrations/cobra/kdl.go only escaped \ and " previously; control characters \n, \t, and \r were passed through unescaped.
  • Correct escape ordering: Backslashes are still escaped first (\\\), so the newly added escape sequences cannot be inadvertently double-escaped.
  • Full code-path coverage: needsQuoting (line 323) already included \t, \n, and \r in its switch case, meaning kdlQuote already routed strings with these characters through kdlQuoteAlways — the fix makes that path produce correct output.
  • Test coverage: Three new test functions cover the function directly (table-driven), long_about escaping end-to-end, and flag help= escaping end-to-end.

Confidence Score: 5/5

  • This PR is safe to merge — it is a targeted, correct bug fix with no regressions introduced.
  • The change is minimal and focused: three ReplaceAll lines added in the correct order after the existing backslash escape. The needsQuoting function already flagged these characters for quoting, so all call-sites are covered. The three new test functions verify the fix both at the unit level and through end-to-end Generate calls, confirming expected KDL output.
  • No files require special attention.

Important Files Changed

Filename Overview
integrations/cobra/kdl.go Adds \n, \t, and \r escape sequences to kdlQuoteAlways; ordering is correct (backslash escaped first), and needsQuoting already detected these chars so all code paths through kdlQuote are covered.
integrations/cobra/cobra_usage_test.go Three new test functions added: a table-driven unit test for kdlQuoteAlways, an integration test for long_about newline escaping, and an integration test for flag help newline escaping — good coverage of the fix.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Input string] --> B{needsQuoting?}
    B -- No --> C[Return bare identifier]
    B -- Yes --> D[kdlQuoteAlways]
    A2[Input string] --> D
    D --> E["Escape \\ → \\\\"]
    E --> F["Escape \" → \\\""]
    F --> G["Escape LF → \\n (NEW)"]
    G --> H["Escape TAB → \\t (NEW)"]
    H --> I["Escape CR → \\r (NEW)"]
    I --> J["Wrap in double quotes"]
    J --> K[Valid KDL string literal]

    style G fill:#c8f7c5
    style H fill:#c8f7c5
    style I fill:#c8f7c5
Loading

Last reviewed commit: 91be5a5

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.94%. Comparing base (7562574) to head (91be5a5).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #539   +/-   ##
=======================================
  Coverage   77.94%   77.94%           
=======================================
  Files          48       48           
  Lines        6682     6682           
  Branches     6682     6682           
=======================================
  Hits         5208     5208           
  Misses       1114     1114           
  Partials      360      360           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes KDL output generation for Cobra-derived specs by ensuring control characters in quoted strings are properly escaped, preventing invalid KDL when help text contains newlines/tabs/carriage returns.

Changes:

  • Escape \n, \t, and \r in kdlQuoteAlways so generated KDL remains syntactically valid.
  • Add unit tests covering escaping behavior and end-to-end generation cases for multiline help text.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
integrations/cobra/kdl.go Escapes newline/tab/carriage-return characters in kdlQuoteAlways to produce valid KDL strings.
integrations/cobra/cobra_usage_test.go Adds tests validating escape behavior and verifying generated KDL contains escaped sequences instead of literal control characters.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses an issue where special characters in Cobra help text were not properly escaped for KDL output, leading to parsing errors. The fix correctly escapes newlines, tabs, and carriage returns. The added tests are comprehensive, covering unit tests for the escaping logic and integration tests for command and flag help text. I have one suggestion to improve the performance of the string replacement logic.

Comment thread integrations/cobra/kdl.go
Comment on lines 304 to +308
s = strings.ReplaceAll(s, `\`, `\\`)
s = strings.ReplaceAll(s, `"`, `\"`)
s = strings.ReplaceAll(s, "\n", `\n`)
s = strings.ReplaceAll(s, "\t", `\t`)
s = strings.ReplaceAll(s, "\r", `\r`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Chaining multiple strings.ReplaceAll calls can be inefficient as it requires multiple passes over the string, allocating a new string for each replacement. A more performant approach is to use strings.NewReplacer, which performs all replacements in a single pass. For optimal performance, the Replacer instance should be created once at the package level and reused across function calls.

	s = strings.NewReplacer(
		"\\", "\\\\",
		"\"", "\\\"",
		"\n", "\\n",
		"\t", "\\t",
		"\r", "\\r"
	).Replace(s)

@jdx jdx merged commit dee7fb6 into jdx:main Mar 13, 2026
12 of 13 checks passed
jdx pushed a commit that referenced this pull request Mar 13, 2026
### 🚀 Features

- **(spec)** **breaking** add support for license, before/after help
metadata by [@jdx](https://github.com/jdx) in
[#542](#542)

### 🐛 Bug Fixes

- **(cobra)** escape newlines, tabs, and carriage returns in
kdlQuoteAlways by [@thecodesmith](https://github.com/thecodesmith) in
[#539](#539)
- bump major version for breaking changes in release automation by
[@jdx](https://github.com/jdx) in
[#544](#544)
- add custom_major_increment_regex for breaking change detection by
[@jdx](https://github.com/jdx) in
[#545](#545)
- handle all breaking change commit formats in major bump regex by
[@jdx](https://github.com/jdx) in
[27e1ab1](27e1ab1)
- normalize breaking change commit format in preprocessor by
[@jdx](https://github.com/jdx) in
[aa72b92](aa72b92)

### 📚 Documentation

- add argparse-usage integration by [@jdx](https://github.com/jdx) in
[#531](#531)
- mark KDL code blocks as KDL and use correct inline-comment `//` by
[@muzimuzhi](https://github.com/muzimuzhi) in
[#536](#536)
- fix include syntax to match implementation by
[@jdx](https://github.com/jdx) in
[#540](#540)
- consolidate integration list to single source by
[@jdx](https://github.com/jdx) in
[#541](#541)
- fix link to integrations by [@muzimuzhi](https://github.com/muzimuzhi)
in [#543](#543)

### 🛡️ Security

- **(deps)** update dependency eslint to v10 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#526](#526)

### 🔍 Other Changes

- Added an integration with ruby's OptionParser by
[@packrat386](https://github.com/packrat386) in
[#533](#533)

### 📦️ Dependency Updates

- update actions/setup-node digest to 53b8394 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#525](#525)
- update jdx/mise-action action to v3 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#528](#528)
- update rust crate roff to v1 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#529](#529)

### New Contributors

- @thecodesmith made their first contribution in
[#539](#539)
- @packrat386 made their first contribution in
[#533](#533)
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Apr 2, 2026
⚠️ **CAUTION: this is a major update, indicating a breaking change!** ⚠️

This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [usage](https://github.com/jdx/usage) | major | `2.18.2` → `3.2.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/usage (usage)</summary>

### [`v3.2.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#320---2026-03-23)

[Compare Source](jdx/usage@v3.1.0...v3.2.0)

##### 🚀 Features

- Support env-backed choices with `choices env=...` by [@&#8203;mustafa0x](https://github.com/mustafa0x) in [#&#8203;548](jdx/usage#548)

##### 🐛 Bug Fixes

- **(zsh)** escape parentheses and brackets in completion descriptions by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;559](jdx/usage#559)

##### New Contributors

- [@&#8203;mustafa0x](https://github.com/mustafa0x) made their first contribution in [#&#8203;548](jdx/usage#548)

### [`v3.1.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#310---2026-03-22)

[Compare Source](jdx/usage@v3.0.0...v3.1.0)

##### 🚀 Features

- **(cli)** render all doc-related fields in --help output by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;554](jdx/usage#554)
- **(cli)** support reading spec from stdin via --file - by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;555](jdx/usage#555)

##### 🐛 Bug Fixes

- **(zsh)** remove trailing space from completions and add directory slash by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;556](jdx/usage#556)
- use field assignment for non-exhaustive Spec in benchmarks by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;552](jdx/usage#552)

##### 📦️ Dependency Updates

- update apple-actions/import-codesign-certs digest to [`fe74d46`](jdx/usage@fe74d46) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;550](jdx/usage#550)
- update codecov/codecov-action digest to [`1af5884`](jdx/usage@1af5884) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;551](jdx/usage#551)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;547](jdx/usage#547)

### [`v3.0.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#300---2026-03-13)

[Compare Source](jdx/usage@v2.18.2...v3.0.0)

##### 🚀 Features

- **(spec)** **breaking** add support for license, before/after help metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;542](jdx/usage#542)

##### 🐛 Bug Fixes

- **(cobra)** escape newlines, tabs, and carriage returns in kdlQuoteAlways by [@&#8203;thecodesmith](https://github.com/thecodesmith) in [#&#8203;539](jdx/usage#539)
- bump major version for breaking changes in release automation by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;544](jdx/usage#544)
- add custom\_major\_increment\_regex for breaking change detection by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;545](jdx/usage#545)
- handle all breaking change commit formats in major bump regex by [@&#8203;jdx](https://github.com/jdx) in [27e1ab1](jdx/usage@27e1ab1)
- normalize breaking change commit format in preprocessor by [@&#8203;jdx](https://github.com/jdx) in [aa72b92](jdx/usage@aa72b92)

##### 📚 Documentation

- add argparse-usage integration by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;531](jdx/usage#531)
- mark KDL code blocks as KDL and use correct inline-comment `//` by [@&#8203;muzimuzhi](https://github.com/muzimuzhi) in [#&#8203;536](jdx/usage#536)
- fix include syntax to match implementation by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;540](jdx/usage#540)
- consolidate integration list to single source by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;541](jdx/usage#541)
- fix link to integrations by [@&#8203;muzimuzhi](https://github.com/muzimuzhi) in [#&#8203;543](jdx/usage#543)

##### 🛡️ Security

- **(deps)** update dependency eslint to v10 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;526](jdx/usage#526)

##### 🔍 Other Changes

- Added an integration with ruby's OptionParser by [@&#8203;packrat386](https://github.com/packrat386) in [#&#8203;533](jdx/usage#533)

##### 📦️ Dependency Updates

- update actions/setup-node digest to [`53b8394`](jdx/usage@53b8394) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;525](jdx/usage#525)
- update jdx/mise-action action to v3 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;528](jdx/usage#528)
- update rust crate roff to v1 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;529](jdx/usage#529)

##### New Contributors

- [@&#8203;thecodesmith](https://github.com/thecodesmith) made their first contribution in [#&#8203;539](jdx/usage#539)
- [@&#8203;packrat386](https://github.com/packrat386) made their first contribution in [#&#8203;533](jdx/usage#533)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDIuMTAiLCJ1cGRhdGVkSW5WZXIiOiI0My4xMDIuMTAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbIlJlbm92YXRlIEJvdCIsImF1dG9tYXRpb246Ym90LWF1dGhvcmVkIiwiZGVwZW5kZW5jeS10eXBlOjptYWpvciJdfQ==-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants