<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Viktor Tiulpin </title>
    <description>The latest articles on Forem by Viktor Tiulpin  (@tiulpin).</description>
    <link>https://forem.com/tiulpin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F319291%2F1e2624b5-1095-470c-b697-82541501ba4a.jpg</url>
      <title>Forem: Viktor Tiulpin </title>
      <link>https://forem.com/tiulpin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tiulpin"/>
    <language>en</language>
    <item>
      <title>Develop, test, and deploy your extensions for all popular CIs from a single codebase</title>
      <dc:creator>Viktor Tiulpin </dc:creator>
      <pubDate>Sun, 18 Jun 2023 15:09:12 +0000</pubDate>
      <link>https://forem.com/tiulpin/develop-test-and-deploy-your-extensions-for-all-popular-cis-from-a-single-codebase-27j1</link>
      <guid>https://forem.com/tiulpin/develop-test-and-deploy-your-extensions-for-all-popular-cis-from-a-single-codebase-27j1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;💡 The feature image shows a typical CI/CD pipeline in action partly drawn by OpenAI DALL-E, but in this article, we are going to develop something beneficial&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Start from the official templates

&lt;ul&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Azure Pipelines&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Create the monorepo&lt;/li&gt;

&lt;li&gt;Share code between actions and tasks&lt;/li&gt;

&lt;li&gt;Build and publish&lt;/li&gt;

&lt;li&gt;CircleCI?&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is a relatively short tutorial on how to develop, test, and deploy your CI extensions for GitHub Actions, Azure Pipelines, and CircleCI from a single monorepo and is based on the experience of creating the &lt;a href="https://github.com/JetBrains/qodana-action" rel="noopener noreferrer"&gt;Qodana CI extensions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start from the official templates
&lt;/h2&gt;

&lt;p&gt;Let's pick the technology stack for our CI extensions.&lt;/p&gt;

&lt;p&gt;OK, I will not pick. I'll just tell you why I used TypeScript and node.js for the extensions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros for using JS-based actions:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;More flexible than bash/Dockerfile-based approaches

&lt;ul&gt;
&lt;li&gt;Different libraries (like &lt;a href="https://github.com/actions/toolkit" rel="noopener noreferrer"&gt;actions/toolkit&lt;/a&gt; and &lt;a href="https://github.com/microsoft/azure-pipelines-task-lib" rel="noopener noreferrer"&gt;microsoft/azure-pipelines-task-lib&lt;/a&gt;) with more accessible and easy-to-use APIs are available out-of-box&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Writing tests is relatively simple&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cons
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let's write a TypeScript-based action!&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;

&lt;p&gt;I found the GitHub actions documentation easier to read than Azure, so I would recommend starting writing and testing your extensions on GitHub by using the official template &lt;a href="https://github.com/actions/typescript-action" rel="noopener noreferrer"&gt;actions/typescript-action&lt;/a&gt;. The mentioned template provides a good starting point; I won't repeat the steps here. Play with it, write some simple stuff, and then return here for the next steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Pipelines
&lt;/h3&gt;

&lt;p&gt;GitHub Actions are built on Azure infrastructure, so porting your GitHub action to Azure Pipelines should be relatively easy.&lt;/p&gt;

&lt;p&gt;So,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the "action" becomes the "task"&lt;/li&gt;
&lt;li&gt;it's packed a bit differently, distributed, and installed the other way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the definition of a task &lt;code&gt;task.json&lt;/code&gt; is the same as the action one &lt;code&gt;action.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, having the following &lt;code&gt;action.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;here'&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Provide&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;here'&lt;/span&gt;
&lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;organization&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;here'&lt;/span&gt;
&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;milliseconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# change this&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;here'&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;applicable'&lt;/span&gt;
&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node16'&lt;/span&gt;
  &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dist/index.js'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Easily" translates to the following Azure task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"822d6cb9-d4d1-431b-9513-e7db7d718a49"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourTaskNameHere"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"friendlyName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your name here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Provide a description here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"helpMarkDown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Provide a longer description here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your name or organization here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Major"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Minor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Patch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instanceNameFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourTaskNameHere"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"milliseconds"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"label name here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"defaultValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default value if applicable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"helpMarkDown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"input description here"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"execution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Node10"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From such a simple example, one can see why I suggested starting with GitHub Actions. But let's continue.&lt;/p&gt;

