<?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>DEV Community: Lucas Cimon</title>
    <description>The latest articles on DEV Community by Lucas Cimon (@lucasc).</description>
    <link>https://dev.to/lucasc</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%2F648772%2F4e1b917c-b566-4235-93ad-4a63d89c4d5b.jpeg</url>
      <title>DEV Community: Lucas Cimon</title>
      <link>https://dev.to/lucasc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucasc"/>
    <language>en</language>
    <item>
      <title>fpdf2 latest news</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Mon, 14 Oct 2024 15:47:50 +0000</pubDate>
      <link>https://dev.to/lucasc/fpdf2-latest-news-7pc</link>
      <guid>https://dev.to/lucasc/fpdf2-latest-news-7pc</guid>
      <description>&lt;p&gt;You can find a selection of the features &amp;amp; improvements in &lt;code&gt;fpdf2&lt;/code&gt; made over the last 18 months in this blog post: &lt;a href="https://chezsoi.org/lucas/blog/fpdf2-latest-news.html" rel="noopener noreferrer"&gt;https://chezsoi.org/lucas/blog/fpdf2-latest-news.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fk7kcd1f2zqfa1zajqlw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fk7kcd1f2zqfa1zajqlw3.png" alt="fpdf2 latest news" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>fpdf2</category>
      <category>pdf</category>
      <category>news</category>
    </item>
    <item>
      <title>Setting up linters in Gitlab CI for C++ and Groovy / Jenkins code</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Fri, 03 May 2024 11:42:05 +0000</pubDate>
      <link>https://dev.to/lucasc/setting-up-linters-in-gitlab-ci-for-c-and-groovy-jenkins-code-3m4l</link>
      <guid>https://dev.to/lucasc/setting-up-linters-in-gitlab-ci-for-c-and-groovy-jenkins-code-3m4l</guid>
      <description>&lt;p&gt;A very short blog post to share some minimal code snippets on how to quickly and easily setup static code analysis tools on C++ code and Jenkins pipelines (or any Groovy code), for example in Gitlab CI pipelines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fx5xy6qs3m0zu4hcigll6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fx5xy6qs3m0zu4hcigll6.png" alt="Logos Gitlab-CI, CodeNarc &amp;amp; clang-tidy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Linting C++ code with clang-tidy
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://clang.llvm.org/extra/clang-tidy/" rel="noopener noreferrer"&gt;clang-tidy&lt;/a&gt; is a clang-based C++ linter tool that can identify and auto-fix some programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;cpp-linter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# No need to specify the next line if your default Docker image is already an alpine:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine:3.17&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk add clang-extra-tools&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;find . -name '*.cpp' -exec clang-tidy --fix {} \; | tee clang-tidy.log&lt;/span&gt;
    &lt;span class="c1"&gt;# Fail the job if errors were found:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!&lt;/span&gt; &lt;span class="s"&gt;grep "Found compiler error" clang-tidy.log&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Executing for every commit on the main branch where C++ files were modified:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_PIPELINE_SOURCE == "push" &amp;amp;&amp;amp; $CI_COMMIT_BRANCH == "main"&lt;/span&gt;
      &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.cpp"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.cpp"&lt;/span&gt;
    &lt;span class="c1"&gt;# Executing for every commit in a non-draft merge request where C++ files were modified&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_PIPELINE_SOURCE == "merge_request_event" &amp;amp;&amp;amp; $CI_MERGE_REQUEST_TITLE !~ /^Draft:.*$/&lt;/span&gt;
      &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.cpp"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.cpp"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You should also initiate a &lt;code&gt;.clang-tidy&lt;/code&gt; configuration file like this:&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;Checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*,-llvmlibc-*'&lt;/span&gt;
&lt;span class="na"&gt;WarningsAsErrors&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This default file is a strict starting point: it enables all rules, except the ones specific to LLVM, and treat all warnings as errors.&lt;br&gt;
You can then add &lt;code&gt;-${ruleName}&lt;/code&gt; to the &lt;code&gt;Checks&lt;/code&gt; entry in this file to disable some checks.&lt;br&gt;
Check the &lt;a href="https://clang.llvm.org/extra/clang-tidy/" rel="noopener noreferrer"&gt;clang-tidy documentation&lt;/a&gt; for more details about rules and suppressing errors &amp;amp; warnings using code comments.&lt;/p&gt;

