{
    "componentChunkName": "component---src-templates-blog-post-tsx",
    "path": "/blog/2023-10-16-backstage-linting/",
    "result": {"data":{"blogPost":{"title":"Backstage: How to fix linting errors without slowing down development","slug":"/blog/2023-10-16-backstage-linting/","authorNodes":[{"name":"Min Kim","slug":"/people/min-kim/"}],"markdown":{"html":"<blockquote>\n<p><strong>TL;DR</strong> This blog post outlines the steps in creating a patch for the lint command in Backstage, which organizes errors by package, rules, and overall counts. The author explains how to use the generated lint error data to monitor and manage lint issues efficiently, and emphasizes the importance of tracking progress in reducing lint errors.</p>\n</blockquote>\n<p>I recently worked with a team that had the lint command in their Backstage project misconfigured. The result? They ended up amassing an overwhelming number of lint errors - literally hundreds upon hundreds. In fact, the report output by the lint command was so long it would max out the terminal.</p>\n<p>Normally, you would run the lint command in CI for every commit of your pull requests. This practice ensures that any new lint errors introduced by the changes would prevent the pull request from being merged. However, we could not set up this configuration right away; doing so would have brought development to a screeching halt.</p>\n<p>With so many lint errors, it was difficult to pinpoint a starting point. To gain a clearer overview of the types and quantity of lint errors we were dealing with, I made modifications to the Backstage lint command. The modified command produced reports within a structured file system and generated an errors' summary.</p>\n<h1 id=\"writing-the-patch\" style=\"position:relative;\"><a href=\"#writing-the-patch\" aria-label=\"writing the patch permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Writing the Patch</h1>\n<p>The lint command of the <code class=\"language-text\">backstage-cli</code> package can be found <a href=\"https://github.com/backstage/backstage/blob/5578c3de6354bf9ef65aaac50fe73b890b0ad0e5/packages/cli/src/commands/repo/lint.ts\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">here</a>. We're going to write a patch for it to produce the summary file so let's start by installing <a href=\"https://www.npmjs.com/package/patch-package\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"language-text\">patch-package</code></a>:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">yarn add patch-package -W</code></pre></div>\n<p>And modify the root <code class=\"language-text\">package.json</code> of your Backstage project:</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">\"scripts\": {\n<span class=\"token inserted-sign inserted\"><span class=\"token prefix inserted\">+</span>  \"postinstall\": \"patch-package\"\n</span>}</code></pre></div>\n<p>Then navigate to the correct lint command file in <code class=\"language-text\">./node_modules/@backstage/cli/dist/cjs/lint</code>. There will be three Javascript files for linting - one for the base lint command, another for the repo lint command, and a third for the versions lint command. The file we're going to modify is the one designated for the repo lint command.</p>\n<p>Here is a high level view of the patch we're going to be writing:</p>\n<div class=\"gatsby-highlight\" data-language=\"md\"><pre class=\"language-md\"><code class=\"language-md\">create <span class=\"token code-snippet code keyword\">`errors`</span> directory\ncreate <span class=\"token code-snippet code keyword\">`summary`</span> file\n\nfor <span class=\"token code-snippet code keyword\">`errors`</span> of each <span class=\"token code-snippet code keyword\">`package`</span>\n  <span class=\"token list punctuation\">-</span> create directory for this specific package\n  <span class=\"token list punctuation\">-</span> output lint report of this package to a text file in its corresponding directory\n  <span class=\"token list punctuation\">-</span> output the total lint error count of this package and list the rules that are being violated\n\ncalculate grand total count of lint errors for the entire project\ncombine the total count of lint errors by rule for the entire project</code></pre></div>\n<p>And here's the actual patch. Typically we would use diff syntax to indicate which lines to be added, but in order to make the code easier to read, we will display the code snippet with Javascript syntax highlighting.</p>\n<blockquote>\n<p>The code snippet below is quite long so you can check out a working demo of a Backstage app with the lint patch <a href=\"https://github.com/minkimcello/backstage-lint-demo\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">here</a>. The actual patch can be found <a href=\"https://github.com/minkimcello/backstage-lint-demo/blob/main/patches/%40backstage%2Bcli%2B0.22.12.patch\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">here</a>, and you could also follow along its <a href=\"https://github.com/minkimcello/backstage-lint-demo/commits/main\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">commit history</a> for a more step-by-step approach in creating the patch.</p>\n</blockquote>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">var</span> fs <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fs'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// ...</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">command</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">opts</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n  <span class=\"token keyword\">let</span> failed <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> errors_dir <span class=\"token operator\">=</span> path<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span><span class=\"token function\">cwd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'errors'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>fs<span class=\"token punctuation\">.</span><span class=\"token function\">existsSync</span><span class=\"token punctuation\">(</span>errors_dir<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    fs<span class=\"token punctuation\">.</span><span class=\"token function\">rmSync</span><span class=\"token punctuation\">(</span>errors_dir<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">recursive</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">force</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">let</span> errors_summary <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">total_count</span><span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">per_package</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">let</span> violations_combined <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> relativeDir<span class=\"token punctuation\">,</span> resultText <span class=\"token punctuation\">}</span> <span class=\"token keyword\">of</span> resultsList<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> package_dir <span class=\"token operator\">=</span> path<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span>errors_dir<span class=\"token punctuation\">,</span> relativeDir<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    fs<span class=\"token punctuation\">.</span><span class=\"token function\">mkdirSync</span><span class=\"token punctuation\">(</span>package_dir<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">recursive</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> stripAnsi <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RegExp</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=>&lt;]</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> resultTextFormatted <span class=\"token operator\">=</span> resultText<span class=\"token punctuation\">.</span><span class=\"token function\">replace</span><span class=\"token punctuation\">(</span>stripAnsi<span class=\"token punctuation\">,</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"\\n\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    fs<span class=\"token punctuation\">.</span><span class=\"token function\">writeFileSync</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>package_dir<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/results.txt</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>relativeDir<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\\n</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>resultTextFormatted<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">'\\n'</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\\n</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> violations_count <span class=\"token operator\">=</span> <span class=\"token function\">parseInt</span><span class=\"token punctuation\">(</span>\n      resultTextFormatted\n        <span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">line</span> <span class=\"token operator\">=></span> line<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">✘ [0-9]+ problem.*</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\s*✘\\s*</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\"> problem.*</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> violations_index <span class=\"token operator\">=</span> resultTextFormatted<span class=\"token punctuation\">.</span><span class=\"token function\">indexOf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Errors:\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">>=</span> <span class=\"token number\">0</span>\n      <span class=\"token operator\">?</span> resultTextFormatted<span class=\"token punctuation\">.</span><span class=\"token function\">indexOf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Errors:\"</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">:</span> resultTextFormatted<span class=\"token punctuation\">.</span><span class=\"token function\">indexOf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Warnings:\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> package_violations <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    resultTextFormatted<span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span>violations_index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">line</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>line<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">http</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> error_details <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">count</span><span class=\"token operator\">:</span> <span class=\"token function\">parseInt</span><span class=\"token punctuation\">(</span>line<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\d+</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">rule</span><span class=\"token operator\">:</span> line<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">http.*</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n        <span class=\"token punctuation\">}</span>\n        package_violations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>error_details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        violations_combined<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>error_details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    errors_summary<span class=\"token punctuation\">.</span>per_package<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">package</span><span class=\"token operator\">:</span> relativeDir<span class=\"token punctuation\">,</span>\n      violations_count<span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">package_violations</span><span class=\"token operator\">:</span> package_violations<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a<span class=\"token punctuation\">.</span>rule<span class=\"token punctuation\">.</span><span class=\"token function\">localeCompare</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">.</span>rule<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    errors_summary<span class=\"token punctuation\">.</span>total_count <span class=\"token operator\">=</span> errors_summary<span class=\"token punctuation\">.</span>total_count <span class=\"token operator\">+</span> violations_count<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>failed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> combined_violations_sorted <span class=\"token operator\">=</span> violations_combined\n    <span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> already_exists <span class=\"token operator\">=</span> acc<span class=\"token punctuation\">.</span><span class=\"token function\">findIndex</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc_item</span> <span class=\"token operator\">=></span> acc_item<span class=\"token punctuation\">.</span>rule <span class=\"token operator\">===</span> item<span class=\"token punctuation\">.</span>rule<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>already_exists <span class=\"token operator\">></span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        acc<span class=\"token punctuation\">[</span>already_exists<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>count <span class=\"token operator\">=</span> acc<span class=\"token punctuation\">[</span>already_exists<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>count <span class=\"token operator\">+</span> item<span class=\"token punctuation\">.</span>count\n        <span class=\"token keyword\">return</span> acc<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span><span class=\"token operator\">...</span>acc<span class=\"token punctuation\">,</span> item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    fs<span class=\"token punctuation\">.</span><span class=\"token function\">writeFileSync</span><span class=\"token punctuation\">(</span>errors_dir <span class=\"token operator\">+</span> <span class=\"token string\">\"/summary.json\"</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">count</span><span class=\"token operator\">:</span> errors_summary<span class=\"token punctuation\">.</span>total_count<span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">errors_combined</span><span class=\"token operator\">:</span> combined_violations_sorted<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a<span class=\"token punctuation\">.</span>rule<span class=\"token punctuation\">.</span><span class=\"token function\">localeCompare</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">.</span>rule<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">per_package</span><span class=\"token operator\">:</span> errors_summary<span class=\"token punctuation\">.</span>per_package<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a<span class=\"token punctuation\">.</span>package<span class=\"token punctuation\">.</span><span class=\"token function\">localeCompare</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">.</span>rule<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<blockquote>\n<p>Note:</p>\n<ol>\n<li>\n<p>ESLint rules are categorized by <code class=\"language-text\">warning</code> and <code class=\"language-text\">error</code>, but we have found that the categorization is highly opinionated and do not necessarily align to its actual severity so for that reason we merged warnings with errors.</p>\n</li>\n<li>\n<p>ESLint offers a wide variety of options for <a href=\"https://eslint.org/docs/latest/use/formatters/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">formatters</a>. I have reviewed each formatter but ultimately the <code class=\"language-text\">stylish</code> (default) formatter met all our needs. Although the sylish output required a bit of formatting, it is the only formatter that provides a relative path of the package location, count of errors per package, and provides source URLs to the violated rules.</p>\n</li>\n</ol>\n</blockquote>\n<h1 id=\"viewing-and-updating-the-summary\" style=\"position:relative;\"><a href=\"#viewing-and-updating-the-summary\" aria-label=\"viewing and updating the summary permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Viewing and Updating the Summary</h1>\n<p>With the patch code shown above, our new lint command will output a summary file that will look something like this:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  count<span class=\"token operator\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n  errors_combined<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n      rule<span class=\"token operator\">:</span> <span class=\"token string\">\"https://eslint.org/docs/latest/rules/foo\"</span><span class=\"token punctuation\">,</span>\n      count<span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span>\n      rule<span class=\"token operator\">:</span> <span class=\"token string\">\"https://eslint.org/docs/latest/rules/bar\"</span><span class=\"token punctuation\">,</span>\n      count<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  per_package<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n      package<span class=\"token operator\">:</span> <span class=\"token string\">\"/packages/a\"</span><span class=\"token punctuation\">,</span>\n      violations_count<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n      package_violations<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n          rule<span class=\"token operator\">:</span> <span class=\"token string\">\"https://eslint.org/docs/latest/rules/foo\"</span><span class=\"token punctuation\">,</span>\n          count<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    ...\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>From the generated summary file, we can extract the following information:</p>\n<ul>\n<li>Total count of lint errors of the entire project</li>\n<li>Total count of lint errors per rule of the entire project</li>\n<li>Total count of lint errors of each package</li>\n<li>Total count of lint errors per rule of each package</li>\n</ul>\n<p>In addition to the summary file, the modified lint command will output each package results to an organized file structure:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">errors/\n  |-- summary.json\n  |-- packages/\n    |-- foo/\n      |-- results.txt\n    |-- bar/\n      |-- results.txt</code></pre></div>\n<p>Ideally from here you would divide and conquer to resolve all of your project's rule violations. However, if your team lacks the capacity to address these errors right away, the best you could do is try to prevent the problem from growing.</p>\n<p>The best way to keep track of your lint errors would be to keep the <code class=\"language-text\">errors</code> directory up to date in all of your pull requests. You could tell your developers to run the modified lint command, but having to rely on people remembering to do things is rarely a good idea, so you're left with two options: you could have your CI run the patched lint command and push the updated summaries to your pull requests - which would require your developers to pull their own branch after each commit they push - or you could use <a href=\"https://typicode.github.io/husky\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"language-text\">husky</code></a> to force your local dev environments to run lint for you.</p>\n<p>Follow their <a href=\"https://typicode.github.io/husky/getting-started.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Getting Started</a> guide for installing Husky to your Backstage project and configure a pre-push hook to run lint:</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\"><span class=\"token shebang important\">#!/usr/bin/env sh</span>\n<span class=\"token builtin class-name\">.</span> <span class=\"token string\">\"<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">dirname</span> -- <span class=\"token string\">\"<span class=\"token variable\">$0</span>\"</span><span class=\"token variable\">)</span></span>/_/husky.sh\"</span>\n\n<span class=\"token function\">yarn</span> lint\n<span class=\"token keyword\">if</span> <span class=\"token function\">git</span> status <span class=\"token operator\">|</span> <span class=\"token function\">grep</span> <span class=\"token string\">\"errors/\"</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">then</span>\n  <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">\"You forgot to run 'yarn lint'. The lint command was ran for you just now, commit the generated errors directory and git push again\"</span>\n  <span class=\"token builtin class-name\">exit</span> <span class=\"token number\">1</span>\n<span class=\"token keyword\">fi</span></code></pre></div>\n<blockquote>\n<p>Note:</p>\n<ol>\n<li>\n<p>Instead of having husky run before a git push, you could configure it to run every time a developer creates a new commit. Depending on the size of your Backstage project, the lint command could take quite a while. In this guide we opted for the pre-push hook to provide a more pleasant developer experience.</p>\n</li>\n<li>\n<p>Having the pre-push hook create the commit and subsequently push the updated set of commits in a single step, without requiring developers to initiate an additional git push, would be a convenient approach. But, unfortunatey, the pre-push hook's mechanism involves calculating the specific commits to push at the moment the hook is activated. As a result, it's incapable of generating a fresh commit and incorporating that newly created commit within the confines of the hook itself.</p>\n</li>\n</ol>\n</blockquote>\n<h1 id=\"using-the-data\" style=\"position:relative;\"><a href=\"#using-the-data\" aria-label=\"using the data permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Using the Data</h1>\n<p>Now that we have a better overview of our linting errors (and a way to keep it reliably up to date), we need to decide on a method of using that data.</p>\n<p>The simplest approach to check if someone is introducing more lint errors would be to quickly check the git diff of their pull request for the total count in the summary file:</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">{\n<span class=\"token deleted-sign deleted\"><span class=\"token prefix deleted\">-</span>  count: 3,\n</span><span class=\"token inserted-sign inserted\"><span class=\"token prefix inserted\">+</span>  count: 5,\n</span><span class=\"token unchanged\"><span class=\"token prefix unchanged\"> </span> errors_combined: [...],\n<span class=\"token prefix unchanged\"> </span> per_package: [...]\n</span>}</code></pre></div>\n<p>However, should a developer address a minor rule violation while simultaneously introducing a more severe error, the overall count of lint errors would remain unchanged. To avoid this scenario, you'd need to compare the cumulative count for each specific rule:</p>\n<div class=\"gatsby-highlight\" data-language=\"diff\"><pre class=\"language-diff\"><code class=\"language-diff\">{\n<span class=\"token unchanged\"><span class=\"token prefix unchanged\"> </span> count: 3,\n<span class=\"token prefix unchanged\"> </span> errors_combined: [\n<span class=\"token prefix unchanged\"> </span>   {\n<span class=\"token prefix unchanged\"> </span>     rule: \"https://eslint.org/docs/latest/rules/foo\",\n</span><span class=\"token deleted-sign deleted\"><span class=\"token prefix deleted\">-</span>      count: 2,\n</span><span class=\"token inserted-sign inserted\"><span class=\"token prefix inserted\">+</span>      count: 1,\n</span><span class=\"token unchanged\"><span class=\"token prefix unchanged\"> </span>   },\n<span class=\"token prefix unchanged\"> </span>   {\n<span class=\"token prefix unchanged\"> </span>     rule: \"https://eslint.org/docs/latest/rules/bar\",\n</span><span class=\"token deleted-sign deleted\"><span class=\"token prefix deleted\">-</span>      count: 1,\n</span><span class=\"token inserted-sign inserted\"><span class=\"token prefix inserted\">+</span>      count: 2,\n</span><span class=\"token unchanged\"><span class=\"token prefix unchanged\"> </span>   }\n<span class=\"token prefix unchanged\"> </span> ],\n<span class=\"token prefix unchanged\"> </span> per_package: [...],\n</span>}</code></pre></div>\n<p>And if you want to ensure that a particular rule isn't being resolved in one package while a new one is being introduced in another, you would have to take a look at the differences under the <code class=\"language-text\">per_package</code> property and review the git diff of the lint reports of each package.</p>\n<p>Whether you choose to resolve your lint issues rule-by-rule or package-by-package, providing your developers with the capability to access the lint reports for each package in a file format offers significant convenience. This will enable them to search through the entire directy for particular rules and be able to view the details of the lint report without having to run the command.</p>\n<h1 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h1>\n<p>If your project has accumulated a long list of lint errors, it's crucial to strategize an approach for addressing them. Your team might be able to allocate resources to fix those linting errors right away or they may need to chip away at it incrementally. Regardless of the approach, it would be beneficial for your team to be able to track the progress of their efforts - it's also very satisfying to see the total count number within the summary file decrease in your pull requests.</p>\n<p>Upon successfully reducing your lint errors count to zero, you can promptly remove the patch and reconfigure your CI system to mandate linting as an obligatory check for all pull requests.</p>","frontmatter":{"date":"October 16, 2023","description":"In this guide Min will show you a unique approach to tackling a mountain of linting errors without disrupting delivery","tags":["backstage","dx"],"img":{"childImageSharp":{"fixed":{"src":"/static/363877537a4c176532fe50374dbfa685/31987/2023-10-16-backstage-linting.png"}}}}}}},"pageContext":{"id":"a8790376-f3a7-5124-88c6-bbef0a1f121e"}},
    "staticQueryHashes": ["1241260443"]}