&lt;p&gt;To start developing your new shiny Azure Pipelines task, I suggest just copying the action directory and then implementing steps from &lt;a href="https://learn.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops" rel="noopener noreferrer"&gt;the official Azure documentation&lt;/a&gt; – it's pretty straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;vss-extension.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;task.json&lt;/code&gt; and place it into your &lt;code&gt;dist&lt;/code&gt; directory (actually better to name it after the task name)&lt;/li&gt;
&lt;li&gt;If you used any methods from &lt;code&gt;@actions/core&lt;/code&gt; or &lt;code&gt;@actions/github&lt;/code&gt; in your action, you need to replace them with the corresponding methods from &lt;code&gt;azure-pipelines-task-lib&lt;/code&gt; (e.g. &lt;code&gt;core.getInput&lt;/code&gt; -&amp;gt; &lt;code&gt;tl.getInput&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The API of &lt;code&gt;azure-pipelines-task-lib&lt;/code&gt; is similar to &lt;code&gt;@actions/core&lt;/code&gt; and other &lt;code&gt;@actions/*&lt;/code&gt; libraries. For example, we have a method for obtaining the input parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getInputs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Inputs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;milliseconds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the same for Azure Pipelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getInputs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Inputs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;milliseconds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more real cases, feel free to explore our Qodana GitHub Actions codebase &lt;a href="https://github.com/JetBrains/qodana-action/blob/main/scan/src/utils.ts" rel="noopener noreferrer"&gt;utils&lt;/a&gt; and Azure Pipelines task &lt;a href="https://github.com/JetBrains/qodana-action/blob/main/vsts/src/utils.ts" rel="noopener noreferrer"&gt;utils&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the monorepo
&lt;/h2&gt;

&lt;p&gt;We are going to use &lt;a href="https://docs.npmjs.com/cli/v9/using-npm/workspaces" rel="noopener noreferrer"&gt;npm workspaces&lt;/a&gt; to manage the monorepo.&lt;br&gt;
Place your action and task code into subdirectories (e.g. &lt;code&gt;github&lt;/code&gt;) of your newly created monorepo. And then create a &lt;code&gt;package.json&lt;/code&gt; file in the root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@org/ci"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Common code for CI extensions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Apache-2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"azure"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint-plugin-github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint-plugin-jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ts-node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the monorepo structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
├── action.yaml
├── github/
├── azure/
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After implementing the workspace setup, you can run tasks and actions from the root directory. For example, to run the &lt;code&gt;build&lt;/code&gt; task from the &lt;code&gt;github&lt;/code&gt; directory, you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nt"&gt;-w&lt;/span&gt; github build 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Share code between actions and tasks
&lt;/h2&gt;

&lt;p&gt;The most valuable part from using the monorepo approach starts here: you can share the code between your actions and tasks.&lt;/p&gt;

&lt;p&gt;We are going to do the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;common&lt;/code&gt; directory in the root of the monorepo, a subproject for shared code&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;tsconfig.json&lt;/code&gt; compiler configurations from all sub-dirs for proper project builds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At first, let's create the base &lt;code&gt;tsconfig&lt;/code&gt; – &lt;code&gt;tsconfig.base.json&lt;/code&gt; with the base settings that are going to be used in all subprojects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forceConsistentCasingInFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resolveJsonModule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"composite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*.test.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*/lib/**"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a simple &lt;code&gt;tsconfig.json&lt;/code&gt; in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"references"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"common"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"azure"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;common/tsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../tsconfig.base.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"include your files here or use typical include/exclude patterns"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, update the &lt;code&gt;tsconfig.json&lt;/code&gt; files in the subprojects (they are basically the same, e.g. &lt;code&gt;github/tsconfig.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../tsconfig.base.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"references"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../common"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can use the shared code from the &lt;code&gt;common&lt;/code&gt; directory in your actions and tasks. For example, we have a &lt;code&gt;qodana.ts&lt;/code&gt; file in the &lt;code&gt;common&lt;/code&gt; directory that contains function &lt;a href="https://github.com/JetBrains/qodana-action/blob/main/common/qodana.ts#LL54C21-L54C21" rel="noopener noreferrer"&gt;&lt;code&gt;getQodanaUrl&lt;/code&gt;&lt;/a&gt; that returns the URL to the Qodana CLI tool. And we &lt;a href="https://github.com/search?q=repo%3AJetBrains/qodana-action%20getQodanaUrl&amp;amp;type=code" rel="noopener noreferrer"&gt;use it&lt;/a&gt; in both actions and tasks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13538286%2F246672580-f8345026-7f7d-47ff-ad66-7da5355475c6.png" class="article-body-image-wrapper"&gt;&lt;img alt="CleanShot 2023-06-18 at 16 54 11@2x" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13538286%2F246672580-f8345026-7f7d-47ff-ad66-7da5355475c6.png" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and publish
&lt;/h2&gt;

&lt;p&gt;You already have GitHub workflows from the template configured to publish your actions to your repository releases.&lt;br&gt;
For automated releases, we use GH CLI, and we have a simple script that publishes a changelog to the repository releases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nv"&gt;previous_tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;for &lt;/span&gt;current_tag &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git tag &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-creatordate&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;do

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$previous_tag&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"## Changelog&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    git log &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;current_tag&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;...&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;previous_tag&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s1"&gt;'* %h %s'&lt;/span&gt; &lt;span class="nt"&gt;--reverse&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; Merge
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;break
&lt;/span&gt;&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nv"&gt;previous_tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;current_tag&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the GitHub workflow that runs it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Release'&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;./changelog.sh &amp;gt; changelog.md&lt;/span&gt;
          &lt;span class="s"&gt;gh release create ${GITHUB_REF##*/} -F changelog.md&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Azure Pipelines task releases, you can use the official approach from Azure. Still, also you can do the same on GitHub actions infrastructure as their publisher tool can be installed anywhere. So, in our case, it's solved by a simple GitHub workflow job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;azure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set Node.js 12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v3.6.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci &amp;amp;&amp;amp; cd vsts/QodanaScan &amp;amp;&amp;amp; npm ci &amp;amp;&amp;amp; npm i -g tfx-cli&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package and publish&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;cd vsts &amp;amp;&amp;amp; npm run azure&lt;/span&gt;
          &lt;span class="s"&gt;mv JetBrains.qodana-*.vsix qodana.vsix&lt;/span&gt;
          &lt;span class="s"&gt;tfx extension publish --publisher JetBrains --vsix qodana.vsix -t $AZURE_TOKEN&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AZURE_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this set up, each release happens automatically on each tag push.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag &lt;span class="nt"&gt;-a&lt;/span&gt; v1.0.0 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"v1.0.0"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push origin v1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13538286%2F246672668-93db2c5d-5617-400e-be2c-efaeb8652427.png" class="article-body-image-wrapper"&gt;&lt;img alt="CleanShot 2023-06-18 at 16 55 34@2x" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13538286%2F246672668-93db2c5d-5617-400e-be2c-efaeb8652427.png" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CircleCI?
&lt;/h2&gt;

&lt;p&gt;Ah, yes, this article mentioned the CircleCI orb also... CircleCI setup is straightforward but does not support TypeScript extensions, so you have to pack your code into a Docker image or a binary and run it there. The only reason it's included in this post is that we build our orb with the monorepo approach, which works well.&lt;/p&gt;

&lt;p&gt;Just implement &lt;a href="https://circleci.com/docs/2.0/orb-author/#quick-start" rel="noopener noreferrer"&gt;the official orb template&lt;/a&gt; and place it in your monorepo, so the structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
├── action.yaml
├── github/
├── azure/
├── src/            # orb source code here
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And don't forget to commit &lt;code&gt;.circleci/&lt;/code&gt; directory to your repository to make CircleCI lint, test, and publish your orb.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13538286%2F246672378-e7107578-9b52-46b3-8c42-3b381f007c93.png" class="article-body-image-wrapper"&gt;&lt;img alt="CleanShot 2023-06-18 at 16 49 57@2x" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13538286%2F246672378-e7107578-9b52-46b3-8c42-3b381f007c93.png" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If this post gets ten reactions here, I'll add a second part about publishing and testing Azure Pipelines and CircleCI orbs&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>azure</category>
      <category>github</category>
      <category>circleci</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Announcing the Preview for Qodana Cloud, a One-Stop-Shop for All Your Code Quality Insights!</title>
      <dc:creator>Viktor Tiulpin </dc:creator>
      <pubDate>Thu, 08 Dec 2022 13:20:36 +0000</pubDate>
      <link>https://forem.com/qodana/announcing-the-preview-for-qodana-cloud-a-one-stop-shop-for-all-your-code-quality-insights-8a3</link>
      <guid>https://forem.com/qodana/announcing-the-preview-for-qodana-cloud-a-one-stop-shop-for-all-your-code-quality-insights-8a3</guid>
      <description>&lt;p&gt;A public preview is now open for Qodana Cloud – a centralized cloud-based solution that collects and displays data from different Qodana linters under one roof. You can use Qodana Cloud to manage your code quality checks in a variety of contexts, ranging from one-person projects to large development teams.&lt;/p&gt;