&lt;p&gt;You can also generate an exhaustive &lt;code&gt;.clang-tidy&lt;/code&gt; configuration file, with an extra &lt;code&gt;CheckOptions&lt;/code&gt; field listing all default values for rules parameters, by running this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

clang-tidy -checks='*,-llvmlibc-*' -warnings-as-errors='*' -dump-config &amp;gt; .clang-tidy


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Linting Groovy / Jenkins code with CodeNarc
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://codenarc.org/" rel="noopener noreferrer"&gt;CodeNarc&lt;/a&gt; analyzes Groovy code for defects, bad practices, inconsistencies, style issues and more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It can be used to perform static code analysis on &lt;a href="https://www.jenkins.io/doc/book/pipeline/" rel="noopener noreferrer"&gt;Jenkins pipelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because &lt;a href="https://codenarc.org/codenarc-command-line.html#executing-codenarc-from-the-command-line" rel="noopener noreferrer"&gt;executing CodeNarc from the command-line&lt;/a&gt; is not so simple, I find it easier to use &lt;a href="https://gradle.org/" rel="noopener noreferrer"&gt;Gradle&lt;/a&gt; and its dedicated plugin to execute CodeNarc:&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;groovy-linter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gradle:8.7&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gradle check&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Executing for every commit on the main branch where C++ files were modified:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_PIPELINE_SOURCE == "push" &amp;amp;&amp;amp; $CI_COMMIT_BRANCH == "main"&lt;/span&gt;
      &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.groovy"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.groovy"&lt;/span&gt;
    &lt;span class="c1"&gt;# Executing for every commit in a non-draft merge request where Groovy files were modified&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_PIPELINE_SOURCE == "merge_request_event" &amp;amp;&amp;amp; $CI_MERGE_REQUEST_TITLE !~ /^Draft:.*$/&lt;/span&gt;
      &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.groovy"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.groovy"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You will also need a &lt;code&gt;build.gradle&lt;/code&gt; file in the directory where the &lt;code&gt;gradle&lt;/code&gt; command is executed:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;

&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'groovy'&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'codenarc'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;jcenter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Required if you have Jenkins dependencies:&lt;/span&gt;
    &lt;span class="n"&gt;maven&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://repo.jenkins-ci.org/public/'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;codeNarcVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'3.4.0'&lt;/span&gt;
&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CodeNarc must be added there, or you will get a ClassNotFoundException:&lt;/span&gt;
    &lt;span class="n"&gt;codenarc&lt;/span&gt; &lt;span class="nl"&gt;group:&lt;/span&gt; &lt;span class="s1"&gt;'org.codenarc'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'CodeNarc'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;version:&lt;/span&gt; &lt;span class="n"&gt;codeNarcVersion&lt;/span&gt;
    &lt;span class="n"&gt;codenarc&lt;/span&gt; &lt;span class="nl"&gt;group:&lt;/span&gt; &lt;span class="s1"&gt;'org.jenkins-ci.plugins'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'plugin'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;version:&lt;/span&gt; &lt;span class="s1"&gt;'4.79'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;sourceSets&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;groovy&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Specify the relative paths to the directories containing your .groovy files:&lt;/span&gt;
            &lt;span class="n"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;codenarc&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;configFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./codenarc_rules.groovy'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;toolVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;codeNarcVersion&lt;/span&gt;
    &lt;span class="n"&gt;reportFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// The two following blocks allow to display the CodeNarc report in the console:&lt;/span&gt;
&lt;span class="c1"&gt;// ( this recipe comes from this SO answer: https://stackoverflow.com/a/36899862/636849 )&lt;/span&gt;
&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;codenarcConsoleReport&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;doLast&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${codenarc.reportsDir}/main.txt"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;codenarcMain&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;finalizedBy&lt;/span&gt; &lt;span class="n"&gt;codenarcConsoleReport&lt;/span&gt;
    &lt;span class="n"&gt;compilationClasspath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sourceSets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compileClasspath&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sourceSets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;output&lt;/span&gt; &lt;span class="c1"&gt;//+ files('../relative/path/to/shared/lib/dir')&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// You can optionnally disable the Groovy compilation step, to execute faster:&lt;/span&gt;
