{"id":436,"date":"2026-10-14T09:21:40","date_gmt":"2026-10-14T09:21:40","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2015\/10\/03\/chef-code-analysis-using-foodcritic\/"},"modified":"2026-10-14T09:21:40","modified_gmt":"2026-10-14T09:21:40","slug":"chef-code-analysis-using-foodcritic","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/chef-code-analysis-using-foodcritic\/","title":{"rendered":"Foodcritic for Chef Cookbooks: Practical Linting and Jenkins CI for Legacy Pipelines"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Originally published on 2015-10-03; fully revised and updated.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Foodcritic is a static analysis tool for Chef cookbooks. This walkthrough is aimed at DevOps engineers maintaining <strong>legacy Chef workflows<\/strong> and explains what Foodcritic checks, how to run it from the command line, and how to integrate it into Jenkins so cookbook issues are caught early.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading='lazy' decoding='async' class='alignnone size-full wp-image-3338 aligncenter' src='https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2015\/10\/chef-code-analysis-using-fo-1.png' alt='Chef code analysis using Foodcritic' width='600' height='400' \/><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Foodcritic checks (and what it does not)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Foodcritic analyzes cookbook code and reports rule violations. It is useful for catching structural and quality issues before running Chef on real nodes.<\/p>\n\n\n\n<ul class='wp-block-list'><li>Style consistency<\/li><li>Correctness issues<\/li><li>Syntax and common mistakes<\/li><li>Best-practice violations<\/li><li>Deprecation-related warnings<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">It does <strong>not<\/strong> validate business intent. A reported violation is a signal to review context, not an automatic instruction to rewrite code. The original tutorial also notes 47 built-in rules at that time; rule counts may vary by version.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation paths used in older environments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original post shows three installation approaches. Use these primarily for legacy systems where Foodcritic is still part of the toolchain.<\/p>\n\n\n\n<figure class='wp-block-table'><table class='has-fixed-layout'><thead><tr><th>Method<\/th><th>How it works<\/th><th>Typical use case<\/th><\/tr><\/thead><tbody><tr><td>RVM + gem<\/td><td>Install Ruby via RVM, then install the foodcritic gem<\/td><td>Multiple Ruby versions on one CI or workstation host<\/td><\/tr><tr><td>System Ruby + gem<\/td><td>Install Ruby from OS package manager, then gem install<\/td><td>Simple host setup with OS-managed Ruby<\/td><\/tr><tr><td>ChefDK bundle<\/td><td>Install ChefDK, which historically bundled Foodcritic<\/td><td>Teams standardizing on Chef&#8217;s packaged tooling<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<pre class='wp-block-code' aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\" class='language-bash'><span class=\"hljs-comment\"># RVM path (example)<\/span>\nrvm install ruby<span class=\"hljs-number\">-2.2<\/span><span class=\"hljs-number\">.3<\/span>\ngem install foodcritic\n\n<span class=\"hljs-comment\"># System package path (examples from original post)<\/span>\nsudo apt-get install ruby<span class=\"hljs-number\">-2.2<\/span><span class=\"hljs-number\">.3<\/span>\n<span class=\"hljs-comment\"># or<\/span>\nsudo yum install ruby<span class=\"hljs-number\">-2.2<\/span><span class=\"hljs-number\">.3<\/span>\n\ngem install foodcritic\n\n<span class=\"hljs-comment\"># Historical ChefDK download page<\/span>\n<span class=\"hljs-comment\"># https:\/\/downloads.getchef.com\/chef-dk\/<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Run Foodcritic locally<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once installed, run Foodcritic against a cookbook directory. Start with a full scan, then tighten policy with tags.<\/p>\n\n\n<pre class='wp-block-code'><span><code class=\"hljs\" class='language-bash'>foodcritic --version\nfoodcritic .\nfoodcritic -C .\nfoodcritic -t style .\nfoodcritic -f correctness .\nfoodcritic -f any -f ~FC014 .<\/code><\/span><\/pre>\n\n\n<figure class='wp-block-table'><table class='has-fixed-layout'><thead><tr><th>Option<\/th><th>Purpose<\/th><th>Example<\/th><\/tr><\/thead><tbody><tr><td>-t, &#8211;tags TAGS<\/td><td>Run only rules matching tags<\/td><td>foodcritic -t style .<\/td><\/tr><tr><td>-f, &#8211;epic-fail TAGS<\/td><td>Fail build when matching tags are found<\/td><td>foodcritic -f correctness .<\/td><\/tr><tr><td>-C, &#8211;context<\/td><td>Show matched lines, not only summaries<\/td><td>foodcritic -C .<\/td><\/tr><tr><td>-I, &#8211;include PATH<\/td><td>Load custom rule paths<\/td><td>foodcritic -I .\/rules .<\/td><\/tr><tr><td>-V, &#8211;version<\/td><td>Show installed version<\/td><td>foodcritic -V<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Jenkins integration workflow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A common setup is to run Foodcritic in a Jenkins freestyle job that polls source control and lints cookbooks during each build.<\/p>\n\n\n\n<pre class='mermaid'>\nflowchart TD\n    A[Developer updates cookbook] --> B[Jenkins job starts]\n    B --> C[Run foodcritic command]\n    C --> D{Violations found}\n    D -- no --> E[Build succeeds]\n    D -- yes --> F[Evaluate fail tags]\n    F --> G{Matches fail policy}\n    G -- yes --> H[Build fails]\n    G -- no --> I[Build passes with warnings]\n    H --> J[Engineer fixes cookbook]\n    I --> J\n    J --> A\n<\/pre>\n\n\n<pre class='wp-block-code' aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\" class='language-bash'><span class=\"hljs-meta\">#!\/usr\/bin\/env rvm-shell 1.9.3<\/span>\nfoodcritic .<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">In Jenkins, this command is typically added under <strong>Build Step \u2192 Execute shell<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Choose a fail policy your team can maintain<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Failing on every warning can create noise. A staged policy is often more effective: fail on high-risk categories first, then tighten over time.<\/p>\n\n\n<pre class='wp-block-code' aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\" class='language-bash'><span class=\"hljs-comment\"># Fail only on correctness problems<\/span>\nfoodcritic -f correctness .\n\n<span class=\"hljs-comment\"># Fail on any warning except one excluded rule<\/span>\nfoodcritic -f any -f ~FC014 .<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class='wp-block-table'><table class='has-fixed-layout'><thead><tr><th>Policy<\/th><th>Command pattern<\/th><th>Trade-off<\/th><\/tr><\/thead><tbody><tr><td>Advisory mode<\/td><td>foodcritic .<\/td><td>Low friction, but weak enforcement<\/td><\/tr><tr><td>Targeted enforcement<\/td><td>foodcritic -f correctness .<\/td><td>Catches likely breakage with less noise<\/td><\/tr><tr><td>Strict enforcement<\/td><td>foodcritic -f any &#8230;<\/td><td>Highest quality gate, can slow teams if noisy<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Foodcritic supports Cucumber-style tag expressions: <a href='https:\/\/github.com\/cucumber\/cucumber\/wiki\/Tags' target='_blank' rel='noopener'>https:\/\/github.com\/cucumber\/cucumber\/wiki\/Tags<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Track warning trends with a Jenkins parser<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original article uses Jenkins Warnings parsing so teams can track lint warning count over time rather than relying only on console logs.<\/p>\n\n\n<pre class='wp-block-code'><span><code class=\"hljs\" class='language-text'>^(FC&#91;0-9]+): (.*): (&#91;^:]+):(&#91;0-9]+)$<\/code><\/span><\/pre>\n\n<pre class='wp-block-code' aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\" class='language-groovy'><span class=\"hljs-keyword\">import<\/span> hudson.plugins.warnings.parser.Warning\n\n<span class=\"hljs-built_in\">String<\/span> fileName = matcher.group(<span class=\"hljs-number\">3<\/span>)\n<span class=\"hljs-built_in\">String<\/span> lineNumber = matcher.group(<span class=\"hljs-number\">4<\/span>)\n<span class=\"hljs-built_in\">String<\/span> category = matcher.group(<span class=\"hljs-number\">1<\/span>)\n<span class=\"hljs-built_in\">String<\/span> message = matcher.group(<span class=\"hljs-number\">2<\/span>)\n\n<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> Warning(fileName, Integer.parseInt(lineNumber), <span class=\"hljs-string\">'Chef Lint Warning'<\/span>, category, message)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<ul class='wp-block-list'><li>Install Git plugin (if needed) and Warnings-related plugin in Jenkins.<\/li><li>Create or configure a Jenkins job pointing to your cookbook repository.<\/li><li>Add an Execute shell step that runs Foodcritic.<\/li><li>Configure warning parsing for Foodcritic output and save the parser.<\/li><li>Enable scanning of console logs in post-build actions.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Original references and related links<\/h2>\n\n\n\n<ul class='wp-block-list'><li><a href='http:\/\/www.foodcritic.io\/' target='_blank' rel='noopener'>http:\/\/www.foodcritic.io\/<\/a><\/li><li><a href='http:\/\/acrmp.github.io\/foodcritic\/' target='_blank' rel='noopener'>http:\/\/acrmp.github.io\/foodcritic\/<\/a><\/li><li><a href='https:\/\/docs.chef.io\/foodcritic.html' target='_blank' rel='noopener'>https:\/\/docs.chef.io\/foodcritic.html<\/a><\/li><li><a href='https:\/\/downloads.getchef.com\/chef-dk\/' target='_blank' rel='noopener'>https:\/\/downloads.getchef.com\/chef-dk\/<\/a><\/li><li><a href='https:\/\/atom.io\/packages\/linter-foodcritic' target='_blank' rel='noopener'>https:\/\/atom.io\/packages\/linter-foodcritic<\/a><\/li><li><a href='http:\/\/www.slideshare.net\/harthoover\/rapid-chef-development-with-berkshelf-testkitchen-and-foodcritic' target='_blank' rel='noopener'>http:\/\/www.slideshare.net\/harthoover\/rapid-chef-development-with-berkshelf-testkitchen-and-foodcritic<\/a><\/li><li><a href='https:\/\/wiki.jenkins-ci.org\/display\/JENKINS\/Building+a+software+project' target='_blank' rel='noopener'>https:\/\/wiki.jenkins-ci.org\/display\/JENKINS\/Building+a+software+project<\/a><\/li><li><a href='http:\/\/blog.ninjahideout.com\/posts\/rvm-improved-support-for-hudson' target='_blank' rel='noopener'>http:\/\/blog.ninjahideout.com\/posts\/rvm-improved-support-for-hudson<\/a><\/li><li><a href='https:\/\/www.youtube.com\/watch?v=ZCfrNvu6nMc' target='_blank' rel='noopener'>https:\/\/www.youtube.com\/watch?v=ZCfrNvu6nMc<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Best practices for legacy Foodcritic usage<\/h2>\n\n\n\n<ul class='wp-block-list'><li>Start with advisory scans, then move to targeted fail tags such as correctness.<\/li><li>Treat each rule in context; do not auto-fix blindly.<\/li><li>Track warning trends so refactoring progress is visible across builds.<\/li><li>Pin and document your Ruby and toolchain versions for reproducible CI behavior.<\/li><li>Revalidate whether Foodcritic is still the right linter for your current Chef ecosystem.<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Foodcritic helps you statically lint Chef cookbooks for style, correctness, and common anti-patterns before convergence fails in production. This guide shows how to run it locally, wire it into Jenkins, and enforce fail policies that teams will actually use.<\/p>\n","protected":false},"author":1,"featured_media":3337,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[88],"tags":[517,407,1457,1461,1003,784,1458,1456,1459,1460,545,593],"class_list":["post-436","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-chef","tag-analysis","tag-chef","tag-chef-code-analysis","tag-chef-code-analysis-using-foodcritic","tag-code","tag-code-analysis","tag-code-analysis-using-foodcritic","tag-foodcritic","tag-foodcritic-guide","tag-foodcritic-tutorials","tag-how","tag-process"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=436"}],"version-history":[{"count":4,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/436\/revisions"}],"predecessor-version":[{"id":79308,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/436\/revisions\/79308"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media\/3337"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}