&lt;p&gt;Qodana Cloud is still in development, and to iron out some wrinkles we are calling for the support of the community. If you want to become an early adopter of our new features, read on to learn how to get started with Qodana Cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63un3dwud5p0szs84zh8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63un3dwud5p0szs84zh8.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qodana.cloud/" rel="noopener noreferrer"&gt;TRY QODANA CLOUD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Qodana Cloud can complement your projects
&lt;/h2&gt;

&lt;p&gt;Do you want to run static analysis in multiple projects or repositories? Is your codebase distributed across several servers and virtual private networks? Do your teams work independently, and are they not always on the same page when it comes to code quality? In any of these scenarios, if you wanted to ensure that your code is clean and secure, you previously would need to switch between different linters or Qodana instances to see the results for different projects. &lt;/p&gt;

&lt;p&gt;The need to switch between linters can make the code review process convoluted and inefficient, and we’ve developed Qodana Cloud to address precisely this problem. Qodana Cloud collects all the data from different Qodana linters in a single place and allows you to drill down into particular issues with interactive dashboards. &lt;/p&gt;

&lt;p&gt;Here is how you can benefit from Qodana Cloud:&lt;/p&gt;

&lt;h3&gt;
  
  
  Get deeper insights into your projects’ trends
&lt;/h3&gt;

&lt;p&gt;With the ability to aggregate reports from different sources in one view, you can discover trends and patterns in your code across all projects and get a better understanding of how your project or the whole team is performing. This way, developers will no longer code in silos, instead seeing the same list of issues. This also makes it easier for managers to track progress for the whole organization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezbwaahqgfwy4jvhtqek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezbwaahqgfwy4jvhtqek.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create separate organizations, teams, and projects in Qodana Cloud and assign a single team to several projects for convenient navigation. In addition, you achieve more transparency thanks to widgets updating in real time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mheypfc266t2c7bp4a1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mheypfc266t2c7bp4a1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In each project, you can also see a history of previous results and compare the results of quality checks between commits. You can examine the absolute number of problems detected or compare the number of problems against a baseline – a snapshot of the codebase problems taken during a specific Qodana run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open problems detected by Qodana in your favorite IDE
&lt;/h3&gt;

&lt;p&gt;If you are already a Qodana user, you might know that you can open issues spotted by Qodana right in your IDE. And now this feature works for Qodana Cloud, too! This means you can fix server-side errors in the editor the same way you work with other suggestions given by the IDE. &lt;/p&gt;

&lt;p&gt;Here’s how it works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnyudert69axmvh7kj0m4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnyudert69axmvh7kj0m4.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What’s especially important about Qodana being bundled with JetBrains IDEs is that you run resource-intensive checks outside of your development environment without hurting your IDE’s performance. In the example above, Qodana spotted a chance that a variable could be null and cause a runtime exception. This is a severe issue, but users tend to switch off such inspections for the sake of saving resources.&lt;/p&gt;

&lt;p&gt;In fact, that’s one of the reasons why we at JetBrains created Qodana, to ensure that you no longer have to choose between code quality and IDE performance! &lt;/p&gt;

&lt;h3&gt;
  
  
  Plan your work better
&lt;/h3&gt;

&lt;p&gt;Split big projects into small steps! Switching to a newer version of a language or framework, or getting rid of a certain utility or pattern, can be a cumbersome task – especially if you are working on a big project involving many developers and QA engineers. &lt;/p&gt;

&lt;p&gt;In Qodana Cloud, you can build a report to evaluate all pieces of code requiring modification and select issues to add to the baseline – otherwise known as the technical debt section. This way, the whole team can see the same list of issues and monitor progress with an interactive Qodana dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqazjb14rhf1pu90wcvu3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqazjb14rhf1pu90wcvu3.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose the dark or the light side
&lt;/h3&gt;

&lt;p&gt;In the design world, dark mode is the new black. Who are we to ignore that? To ensure you have a good experience with Qodana Cloud, we’ve provided the option to manually choose the dark or the light theme or have the UI sync automatically with your system preferences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc998sqg6qqmc2wzsa6u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc998sqg6qqmc2wzsa6u.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s coming next?
&lt;/h2&gt;

&lt;p&gt;In future releases, we will add role-based access control so that you can grant different permissions based on a user’s responsibilities. For example, perhaps your legal team only needs the ability to view reports about licenses used in a product, or your security team needs to see a list of vulnerabilities in the codebase. You will be able to create custom roles for them with permissions that are specific to their tasks. We are also working on implementing more security controls and allowing quick-fixes for certain types of issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphj4pmulhw55cappvo81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphj4pmulhw55cappvo81.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A sneak peek of what’s coming next in Qodana Cloud.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/Qodana" rel="noopener noreferrer"&gt;Follow us on Twitter&lt;/a&gt; or subscribe to the blog for updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get started with Qodana Cloud
&lt;/h2&gt;

&lt;p&gt;To get started, go to &lt;a href="https://qodana.cloud/" rel="noopener noreferrer"&gt;qodana.cloud&lt;/a&gt; and log in with your JetBrains Account. Alternatively, as a non-registered user, you can explore demo projects that were already analyzed by Qodana Cloud to see it in action.&lt;/p&gt;

&lt;p&gt;To pull your inspection reports from other Qodana instances into the cloud, Qodana Cloud will generate a token for you to set into your project in your CI tool. For detailed instructions, &lt;a href="https://www.jetbrains.com/help/qodana/cloud-forward-reports.html" rel="noopener noreferrer"&gt;see our documentation&lt;/a&gt;.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Project setup
&lt;/h3&gt;

