{"id":13483426,"url":"https://github.com/square/cane","last_synced_at":"2025-05-14T11:13:33.102Z","repository":{"id":2227719,"uuid":"3179770","full_name":"square/cane","owner":"square","description":"Code quality threshold checking as part of your build","archived":false,"fork":false,"pushed_at":"2023-02-06T23:14:03.000Z","size":333,"stargazers_count":1312,"open_issues_count":9,"forks_count":74,"subscribers_count":40,"default_branch":"master","last_synced_at":"2025-04-11T04:56:25.794Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/square.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-01-14T19:33:47.000Z","updated_at":"2025-03-15T00:18:55.000Z","dependencies_parsed_at":"2023-02-19T19:35:26.946Z","dependency_job_id":null,"html_url":"https://github.com/square/cane","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fcane","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fcane/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fcane/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fcane/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/square","download_url":"https://codeload.github.com/square/cane/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129530,"owners_count":22019629,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:01:11.126Z","updated_at":"2025-05-14T11:13:33.069Z","avatar_url":"https://github.com/square.png","language":"Ruby","readme":"# Cane\n\nFails your build if code quality thresholds are not met.\n\n\u003e Discipline will set you free.\n\n**Cane still technically works, but for new projects you're probably better off\nusing [Rubocop](http://rubocop.readthedocs.io/en/latest/) and customizing it to\nyour liking. It is far more comprehensive and more widely used.**\n\n## Usage\n\n    gem install cane\n    cane --abc-glob '{lib,spec}/**/*.rb' --abc-max 15\n\nYour main build task should run this, probably via `bundle exec`. It will have\na non-zero exit code if any quality checks fail. Also, a report:\n\n    \u003e cane\n\n    Methods exceeded maximum allowed ABC complexity (2):\n\n      lib/cane.rb  Cane#sample    23\n      lib/cane.rb  Cane#sample_2  17\n\n    Lines violated style requirements (2):\n\n      lib/cane.rb:20   Line length \u003e80\n      lib/cane.rb:42   Trailing whitespace\n\n    Class definitions require explanatory comments on preceding line (1):\n      lib/cane:3  SomeClass\n\nCustomize behaviour with a wealth of options:\n\n    \u003e cane --help\n    Usage: cane [options]\n\n    Default options are loaded from a .cane file in the current directory.\n\n    -r, --require FILE               Load a Ruby file containing user-defined checks\n    -c, --check CLASS                Use the given user-defined check\n\n        --abc-glob GLOB              Glob to run ABC metrics over (default: {app,lib}/**/*.rb)\n        --abc-max VALUE              Ignore methods under this complexity (default: 15)\n        --abc-exclude METHOD         Exclude method from analysis (eg. Foo::Bar#method)\n        --no-abc                     Disable ABC checking\n\n        --style-glob GLOB            Glob to run style checks over (default: {app,lib,spec}/**/*.rb)\n        --style-measure VALUE        Max line length (default: 80)\n        --style-exclude GLOB         Exclude file or glob from style checking\n        --no-style                   Disable style checking\n\n        --doc-glob GLOB              Glob to run doc checks over (default: {app,lib}/**/*.rb)\n        --doc-exclude GLOB           Exclude file or glob from documentation checking\n        --no-readme                  Disable readme checking\n        --no-doc                     Disable documentation checking\n\n        --lt FILE,THRESHOLD          Check the number in FILE is \u003c to THRESHOLD (a number or another file name)\n        --lte FILE,THRESHOLD         Check the number in FILE is \u003c= to THRESHOLD (a number or another file name)\n        --eq FILE,THRESHOLD          Check the number in FILE is == to THRESHOLD (a number or another file name)\n        --gte FILE,THRESHOLD         Check the number in FILE is \u003e= to THRESHOLD (a number or another file name)\n        --gt FILE,THRESHOLD          Check the number in FILE is \u003e to THRESHOLD (a number or another file name)\n\n    -f, --all FILE                   Apply all checks to given file\n        --max-violations VALUE       Max allowed violations (default: 0)\n        --json                       Output as JSON\n        --parallel                   Use all processors. Slower on small projects, faster on large.\n        --color                      Colorize output\n\n    -v, --version                    Show version\n    -h, --help                       Show this message\n\nSet default options using a `.cane` file:\n\n    \u003e cat .cane\n    --no-doc\n    --abc-glob **/*.rb\n    \u003e cane\n\nIt works exactly the same as specifying the options on the command-line.\nCommand-line arguments will override arguments specified in the `.cane` file.\n\n## Integrating with Rake\n\n```ruby\nbegin\n  require 'cane/rake_task'\n\n  desc \"Run cane to check quality metrics\"\n  Cane::RakeTask.new(:quality) do |cane|\n    cane.abc_max = 10\n    cane.add_threshold 'coverage/covered_percent', :\u003e=, 99\n    cane.no_style = true\n    cane.abc_exclude = %w(Foo::Bar#some_method)\n  end\n\n  task :default =\u003e :quality\nrescue LoadError\n  warn \"cane not available, quality task not provided.\"\nend\n```\n\nLoading options from a `.cane` file is supported by setting `canefile=` to the\nfile name.\n\nRescuing `LoadError` is a good idea, since `rake -T` failing is totally\nfrustrating.\n\n## Adding to a legacy project\n\nCane can be configured to still pass in the presence of a set number of\nviolations using the `--max-violations` option. This is ideal for retrofitting\non to an existing application that may already have many violations. By setting\nthe maximum to the current number, no immediate changes will be required to\nyour existing code base, but you will be protected from things getting worse.\n\nYou may also consider beginning with high thresholds and ratcheting them down\nover time, or defining exclusions for specific troublesome violations (not\nrecommended).\n\n## Integrating with SimpleCov\n\nAny value in a file can be used as a threshold:\n\n    \u003e echo \"89\" \u003e coverage/.last_run.json\n    \u003e cane --gte 'coverage/.last_run.json,90'\n\n    Quality threshold crossed\n\n      coverage/covered_percent is 89, should be \u003e= 90\n\n## Implementing your own checks\n\nChecks must implement:\n\n* A class level `options` method that returns a hash of available options. This\n  will be included in help output if the check is added before `--help`. If\n  your check does not require any configuration, return an empty hash.\n* A one argument constructor, into which will be passed the options specified\n  for your check.\n* A `violations` method that returns an array of violations.\n\nSee existing checks for guidance. Create your check in a new file:\n\n```ruby\n# unhappy.rb\nclass UnhappyCheck \u003c Struct.new(:opts)\n  def self.options\n    {\n      unhappy_file: [\"File to check\", default: [nil]]\n    }\n  end\n\n  def violations\n    [\n      description: \"Files are unhappy\",\n      file:        opts.fetch(:unhappy_file),\n      label:       \":(\"\n    ]\n  end\nend\n```\n\nInclude your check either using command-line options:\n\n    cane -r unhappy.rb --check UnhappyCheck --unhappy-file myfile\n\nOr in your rake task:\n\n```ruby\nrequire 'unhappy'\n\nCane::RakeTask.new(:quality) do |c|\n  c.use UnhappyCheck, unhappy_file: 'myfile'\nend\n```\n\n## Protips\n\n### Writing class level documentation\n\nClasses are commonly the first entry point into a code base, often for an\noncall engineer responding to an exception, so provide enough information to\norient first-time readers.\n\nA good class level comment should answer the following:\n\n* Why does this class exist?\n* How does it fit in to the larger system?\n* Explanation of any domain-specific terms.\n\nIf you have specific documentation elsewhere (say, in the README or a wiki), a\nlink to that suffices.\n\nIf the class is a known entry point, such as a regular background job that can\npotentially fail, then also provide enough context that it can be efficiently\ndealt with. In the background job case:\n\n* Should it be retried?\n* What if it failed 5 days ago and we're only looking at it now?\n* Who cares that this job failed?\n\n### Writing a readme\n\nA good README should include at a minimum:\n\n* Why the project exists.\n* How to get started with development.\n* How to deploy the project (if applicable).\n* Status of the project (spike, active development, stable in production).\n* Compatibility notes (1.8, 1.9, JRuby).\n* Any interesting technical or architectural decisions made on the project\n  (this could be as simple as a link to an external design document).\n\n## Compatibility\n\nRequires CRuby 2.0, since it depends on the `ripper` library to calculate\ncomplexity metrics. This only applies to the Ruby used to run Cane, not the\nproject it is being run against. In other words, you can run Cane against your\n1.8 or JRuby project.\n\n## Support\n\nMake a [new github issue](https://github.com/square/cane/issues/new).\n\n## Contributing\n\nFork and patch! Before any changes are merged to master, we need you to sign an\n[Individual Contributor\nAgreement](https://spreadsheets.google.com/a/squareup.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ\u0026ndplr=1)\n(Google Form).\n","funding_links":[],"categories":["Ruby","Code Analysis and Metrics"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquare%2Fcane","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsquare%2Fcane","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquare%2Fcane/lists"}