&lt;span class="n"&gt;compileGroovy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And finally you can initialize a configuration file for the linter, named &lt;code&gt;codenarc_rules.groovy&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;codenarc_rules.groovy&lt;/em&gt;&lt;br&gt;
  &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ruleset {&lt;br&gt;
    ruleset('rulesets/basic.xml')&lt;br&gt;
    ruleset('rulesets/braces.xml')&lt;br&gt;
    ruleset('rulesets/comments.xml')&lt;br&gt;
    ruleset('rulesets/concurrency.xml')&lt;br&gt;
    ruleset('rulesets/convention.xml') {&lt;br&gt;
        // You may want to disable some rules, like this:&lt;br&gt;
        CompileStatic(enabled:false)&lt;br&gt;
        MethodParameterTypeRequired(enabled:false)&lt;br&gt;
        MethodReturnTypeRequired(enabled:false)&lt;br&gt;
        NoDef(enabled:false)&lt;br&gt;
        PublicMethodsBeforeNonPublicMethods(enabled:false)&lt;br&gt;
        VariableTypeRequired(enabled:false)&lt;br&gt;
        TrailingComma(enabled:false)&lt;br&gt;
    }&lt;br&gt;
    ruleset('rulesets/design.xml')&lt;br&gt;
    ruleset('rulesets/dry.xml') {&lt;br&gt;
        // Allowing several benign cases of code duplication:&lt;br&gt;
        DuplicateListLiteral(enabled:false)&lt;br&gt;
        DuplicateMapLiteral(enabled:false)&lt;br&gt;
        DuplicateNumberLiteral(enabled:false)&lt;br&gt;
        DuplicateStringLiteral(enabled:false)&lt;br&gt;
    }&lt;br&gt;
    ruleset('rulesets/enhanced.xml')&lt;br&gt;
    ruleset('rulesets/exceptions.xml')&lt;br&gt;
    ruleset('rulesets/formatting.xml') {&lt;br&gt;
        // Disabling rules that are too strict for my project:&lt;br&gt;
        BlockEndsWithBlankLine(enabled:false)&lt;br&gt;
        BlockStartsWithBlankLine(enabled:false)&lt;br&gt;
        ConsecutiveBlankLines(enabled:false)&lt;br&gt;
        Indentation(enabled:false)&lt;br&gt;
        LineLength(enabled:false)&lt;br&gt;
        SpaceAfterOpeningBrace(enabled:false)&lt;br&gt;
        SpaceAroundMapEntryColon(enabled:false)&lt;br&gt;
        SpaceBeforeClosingBrace(enabled:false)&lt;br&gt;
    }&lt;br&gt;
    ruleset('rulesets/generic.xml')&lt;br&gt;
    // ruleset('rulesets/grails.xml')&lt;br&gt;
    ruleset('rulesets/groovyism.xml')&lt;br&gt;
    ruleset('rulesets/imports.xml')&lt;br&gt;
    // ruleset('rulesets/jdbc.xml')&lt;br&gt;
    // ruleset('rulesets/junit.xml')&lt;br&gt;
    ruleset('rulesets/logging.xml') {&lt;br&gt;
        // Allowing to print to stderr &amp;amp; stdout&lt;br&gt;
        SystemErrPrint(enabled:false)&lt;br&gt;
        SystemOutPrint(enabled:false)&lt;br&gt;
    }&lt;br&gt;
    ruleset('rulesets/naming.xml')&lt;br&gt;
    ruleset('rulesets/security.xml')&lt;br&gt;
    ruleset('rulesets/serialization.xml')&lt;br&gt;
    ruleset('rulesets/size.xml')&lt;br&gt;
    ruleset('rulesets/unnecessary.xml')&lt;br&gt;
    ruleset('rulesets/unused.xml')&lt;br&gt;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The details for all rules can be found there: &lt;a href="https://codenarc.org/codenarc-rule-index.html" rel="noopener noreferrer"&gt;codenarc.org/codenarc-rule-index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Original post on my blog: &lt;a href="https://chezsoi.org/lucas/blog/setting-up-linters-in-gitlab-ci-for-c-and-groovy-jenkins-code.html" rel="noopener noreferrer"&gt;https://chezsoi.org/lucas/blog/setting-up-linters-in-gitlab-ci-for-c-and-groovy-jenkins-code.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>groovy</category>
      <category>cpp</category>
      <category>linter</category>
      <category>clang</category>
    </item>
    <item>
      <title>Pylint strict base configuration</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Wed, 03 May 2023 11:31:51 +0000</pubDate>
      <link>https://dev.to/lucasc/pylint-strict-base-configuration-1p0j</link>
      <guid>https://dev.to/lucasc/pylint-strict-base-configuration-1p0j</guid>
      <description>&lt;p&gt;&lt;a href="https://pylint.pycqa.org"&gt;Pylint&lt;/a&gt; is a great static code analyser for Python code.&lt;br&gt;
I have been using it for several years, in various projects, and it's simple to use yet very powerful.&lt;/p&gt;

&lt;p&gt;I even contributed to Pylint by submitting a new rule a few years ago : &lt;a href="https://github.com/pylint-dev/pylint/pull/1655"&gt;implicit-str-concat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For an introduction to Pylint, you can check those tutorials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;official documentation: &lt;a href="https://pylint.readthedocs.io/en/latest/tutorial.html"&gt;Tutorial&lt;/a&gt; &amp;amp; &lt;a href="https://pylint.readthedocs.io/en/latest/user_guide/usage/run.html"&gt;Usage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@oliyadkebede32/how-to-get-started-with-pylint-79bf950f61a8"&gt;How To Get Started With Pylint by Oliyadk @medium.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pylint default configuration is very reasonable, but one thing that I find missing is a simple way to switch to a "strict" mode, that would enable all optional rules. ESLint has &lt;a href="https://www.npmjs.com/package/eslint-config-strict-mode"&gt;eslint-config-strict-mode&lt;/a&gt; for example.&lt;/p&gt;

&lt;p&gt;Such "strict" mode can be useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;when bootstrapping a new project, you may want to enable all rules by default,&lt;br&gt;
and progressively disable the ones you find non necessary for your project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;on an existing project using Pylint, when you want to test what extra checks&lt;br&gt;
can be performed by this tool, and see if it can spot bugs you missed so far&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following &lt;code&gt;.pylintrc&lt;/code&gt; configuration file is my attempt to setup a "strict" mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;[MESSAGES&lt;/span&gt; &lt;span class="err"&gt;CONTROL]&lt;/span&gt;
&lt;span class="c"&gt;# Enable some checkers that are not activated by default:&lt;/span&gt;
&lt;span class="py"&gt;enable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;bad-inline-option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;deprecated-pragma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;file-ignored&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;use-symbolic-message-instead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;useless-suppression&lt;/span&gt;

&lt;span class="c"&gt;# Include some helpful details on errors messages for naming rules:&lt;/span&gt;
&lt;span class="py"&gt;include-naming-hint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;yes&lt;/span&gt;

&lt;span class="nn"&gt;[MASTER]&lt;/span&gt;
&lt;span class="c"&gt;# Informational messages ("I") should make Pylint execution fail (non-0 program return code):&lt;/span&gt;
&lt;span class="py"&gt;fail-on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;I&lt;/span&gt;
&lt;span class="c"&gt;# Enable many optional extensions:&lt;/span&gt;
&lt;span class="py"&gt;load-plugins&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;pylint.extensions.bad_builtin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.code_style,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.comparison_placement,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.consider_refactoring_into_while_condition,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.docparams,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.dunder,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.eq_without_hash,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.for_any_all,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.magic_value,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.mccabe,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.no_self_use,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.overlapping_exceptions,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.private_import,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.redefined_loop_name,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.redefined_variable_type,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.set_membership,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.typing,&lt;/span&gt;
               &lt;span class="err"&gt;pylint.extensions.while_used,&lt;/span&gt;

&lt;span class="nn"&gt;[STRING_CONSTANT]&lt;/span&gt;
&lt;span class="c"&gt;# Doc: https://pylint.pycqa.org/en/latest/user_guide/messages/warning/implicit-str-concat.html&lt;/span&gt;
&lt;span class="py"&gt;check-quote-consistency&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;yes&lt;/span&gt;

&lt;span class="nn"&gt;[VARIABLES]&lt;/span&gt;
&lt;span class="py"&gt;allow-global-unused-variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I recently used this approach on &lt;code&gt;fpdf2&lt;/code&gt;: &lt;a href="https://github.com/PyFPDF/fpdf2/pull/780"&gt;PR #780 Hardening Pylint config&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related configuration files in &lt;code&gt;fpdf2&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/PyFPDF/fpdf2/blob/master/.pylintrc"&gt;.pylintrc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PyFPDF/fpdf2/blob/master/.github/workflows/continuous-integration-workflow.yml#L36"&gt;call in GitHub Actions pipeline definition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this &lt;code&gt;.pylintrc&lt;/code&gt; could be useful to others!&lt;/p&gt;

&lt;p&gt;You can drop a comment if this article was helpful to you 😊&lt;/p&gt;

</description>
      <category>python</category>
      <category>pylint</category>
    </item>
    <item>
      <title>fpdf2 latest new features</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Mon, 03 Apr 2023 14:43:13 +0000</pubDate>
      <link>https://dev.to/lucasc/fpdf2-latest-new-features-4mn0</link>
      <guid>https://dev.to/lucasc/fpdf2-latest-new-features-4mn0</guid>
      <description>&lt;p&gt;&lt;code&gt;fpdf2&lt;/code&gt; is a simple &amp;amp; fast PDF creation library for Python:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pyfpdf/fpdf2/"&gt;https://github.com/pyfpdf/fpdf2/&lt;/a&gt; - Doc: &lt;a href="https://pyfpdf.github.io/fpdf2/"&gt;https://pyfpdf.github.io/fpdf2/&lt;/a&gt; &lt;a href="https://pypi.python.org/pypi/fpdf2"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PimLPvpz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.shields.io/pypi/v/fpdf2.svg%3Fv%3D2.7.3" alt="Pypi latest version" width="78" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we just released a new version, &lt;a href="https://github.com/PyFPDF/fpdf2/blob/master/CHANGELOG.md"&gt;v2.7&lt;/a&gt;,&lt;br&gt;
this is the time to mention some recent additions! 😊&lt;/p&gt;

&lt;p&gt;This article will present some of the major features introduced between &lt;strong&gt;v2.5.3&lt;/strong&gt; &amp;amp; &lt;strong&gt;v2.7.3&lt;/strong&gt; of &lt;code&gt;fpdf2&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Images &amp;amp; shapes&lt;/li&gt;
&lt;li&gt;Fonts &amp;amp; text&lt;/li&gt;
&lt;li&gt;Signing &amp;amp; encrypting documents&lt;/li&gt;
&lt;li&gt;Annotations&lt;/li&gt;
&lt;li&gt;Documentation &amp;amp; translated tutorials&lt;/li&gt;
&lt;li&gt;What's coming next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0bb_GHCq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://chezsoi.org/lucas/blog/images/2023/04/table-demo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0bb_GHCq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://chezsoi.org/lucas/blog/images/2023/04/table-demo.jpg" alt="Sample table rendered in a PDF document" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the full article there: &lt;a href="https://chezsoi.org/lucas/blog/fpdf2-latest-new-features.html"&gt;https://chezsoi.org/lucas/blog/fpdf2-latest-new-features.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>python</category>
      <category>lib</category>
      <category>release</category>
    </item>
    <item>
      <title>fpdf2.5.2 : SVG support and comparison with borb</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Sun, 24 Apr 2022 11:12:01 +0000</pubDate>
      <link>https://dev.to/lucasc/fpdf252-svg-support-and-comparison-with-borb-2fip</link>
      <guid>https://dev.to/lucasc/fpdf252-svg-support-and-comparison-with-borb-2fip</guid>
      <description>&lt;p&gt;&lt;code&gt;fpdf2&lt;/code&gt; is a simple &amp;amp; fast PDF creation library for Python that I have been maintaining since mid-2020.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pyfpdf/fpdf2/"&gt;https://github.com/pyfpdf/fpdf2/&lt;/a&gt; - Doc: &lt;a href="https://pyfpdf.github.io/fpdf2/"&gt;https://pyfpdf.github.io/fpdf2/&lt;/a&gt; &lt;a href="https://pypi.python.org/pypi/fpdf2"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q8hzhYIq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.shields.io/pypi/v/fpdf2.svg" alt="Pypi latest version" width="78" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, I'm going to present some of the new features that landed since &lt;a href="//hacktoberfest-on-fpdf2.html"&gt;my last post on the subject&lt;/a&gt;, including &lt;strong&gt;SVG support&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8vsZzA7b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://chezsoi.org/lucas/blog/images/2022/04/Ghostscript_Tiger.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8vsZzA7b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://chezsoi.org/lucas/blog/images/2022/04/Ghostscript_Tiger.png" alt="Ghostscript Tiger PDF preview" width="179" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will also perform a quick comparison with the &lt;a href="https://borbpdf.com/"&gt;borb&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;More after the jump: &lt;a href="https://chezsoi.org/lucas/blog/fpdf2-5-2-svg-support-and-borb.html"&gt;https://chezsoi.org/lucas/blog/fpdf2-5-2-svg-support-and-borb.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>pdf</category>
      <category>lib</category>
      <category>release</category>
    </item>
    <item>
      <title>Bonnes pratiques Gitlab CI</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Thu, 29 Jul 2021 12:18:13 +0000</pubDate>
      <link>https://dev.to/lucasc/bonnes-pratiques-gitlab-ci-5fb7</link>
      <guid>https://dev.to/lucasc/bonnes-pratiques-gitlab-ci-5fb7</guid>
      <description>&lt;p&gt;À E-voyageurs Technologies, je travaille au sein d'une équipe en charge de l'usine logicielle, qui administre depuis plusieurs années une instance Gitlab self-hosted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OW6IBqXI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuql2ngyqkd49msn0ghw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OW6IBqXI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuql2ngyqkd49msn0ghw.png" alt="Logo Gitlab CI" width="340" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cet article contient quelques-unes de nos recommandations à l'intention des utilisateurs de notre Gitlab, ayant pour but à la fois améliorer les performances de leurs pipelines, et limiter leur impact en termes de ressources sur cette instance Gitlab partagée entre des dizaines d'équipes. Un dernier volet rassemble quelques points de sécurité.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://chezsoi.org/lucas/blog/bonnes-pratiques-gitlab-ci.html"&gt;https://chezsoi.org/lucas/blog/bonnes-pratiques-gitlab-ci.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bestpractices</category>
      <category>ci</category>
      <category>gitlab</category>
      <category>french</category>
    </item>
    <item>
      <title>New release for fpdf2</title>
      <dc:creator>Lucas Cimon</dc:creator>
      <pubDate>Sun, 13 Jun 2021 13:50:32 +0000</pubDate>
      <link>https://dev.to/lucasc/new-release-for-fpdf2-40pi</link>
      <guid>https://dev.to/lucasc/new-release-for-fpdf2-40pi</guid>
      <description>&lt;p&gt;This release brings basic Markdown styling, JPEG storage improvements, presentation transitions and many other features!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2h7xEPMK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6upl1do5vsknegrnylbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2h7xEPMK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6upl1do5vsknegrnylbf.png" alt="Alt Text" width="408" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More details, including how to convert GIFs to PDFs, in this announcement blog post: &lt;a href="https://chezsoi.org/lucas/blog/fpdf2-4-0-and-converting-gifs-to-pdfs.html"&gt;https://chezsoi.org/lucas/blog/fpdf2-4-0-and-converting-gifs-to-pdfs.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
    </item>
  </channel>
</rss>