&lt;p&gt;Setting up a project in Qodana Cloud takes five simple steps: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Trigger the first run&lt;/strong&gt;. First, Qodana analyzes your project using vital checks only. It will identify the number of files and folders containing problems, the languages used, and other important info about your project. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize your analysis&lt;/strong&gt;. Next, Qodana offers you the option to enable extra inspections that might be critical for your analysis. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Narrow the analysis down&lt;/strong&gt;. You can then exclude certain files and folders from the analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create technical debt.&lt;/strong&gt; Our favorite part is the ability to add detected problems to the baseline, allowing you to get back to them when you have time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply the inspections throughout the project.&lt;/strong&gt; To apply the selected settings to your project, download &lt;code&gt;qodana.yaml&lt;/code&gt; and &lt;code&gt;qodana.sarif.json&lt;/code&gt;, put them in the root folder, and restart Qodana.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ht8kriykivzlu2tflve.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ht8kriykivzlu2tflve.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s all for now! If you have any suggestions for future blog topics or if you want to learn more about how Qodana can help you and your business, post a comment here, tag us on &lt;a href="https://twitter.com/Qodana" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or contact us at &lt;a href="mailto:qodana-support@jetbrains.com"&gt;qodana-support@jetbrains.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy developing, and keep your code clean!&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>coding</category>
      <category>collaboration</category>
      <category>news</category>
    </item>
    <item>
      <title>Qodana 2022.3 EAP Is Out: Qodana for .NET and Go and 100+ New Inspections</title>
      <dc:creator>Viktor Tiulpin </dc:creator>
      <pubDate>Wed, 02 Nov 2022 11:22:28 +0000</pubDate>
      <link>https://forem.com/qodana/qodana-20223-eap-is-out-qodana-for-net-and-go-and-100-new-inspections-2on5</link>
      <guid>https://forem.com/qodana/qodana-20223-eap-is-out-qodana-for-net-and-go-and-100-new-inspections-2on5</guid>
      <description>&lt;p&gt;We’re delighted to announce the release of Qodana 2022.3 EAP. This version of the platform brings support for NET. and Go, and over 100 new inspections for cleaner code. However, please, note that Qodana 2022.2 images are more stable, as Qodana 2022.3 EAP is still in its infancy.&lt;/p&gt;

&lt;p&gt;Read on to learn more and become an early adopter of some exciting new features!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxqu3nfp36o2gkzsng31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxqu3nfp36o2gkzsng31.png" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jetbrains.com/qodana" rel="noopener noreferrer"&gt;GET STARTED WITH QODANA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our brand new linters bring all of the smarts from &lt;a href="https://www.jetbrains.com/rider/" rel="noopener noreferrer"&gt;Rider&lt;/a&gt; and &lt;a href="https://www.jetbrains.com/go/" rel="noopener noreferrer"&gt;GoLand&lt;/a&gt; to enable you to spot anomalous code and potential bugs, eliminate dead code, improve the overall code structure, and introduce coding best practices across all of your .NET and Go projects!&lt;/p&gt;

&lt;h2&gt;
  
  
  Qodana for .NET
&lt;/h2&gt;

&lt;p&gt;Qodana supports almost all .NET inspections provided by Rider. Since there’s a long list of them, check out the &lt;a href="https://www.jetbrains.com/help/rider/Code_Analysis__Code_Inspections.html" rel="noopener noreferrer"&gt;Rider documentation&lt;/a&gt; to learn more about all of the inspections. Meanwhile, a few examples of the .NET inspections that Qodana can run are below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inconsistent lock ordering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the main issues when using locks to achieve thread safety is avoiding deadlocks, i.e. when threads simultaneously block each other from continuing execution, so no progress is made. With this new inspection, Qodana will highlight cycles leading to possible deadlocks at runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xhb3my8t16j5s03bakk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xhb3my8t16j5s03bakk.png" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access to modified captured variable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Qodana for .NET detects access to the captured variable from an anonymous method when the variable is modified externally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakl3narcjasst1w2tv3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakl3narcjasst1w2tv3a.png" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid using ‘async’ lambda when delegate type returns ‘void’&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection spots the usage of ‘async’ lambda expressions: any exceptions unhandled by the lambda will never affect the caller thread and will not be caught with the catch clause.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqw6r0y69l3i5l9t5vvwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqw6r0y69l3i5l9t5vvwo.png" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type check and casts can be merged&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The type-testing &lt;code&gt;is&lt;/code&gt; operator in its classical form &lt;code&gt;(Expression is Type)&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; only when the runtime type of &lt;code&gt;Expression&lt;/code&gt; is compatible with &lt;code&gt;Type&lt;/code&gt; and the result of &lt;code&gt;Expression&lt;/code&gt; is not &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When we use &lt;code&gt;is&lt;/code&gt; to check the compatibility before casting, like in the example below, we encounter at least two problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We perform the type check twice for no reason, which can affect performance if we do it inside a loop.&lt;/li&gt;
&lt;li&gt;The fact that the program execution will not get into the &lt;code&gt;if&lt;/code&gt; statement if &lt;code&gt;obj&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; is not immediately clear to those who read this code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Qodana will spot this issue and suggest you fix it right in Rider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh4eloha8vrt9qlm4ofp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh4eloha8vrt9qlm4ofp.png" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda expression/anonymous method should not have captures of the containing context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The lambda expression/anonymous method passed to a parameter annotated by the ‘[RequireStaticDelegate]’ attribute must not have captures of the containing context (local variables, local functions, ‘this’ reference) to avoid heap allocations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkicw13b4lcxwkzrwqkq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkicw13b4lcxwkzrwqkq.png" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To configure the linter for .NET and run analysis, please refer to the &lt;a href="https://www.jetbrains.com/help/qodana/2022.3/qodana-dotnet-docker-readme.html" rel="noopener noreferrer"&gt;Qodana documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qodana for Go
&lt;/h2&gt;

&lt;p&gt;Qodana 2022.3 is designed to support all inspections provided by GoLand. To see the exhaustive list, please refer to the &lt;a href="https://www.jetbrains.com/help/go/code-inspections-in-go.html" rel="noopener noreferrer"&gt;GoLand documentation&lt;/a&gt;. Below are examples of some of the Go inspections that Qodana now supports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Placeholder argument ‘d.DeletedCount’ has the wrong type ‘int64’ (%s)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection reports incorrect usages of &lt;code&gt;fmt.Printf&lt;/code&gt;, &lt;code&gt;fmt.Println&lt;/code&gt;, and similar formatting and printing functions.&lt;/p&gt;

&lt;p&gt;In their format strings, formatting functions use formatting verbs, like &lt;code&gt;%s&lt;/code&gt;, &lt;code&gt;%d&lt;/code&gt;, &lt;code&gt;%v&lt;/code&gt;, and others.&lt;/p&gt;

&lt;p&gt;If formatting verbs are used incorrectly, the result of a formatting function will contain an error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7neb8vgz290a4zvr1dlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7neb8vgz290a4zvr1dlx.png" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unhandled error&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection reports calls to functions and methods that do not handle the call result of the &lt;code&gt;error&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;An API of such functions imply that their execution might finish unsuccessfully and they would return an error. Calls that do not handle the error result could be an indication of misuse of the API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3pebycejzvznv9uxmxj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3pebycejzvznv9uxmxj.png" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unused dependency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection reports unused dependencies on your project. It’s good practice to scan for unused dependencies on a regular basis, as it reduces the library size of your project and improves maintainability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ou8d7b062wc23brvdnx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ou8d7b062wc23brvdnx.png" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see Qodana in action and play around with these new inspections, feel free to jump into our documentation and see how to configure linters for &lt;a href="https://www.jetbrains.com/help/qodana/2022.3/qodana-go-docker-readme.html" rel="noopener noreferrer"&gt;Go&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  New inspections
&lt;/h2&gt;

&lt;p&gt;Besides adding new linters for .NET and Go, Qodana 2022.3 also brings you over 100 new inspections for the existing linters. Let’s take a look at the most prominent examples of inspections for Java, Kotlin, and Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java and Kotlin inspections
&lt;/h3&gt;

&lt;p&gt;We’ve added over 40 new inspections to Qodana for JVM Community and Qodana for JVM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DataFlowIssue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection reports code constructs that always violate nullability contracts, may throw exceptions, or are redundant, based on data flow analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EscapedSpace&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Java 15 introduced the string escape sequence \s to make trailing whitespace in text-blocks visible. In most other contexts, especially in regular expressions, this escape sequence can easily be confused with the &lt;code&gt;\s&lt;/code&gt; of a regular expression meaning whitespace. In Java string literals it has to be written as &lt;code&gt;"\\s"&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MismatchedJavadocCode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection reports cases in which the Javadoc of a method obviously contradicts the method signature, such as the comment saying “returns true” when the method returns a string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Destructure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This inspection reports declarations in Kotlin that can be destructured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UnresolvedRestParam&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now Qodana can detect inconsistent method declarations in REST services (such as &lt;code&gt;@PathParam&lt;/code&gt; parameters that don’t match a placeholder from the &lt;code&gt;@Get&lt;/code&gt;annotation), as these would throw exceptions at runtime. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReactiveStreamsThrowInOperator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This new inspection spots &lt;code&gt;throw&lt;/code&gt; statements in Reactor/RxJava operator code instead of returning designated error values, which prevents the errors from being processed further. For example, it ignores them or replaces them with a fallback value.&lt;/p&gt;

&lt;p&gt;Please, note that this version of Qodana also brings all of the new inspections for Android from IntelliJ IDEA 2022.3 and Android Studio – Electric Eel | 2022.1.1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python inspections
&lt;/h3&gt;

&lt;p&gt;We’ve also added some inspections for the Google App Engine to Qodana for Python that will highlight issues before they cause failures in production environments. For example, now you can spot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GQL queries that  do not comply with restrictions for queries allowed on the Google App Engine server.&lt;/li&gt;
&lt;li&gt;GQL queries without indexes.&lt;/li&gt;
&lt;li&gt;Usages of Python features that are restricted by the Google App Engine sandbox.&lt;/li&gt;
&lt;li&gt;Сases when threadsafe is not enabled with the CGI handler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To exclude certain inspections from your analysis, you can customize your default inspection profile or create a brand new one. You may also want to ​​enforce inspections that are important to your coding guidelines. Check out our &lt;a href="https://www.jetbrains.com/help/qodana/qodana-yaml.html#Include+an+inspection+into+the+analysis+scope" rel="noopener noreferrer"&gt;Qodana documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;That’s everything about Qodana 2022.3 EAP! We hope you enjoy this new release. If you have any suggestions for future blog topics or if you want to learn more about how Qodana can help you and your business, post a comment here, tag us on &lt;a href="https://twitter.com/Qodana" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or contact us at &lt;em&gt;&lt;a href="mailto:qodana-support@jetbrains.com"&gt;qodana-support@jetbrains.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Happy developing and keep your code clean!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your Qodana team&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>news</category>
      <category>releases</category>
    </item>
    <item>
      <title>Qodana 2022.2 Is Available: 50+ New Inspections and CircleCI Orb</title>
      <dc:creator>Viktor Tiulpin </dc:creator>
      <pubDate>Fri, 05 Aug 2022 15:12:57 +0000</pubDate>
      <link>https://forem.com/qodana/qodana-20222-is-available-50-new-inspections-and-circleci-orb-3i3b</link>
      <guid>https://forem.com/qodana/qodana-20222-is-available-50-new-inspections-and-circleci-orb-3i3b</guid>
      <description>&lt;p&gt;Qodana 2022.2 is now available, bringing new and improved code inspections for Java, Kotlin, Android, PHP, JS, and Python. Additionally, we’ve added &lt;a href="https://circleci.com/developer/orbs/orb/jetbrains/qodana?version=2022.2.1" rel="noopener noreferrer"&gt;CircleCI Orb&lt;/a&gt; to the Qodana integration toolset. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0xs18yjyrv7vhuofcu1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0xs18yjyrv7vhuofcu1.png" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jetbrains.com/qodana" rel="noopener noreferrer"&gt;GET STARTED WITH QODANA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More CIs to run Qodana with
&lt;/h2&gt;

&lt;p&gt;Qodana already has plugins for &lt;a href="https://www.jetbrains.com/help/qodana/qodana-azure-pipelines.html" rel="noopener noreferrer"&gt;Azure Pipelines&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/help/qodana/qodana-github-action.html" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;, and &lt;a href="https://www.jetbrains.com/help/qodana/teamcity.html" rel="noopener noreferrer"&gt;TeamCity&lt;/a&gt;. Starting from 2022.2, we’ve prepared a &lt;a href="https://circleci.com/developer/orbs/orb/jetbrains/qodana" rel="noopener noreferrer"&gt;CircleCI Qodana orb&lt;/a&gt; that allows you to set up code inspections quickly and easily with your CircleCI projects.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FD-yaEpuhrBZkxU2Kv3C9DNKH4ctdoPi3uPKbcsuxH9I5M5lPw-QrUho3h2mLdEsnJ-Na1P9TaD0ySxDtLP8vUxJyhVmOBSBrs0Q1KVQSWat_c54vEDlqRALOhPcUjChzm0mmESAFGlI4kbvGxh1NGiM" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FD-yaEpuhrBZkxU2Kv3C9DNKH4ctdoPi3uPKbcsuxH9I5M5lPw-QrUho3h2mLdEsnJ-Na1P9TaD0ySxDtLP8vUxJyhVmOBSBrs0Q1KVQSWat_c54vEDlqRALOhPcUjChzm0mmESAFGlI4kbvGxh1NGiM" width="1600" height="1133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, it’s easy to set up Qodana in &lt;a href="https://www.jetbrains.com/help/qodana/gitlab.html" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/help/qodana/jenkins.html" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;, or &lt;a href="https://www.jetbrains.com/help/qodana/getting-started.html#docker-image-tab" rel="noopener noreferrer"&gt;any other CI that supports running Docker images&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  New inspections
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Regular expressions
&lt;/h3&gt;

&lt;p&gt;Regular expressions are widely known for their complexity, intricate syntax, and sometimes verbosity. To make life easier, we’ve added new inspections in this area. Previously these inspections were available only for Java, but we’ve now made them available for all languages. &lt;/p&gt;

&lt;h4&gt;
  
  
  Simplified regular expressions
&lt;/h4&gt;

&lt;p&gt;A regular expression like &lt;code&gt;[\wa-z\d]&lt;/code&gt; can be simplified to just &lt;code&gt;\w&lt;/code&gt; since &lt;code&gt;\w&lt;/code&gt; already includes &lt;code&gt;a-z&lt;/code&gt; as well as the digits.  It helps improve the code’s overall readability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Suspicious backreferences
&lt;/h4&gt;

&lt;p&gt;A regular expression like &lt;code&gt;\1(abc)&lt;/code&gt; cannot match anything. This is because the &lt;code&gt;\1&lt;/code&gt; refers to the &lt;code&gt;abc&lt;/code&gt; that is not yet defined when evaluating the &lt;code&gt;\1&lt;/code&gt;. This inspection prevents simple typos in regular expressions and speeds up the editing experience. &lt;/p&gt;

&lt;h4&gt;
  
  
  Redundant &lt;code&gt;\d&lt;/code&gt;, &lt;code&gt;[:digit:]&lt;/code&gt;, or &lt;code&gt;\D&lt;/code&gt; class elements
&lt;/h4&gt;

&lt;p&gt;The regular expression &lt;code&gt;[\w+\d]&lt;/code&gt; can be written as &lt;code&gt;[\w+]&lt;/code&gt;, as the &lt;code&gt;\w&lt;/code&gt; already includes the &lt;code&gt;\d&lt;/code&gt;. It helps improve the code’s overall readability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Markdown support
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Incorrectly numbered list items
&lt;/h4&gt;

&lt;p&gt;Ordered list items like &lt;code&gt;1. 2. 4.&lt;/code&gt; are marked as being inconsistently numbered. In the rendered Markdown, the list is still displayed as &lt;code&gt;1. 2. 3.&lt;/code&gt;, but the inconsistency makes editing the source code harder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java, Kotlin, and Android inspections
&lt;/h3&gt;

&lt;p&gt;We’ve added and reorganized inspections in the categories: Javadoc, DevKit, Markdown, Kotlin language, style, architectural patterns, performance, and JUnit support. Here are a couple of examples from the JUnit set.&lt;/p&gt;

&lt;h4&gt;
  
  
  JUnit: Malformed Declaration
&lt;/h4&gt;

&lt;p&gt;Reports the JUnit test member declarations that are malformed and are likely not to be recognized by the JUnit test framework. Declarations like these could result in unexecuted tests or lifecycle methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19w6j6eyeuex9i6gjif2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19w6j6eyeuex9i6gjif2.png" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesz1k6mkvcqvplgrryck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesz1k6mkvcqvplgrryck.png" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  JUnit: Unconstructable TestCase
&lt;/h4&gt;

&lt;p&gt;Reports JUnit test cases that can’t be constructed because they have an invalid constructor. Test cases like these will not be picked up by the JUnit test runner and will therefore not execute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F482qqdm61vskykhz2k9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F482qqdm61vskykhz2k9z.png" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those examples you can see live on &lt;a href="https://qodana.teamcity.com/buildConfiguration/Hosted_Root_Java_Build/61718?buildTab=Qodana&amp;amp;genericFilters=N4IgLg9hA2IFwG0QGEIBMCmACAkgOwGcAHDAYzAEsI8QBdAGhAIwDcMAnCsAT3iQAkKAcwAWdRqQCGYDEIjteiEABUMBMFgBm7SQFsMAd3kBrAnQC%2BQA&amp;amp;locationFilters=NoXSA&amp;amp;orderedLevels=NoIgLg9hA2IDQgM4FMBuyBOBLMBPeIAxgIZjIDmEG%2BCeADsiALpA&amp;amp;showingBaseline=GYQwNgzgpkA" rel="noopener noreferrer"&gt;our public TeamCity instance&lt;/a&gt;. Please use the Guest login to enter. Other inspections are described in &lt;a href="https://www.jetbrains.com/help/qodana/2022.2/new-in-2022-2.html" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP inspections
&lt;/h3&gt;

&lt;p&gt;We added inspections in Probable bugs, Documentation, Style, Testing, and Laravel categories, for example:&lt;/p&gt;

&lt;h4&gt;
  
  
  Probable bug: Number ranges mismatch
&lt;/h4&gt;

&lt;p&gt;In a function that is declared with &lt;code&gt;returns int&amp;lt;0,10&amp;gt;&lt;/code&gt;, marks return statements that return a number outside this range. Similarly for fields, field constructors and function calls. &lt;/p&gt;

&lt;h4&gt;
  
  
  Documentation: Type tag without variable name
&lt;/h4&gt;

&lt;p&gt;The PHPDoc snippet &lt;code&gt;@param string&lt;/code&gt; is redundant as it doesn’t say &lt;em&gt;what&lt;/em&gt; is a string. It should be either removed or replaced with &lt;code&gt;@param string $argument&lt;/code&gt;, saying that &lt;em&gt;argument&lt;/em&gt; is a string. &lt;/p&gt;

&lt;h4&gt;
  
  
  Blade: Parse error due to unpaired parentheses in string literals
&lt;/h4&gt;

&lt;p&gt;Early detection of unpaired parentheses in string literals that are later parsed by Blade, a template engine.&lt;/p&gt;

&lt;p&gt;To include or exclude certain inspections from your analysis, you can customize your default inspection profile or create a brand new one. You may also want to ​​enforce inspections that are important to your coding guidelines or best practices. Check out our &lt;a href="https://www.jetbrains.com/help/qodana/qodana-yaml.html#Include+an+inspection+into+the+analysis+scope" rel="noopener noreferrer"&gt;Qodana documentation&lt;/a&gt; for more information. &lt;/p&gt;

&lt;p&gt;If you have any suggestions for future blog topics or if you want to learn more about how Qodana can help you and your business, post a comment here, tag us on &lt;a href="https://twitter.com/Qodana" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or contact us at &lt;em&gt;&lt;a href="mailto:qodana-support@jetbrains.com"&gt;qodana-support@jetbrains.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your Qodana team&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>releases</category>
      <category>circleci</category>
    </item>
    <item>
      <title>Better Late Than Never, or New Year’s Resolutions with Qodana</title>
      <dc:creator>Viktor Tiulpin </dc:creator>
      <pubDate>Tue, 28 Dec 2021 12:00:00 +0000</pubDate>
      <link>https://forem.com/qodana/better-late-than-never-or-new-years-resolutions-with-qodana-i4h</link>
      <guid>https://forem.com/qodana/better-late-than-never-or-new-years-resolutions-with-qodana-i4h</guid>
      <description>&lt;p&gt;Even before we started to work on &lt;a href="https://jetbrains.com/qodana" rel="noopener noreferrer"&gt;Qodana&lt;/a&gt;, we knew from our own experience and user interviews that it’s hard to add static analysis to a project, which leads people to postpone this decision. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1475799581434781699-996" src="https://platform.twitter.com/embed/Tweet.html?id=1475799581434781699"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1475799581434781699-996');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1475799581434781699&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;If you join a project with established code quality procedures, you can just follow the rules. If you start a shiny new project, you can pick any set of rules you want. This option has its pitfalls, but at least you know that your code meets your own standards from the beginning.&lt;/p&gt;

&lt;p&gt;In contrast, if you join or work for a project where quality gates are not yet part of the process, being pressured to implement them could be a heartbreaking experience! We’ve felt it ourselves, and heard from many users who opened up in interviews: implementing the appropriate gateways can be a bumpy road.&lt;/p&gt;

&lt;p&gt;Most people we talked to encountered issues with the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Figuring out what kind of rules to apply&lt;/li&gt;
&lt;li&gt;Determining what to do with the existing issues&lt;/li&gt;
&lt;li&gt;Identifying how to make all of this sustainable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our goal with Qodana has always been to provide a pleasant experience for those who decide to have quality gates, and we’ve developed two new features to further this goal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project setup wizard, which lets you tailor your analysis configuration further based on the initial results.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jetbrains.com/help/qodana/qodana-baseline.html" rel="noopener noreferrer"&gt;Technical debt (or baseline)&lt;/a&gt;, which lets you mark a given state of the project as initial and track further changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have been thinking about using quality gates for your project but haven’t known where to start, this post is right for you. You don’t need to wait until the first week of January to adopt your New Year’s resolution: you can get started now, and make your life in the new year easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a local run and create a baseline
&lt;/h2&gt;

&lt;p&gt;Let’s start with your local machine. If you use a JetBrains IDE and are familiar with code &lt;a href="https://www.jetbrains.com/help/idea/customizing-profiles.html" rel="noopener noreferrer"&gt;inspection profiles&lt;/a&gt;, you can &lt;a href="https://www.jetbrains.com/help/qodana/qodana-yaml.html#Set+up+a+profile" rel="noopener noreferrer"&gt;re-use them in your Qodana run&lt;/a&gt;. If you are unsure of what server-side analysis fits your project best, you can start with Qodana’s default options. We introduced &lt;a href="https://www.jetbrains.com/help/qodana/qodana-yaml.html#three-phase-analysis" rel="noopener noreferrer"&gt;three-phase analysis&lt;/a&gt; precisely for this case.&lt;/p&gt;

&lt;p&gt;Follow these steps to run Qodana on your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick the proper &lt;a href="https://www.jetbrains.com/help/qodana/linters.html" rel="noopener noreferrer"&gt;Qodana linter for your project’s technology&lt;/a&gt; stack and pull its image:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull jetbrains/qodana-&amp;lt;linter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Options include &lt;code&gt;qodana-jvm&lt;/code&gt;, &lt;code&gt;qodana-jvm-android&lt;/code&gt;, &lt;code&gt;qodana-php&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perform the first run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &amp;lt;project-root-directory&amp;gt;/:/data/project/ &lt;span class="se"&gt;\&lt;/span&gt;
  jetbrains/qodana-&amp;lt;linter&amp;gt; &lt;span class="nt"&gt;--show-report&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your project’s working folder likely has all the required dependencies in place, so you don’t need to perform any additional steps for the analysis to succeed. In cases when you’ve just cloned the repository and haven’t previously worked in the editor on this machine, depending on the project technology, either Qodana will do everything on its own or you will need to take one extra step to download project dependencies. For more details about this step, please check out the corresponding section in &lt;a href="https://www.jetbrains.com/help/qodana/linters.html" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After the analysis is complete, you will see an invitation to check the results. Open Qodana UI in your browser using this link: &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will see a page that looks like the example below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fweb4von27wruzk8qjf18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fweb4von27wruzk8qjf18.png" alt="Qodana UI main screen" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During this first run, Qodana analyzes your project using vital checks only. Non-vital checks and folders containing non-vital code, such as the &lt;em&gt;Tests&lt;/em&gt; folder, are ignored. Qodana also reports any conditions that could affect the truthfulness or completeness of the results. For example, if your project relies on external resources or generated code that is unavailable during the analysis, the final results could be compromised. Qodana notifies you about such suspicious results. We encourage you to follow the guidance during project setup and tune the configuration. After the first run, everything you need will be at hand. You can review every problem in the left-hand panel, and you will see immediately how your adjustments affect the number of issues you need to work on.&lt;/p&gt;

&lt;p&gt;Our favorite step is the third one – putting all the detected problems into a &lt;a href="https://www.jetbrains.com/help/qodana/qodana-baseline.html" rel="noopener noreferrer"&gt;baseline&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F720ynzfk9zm341fcoybb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F720ynzfk9zm341fcoybb.png" alt="Baseline screen" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why put everything in a &lt;a href="https://bit.ly/what-is-technical-debt" rel="noopener noreferrer"&gt;technical debt&lt;/a&gt; (baseline)? Well, bringing the number of problems in your code down to zero is quite simple, really: first, fix all existing issues; second, ensure no new problems appear over time. The second part may be easier than the first one, and by using the baseline, we set the current project state as the initial one and only track new issues that appear.&lt;/p&gt;

&lt;p&gt;The last step in the project setup is downloading two files: &lt;code&gt;qodana.yaml&lt;/code&gt; and &lt;code&gt;qodana.sarif.json&lt;/code&gt;. Just place them into your project folder, and be sure to copy the suggested Docker command.&lt;/p&gt;

&lt;h2&gt;
  
  
  A local run with the baseline enabled
&lt;/h2&gt;

&lt;p&gt;Now return to your console and run Qodana again using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &amp;lt;project-root-directory&amp;gt;:/data/project &lt;span class="se"&gt;\&lt;/span&gt;
  jetbrains/qodana-&amp;lt;linter&amp;gt; &lt;span class="nt"&gt;--show-report&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;
  &lt;span class="nt"&gt;--baseline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;baseline.sarif.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please ensure you use the proper Docker image name.&lt;/p&gt;

&lt;p&gt;You will see something similar to the example below. Qodana now treats all of the previously discovered issues as the baseline and does not report them again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7y4bmm7mzxlx3uftdr9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7y4bmm7mzxlx3uftdr9b.png" alt="Qodana report with the baseline set" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Add even more checks
&lt;/h2&gt;

&lt;p&gt;At this point, you are ready for the next step – setting the quality gate in your CI. If you want to get started with that right away, feel free to jump right to the &lt;em&gt;Quality gate&lt;/em&gt; section of this post.&lt;/p&gt;

&lt;p&gt;But if you are feeling adventurous, you can examine the suggestions Qodana prepared for you, and perhaps enable even more checks. Use the &lt;em&gt;Problem examples&lt;/em&gt; dialog to see the checks we suggest, as well as some examples in your codebase that need improvement according to those rules. If you find them appropriate, turn them on, download the updated &lt;code&gt;qodana.yaml&lt;/code&gt; file to the project root folder, and run Qodana locally again to perform the same triage using even more checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38xim4xyos6i73gg74q7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38xim4xyos6i73gg74q7.png" alt="Qodana profile" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can repeat the previous steps again and again, thereby tailoring your configuration further. &lt;/p&gt;

&lt;p&gt;When you are happy with what you see locally – it’s time to set a quality gate for your CI pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality gate for the CI pipeline
&lt;/h2&gt;

&lt;p&gt;One good thing about Qodana is that you can add it to any CI platform. But since it would be hard to cover all integration-related nuances in a single blog post, we will describe its integration with GitHub Actions. The process of setting up quality gates is very similar in all other CI platforms, too, with the exception of TeamCity. In TeamCity, we would suggest you use our &lt;a href="https://plugins.jetbrains.com/plugin/15498-qodana" rel="noopener noreferrer"&gt;dedicated plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, if you use GitHub Actions as your main CI platform, follow these steps to set up quality gates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;qodana.yaml&lt;/code&gt; and &lt;code&gt;qodana.sarif.json&lt;/code&gt; to your VCS. If you find &lt;code&gt;qodana.sarif.json&lt;/code&gt; is too big to be placed in your VCS, you can store it anywhere it is accessible for your GitHub Actions.&lt;/li&gt;
&lt;li&gt;Add the &lt;a href="https://github.com/JetBrains/qodana-action" rel="noopener noreferrer"&gt;Qodana GitHub Action&lt;/a&gt; to your workflow by following the instructions in &lt;a href="https://www.jetbrains.com/help/qodana/qodana-github-action.html" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Set the workflow to run on &lt;em&gt;pull_request&lt;/em&gt; events that target the main branch.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  pull_request:
    branches:
    - main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add the &lt;em&gt;fail-threshold&lt;/em&gt; option with a value of &lt;em&gt;0&lt;/em&gt; to the Qodana Action, as well as &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule" rel="noopener noreferrer"&gt;branch protection rules&lt;/a&gt; to any branch you want to protect. We suggest starting with the main branch. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dyifr5fpuatqpjoxddi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dyifr5fpuatqpjoxddi.png" alt="GitHub Quality Gate" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information about branch protection rules, refer to the &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule" rel="noopener noreferrer"&gt;GitHub Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now you are all set! Your changes will be checked by Qodana so they will meet your standards. You can use this action as a proper quality gate. For example, you can check incoming pull requests and merge only those whose quality is up to your standards. If your most common scenario involves developing in branches, you can add this action to make sure the whole branch is checked before being merged into the main branch. Actually, you can include a Qodana Action in any part of your pipeline and use the produced artifacts and resulting exit code as the basis for further decisions.&lt;/p&gt;

&lt;p&gt;And if, like us, you like making New Year’s resolutions, you can make a plan to work on the issues from the baseline one step at a time in 2022. Qodana UI will help you plan out this work, e.g. you can choose issues to be fixed by category, or folder, or just one-by-one.&lt;/p&gt;

&lt;p&gt;When you reach zero issues in the actual list and the baseline, it will be time to go to the ‘next level’. You can turn to Qodana’s suggestions for inspiration, or study the whole list of checks to add more of them to your profile.&lt;/p&gt;

&lt;p&gt;By the way, Qodana is extensible: you can create your own checks. We will cover this topic in a dedicated blogpost at the beginning of next year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it now and improve your code
&lt;/h2&gt;

&lt;p&gt;Check &lt;a href="https://www.jetbrains.com/qodana/" rel="noopener noreferrer"&gt;out the website&lt;/a&gt; to download the version of the components that’s suitable for your technology stack. Some of them have already reached stable releases, and some are available under an Early Access Program (EAP).  Don’t wait to set yourself up to meet your code quality goals in the new year. Future you will thank you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;We would be grateful for any feedback you have, and all ideas are welcome! Contact us at &lt;a href="mailto:qodana-support@jetbrains.com"&gt;qodana-support@jetbrains.com&lt;/a&gt; or via our &lt;a href="https://youtrack.jetbrains.com/issues/QD" rel="noopener noreferrer"&gt;issue tracker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your JetBrains Qodana Team&lt;br&gt;&lt;br&gt;
The Drive to Develop&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>staticanalysis</category>
      <category>codereview</category>
      <category>jetbrains</category>
    </item>
  </channel>
</rss>
