├── .gitignore ├── .poper.sample.yml ├── .rubocop.yml ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin └── poper ├── lib ├── poper.rb └── poper │ ├── cli.rb │ ├── config.rb │ ├── config_file.rb │ ├── rule │ ├── capitalization.rb │ ├── character_limit.rb │ ├── generic.rb │ ├── rule.rb │ ├── single_word.rb │ └── summary_character_limit.rb │ ├── runner.rb │ └── version.rb ├── poper.gemspec ├── poper.gif └── spec ├── poper ├── config_file_spec.rb ├── config_spec.rb ├── rule │ ├── capitalization_spec.rb │ ├── character_limit_spec.rb │ ├── generic_spec.rb │ ├── single_word_spec.rb │ └── summary_character_limit_spec.rb └── runner_spec.rb ├── spec_helper.rb ├── specrepo.git ├── HEAD ├── config ├── description ├── index ├── info │ └── exclude ├── logs │ ├── HEAD │ └── refs │ │ └── heads │ │ └── master ├── objects │ ├── 35 │ │ └── 7c6fa883bb18682770b14168708df1531c958f │ ├── 73 │ │ └── 0e6eec4eacc6572b578f94342971e8b518e8d7 │ ├── 85 │ │ └── ae779d5da760fb519309eefdbe59bf67240c4d │ ├── 91 │ │ └── f80a89a5e00803427e63ac6004ab2e7e76ab5f │ ├── 3a │ │ └── d1e5ae3f3d073106e31a7056494ca6c0b4e9f9 │ ├── a7 │ │ └── 2832b303c4d4f1833da79fc8a566e8a0eb37af │ └── d0 │ │ └── fa2b87c6dd106c3bdc00e02aef39b860d739dd └── refs │ └── heads │ └── master └── support └── coverage.rb /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | *.gem 3 | .bundle 4 | .DS_Store 5 | Gemfile.lock 6 | coverage 7 | .poper.yml -------------------------------------------------------------------------------- /.poper.sample.yml: -------------------------------------------------------------------------------- 1 | # These are the defaults. 2 | # Run `cp .poper.sample.yml .poper.yml`, 3 | # then uncomment and edit whichever rules you want to employ. 4 | disallow_single_word: 5 | enabled: true 6 | 7 | character_limit: 8 | enabled: true 9 | number: 72 10 | 11 | summary_character_limit: 12 | enabled: true 13 | number: 50 14 | 15 | disallow_generic: 16 | enabled: true 17 | words: 18 | - fix 19 | - fixed 20 | - fixes 21 | - oops 22 | - todo 23 | - fixme 24 | - commit 25 | - changes 26 | - hm 27 | - hmm 28 | - hmmm 29 | - test 30 | - tests 31 | - quickfix 32 | 33 | enforce_capitalized: 34 | enabled: true 35 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Documentation: 2 | Enabled: false 3 | 4 | SignalException: 5 | EnforcedStyle: only_raise 6 | 7 | Style/TrailingCommaInArguments: 8 | Enabled: false 9 | 10 | Style/MultilineOperationIndentation: 11 | EnforcedStyle: indented 12 | 13 | Style/PredicateName: 14 | Exclude: 15 | - 'lib/poper/cli.rb' 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | before_install: gem update --system 3 | dist: trusty 4 | language: ruby 5 | rvm: 6 | - 2.3 7 | - 2.4 8 | - 2.5 9 | - 2.6 10 | 11 | after_success: 12 | - bundle exec codeclimate-test-reporter 13 | 14 | addons: 15 | code_climate: 16 | repo_token: 46beca17cd9d4c01400e1401b2247c21a296fd7d92a4460341ec71fc0ebebafe 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Mindaugas Mozūras 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Poper 2 | 3 | [![Build Status](https://travis-ci.org/mmozuras/poper.png)](https://travis-ci.org/mmozuras/poper) 4 | +[![Coverage Status](https://img.shields.io/codeclimate/coverage/github/mmozuras/poper.svg)](https://codeclimate.com/github/mmozuras/poper) 5 | +[![Code Climate](https://codeclimate.com/github/mmozuras/poper.svg)](https://codeclimate.com/github/mmozuras/poper) 6 | [![Gem Version](https://badge.fury.io/rb/poper.png)](http://badge.fury.io/rb/poper) 7 | [![Dependency Status](https://gemnasium.com/mmozuras/poper.png)](https://gemnasium.com/mmozuras/poper) 8 | 9 | Poper makes sure that your git commit messages are well-formed. It's partly 10 | inspired by [this article][] written by [tpope][]. Rules specified there form 11 | the basis of [Poper rules][]. But Poper doesn't stop there. It also doesn't 12 | like generic commit messages like 'oops, fix tests'. Poper was created to be 13 | used by [Pronto][], but will work perfectly well in whatever scenario you'll 14 | come up for it! 15 | 16 | ## Usage 17 | 18 | ![Poper demo](poper.gif "") 19 | 20 | Install Poper like any other gem and then run it from your terminal, specifying 21 | a commit: 22 | 23 | ```bash 24 | gem install poper 25 | cd /repo/which/commits/you/want/to/check 26 | poper run HEAD~3 27 | ``` 28 | 29 | Every commit between current HEAD and specified commit will be checked. 30 | 31 | [this article]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 32 | [tpope]: https://twitter.com/tpope 33 | [Poper rules]: https://github.com/mmozuras/poper/tree/master/lib/poper/rule 34 | [Pronto]: https://github.com/mmozuras/pronto 35 | 36 | ## Configuration 37 | 38 | The behavior of Poper can be controlled via the `.poper.yml` configuration 39 | file. It must be placed in your project directory. A sample file, `.poper.sample.yml`, is included for easy setup. 40 | 41 | The file has the following format: 42 | 43 | ```yaml 44 | disallow_single_word: 45 | enabled: true 46 | 47 | character_limit: 48 | enabled: true 49 | number: 72 50 | 51 | summary_character_limit: 52 | enabled: true 53 | number: 50 54 | 55 | disallow_generic: 56 | enabled: true 57 | words: 58 | - fix 59 | - fixed 60 | - fixes 61 | - oops 62 | - todo 63 | - fixme 64 | - commit 65 | - changes 66 | - hm 67 | - hmm 68 | - hmmm 69 | - test 70 | - tests 71 | - quickfix 72 | 73 | enforce_capitalized: 74 | enabled: true 75 | ``` 76 | 77 | All properties that can be specified via `.poper.yml`, can also be specified 78 | via environment variables. Their names will be the upcased path to the property. 79 | For example: `POPER_ENFORCE_CAPITALIZED_ENABLED` or `POPER_DISALLOW_GENERIC_WORDS`. (In the case of the latter, since environment variables don't support arrays, use a comma-separated list of words and poper will parse them appropriately.) Environment variables 80 | will always take precedence over values in configuration file. 81 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require 'bundler' 3 | require 'rspec/core/rake_task' 4 | 5 | Bundler::GemHelper.install_tasks 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task(:default).clear 9 | task default: [:spec] 10 | -------------------------------------------------------------------------------- /bin/poper: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'poper' 4 | require 'poper/cli' 5 | 6 | Poper::CLI.start 7 | -------------------------------------------------------------------------------- /lib/poper.rb: -------------------------------------------------------------------------------- 1 | require 'rugged' 2 | require 'yaml' 3 | require 'poper/runner' 4 | require 'poper/rule/rule' 5 | require 'poper/rule/capitalization' 6 | require 'poper/rule/single_word' 7 | require 'poper/rule/summary_character_limit' 8 | require 'poper/rule/character_limit' 9 | require 'poper/rule/generic' 10 | 11 | require 'poper/config_file' 12 | require 'poper/config' 13 | -------------------------------------------------------------------------------- /lib/poper/cli.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | 3 | module Poper 4 | class CLI < Thor 5 | require 'poper' 6 | require 'poper/version' 7 | 8 | class << self 9 | def is_thor_reserved_word?(word, type) 10 | return false if word == 'run' 11 | 12 | super 13 | end 14 | end 15 | 16 | desc 'run COMMIT', 'Run Poper, checking commits from HEAD to it' 17 | 18 | def run(commit) 19 | Runner.new(commit).run.each do |message| 20 | # message.commit and message.message are Strings 21 | # prints first 7 characteres of the commit sha1 hash 22 | # followed by the associated message 23 | puts "#{message.commit[0..6]}: #{message.message}" 24 | end 25 | end 26 | 27 | desc 'version', 'Show the Poper version' 28 | map %w[-v --version] => :version 29 | 30 | def version 31 | puts "Poper version #{::Poper::VERSION}" 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/poper/config.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | class Config 3 | def initialize(config_hash = ConfigFile.new.to_h) 4 | @config_hash = config_hash 5 | end 6 | 7 | %w[ 8 | disallow_single_word 9 | character_limit 10 | summary_character_limit 11 | disallow_generic 12 | enforce_capitalized 13 | ].each do |rule| 14 | ConfigFile::EMPTY[rule].each do |key, _| 15 | name = "#{rule}_#{key}" 16 | define_method(name) { ENV["POPER_#{name.upcase}"] || @config_hash[rule][key] } 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/poper/config_file.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | class ConfigFile 3 | EMPTY = { 4 | 'disallow_single_word' => { 5 | 'enabled' => true 6 | }, 7 | 'character_limit' => { 8 | 'enabled' => true, 9 | 'number' => 72 10 | }, 11 | 'summary_character_limit' => { 12 | 'enabled' => true, 13 | 'number' => 50 14 | }, 15 | 'disallow_generic' => { 16 | 'enabled' => true, 17 | 'words' => %w[fix fixed fixes oops todo fixme commit changes hm hmm hmmm 18 | test tests quickfix] 19 | }, 20 | 'enforce_capitalized' => { 21 | 'enabled' => true 22 | } 23 | }.freeze 24 | 25 | def initialize(path = '.poper.yml') 26 | @path = path 27 | end 28 | 29 | def to_h 30 | hash = File.exist?(@path) ? YAML.load_file(@path) : {} 31 | deep_merge(hash) 32 | end 33 | 34 | private 35 | 36 | def deep_merge(hash) 37 | merger = proc do |_, oldval, newval| 38 | if oldval.is_a?(Hash) && newval.is_a?(Hash) 39 | oldval.merge(newval, &merger) 40 | else 41 | oldval.nil? ? newval : oldval 42 | end 43 | end 44 | 45 | hash.merge(EMPTY, &merger) 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/poper/rule/capitalization.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | module Rule 3 | class Capitalization < Rule 4 | def check(message) 5 | error_message unless message[0] == message[0].capitalize 6 | end 7 | 8 | def enabled? 9 | @config.enforce_capitalized_enabled.to_s == 'true' 10 | end 11 | 12 | private 13 | 14 | def error_message 15 | 'Git commit message should start with a capital letter' 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/poper/rule/character_limit.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | module Rule 3 | class CharacterLimit < Rule 4 | def check(message) 5 | error_message if message.lines.any? do |line| 6 | line.chomp.length > character_limit 7 | end 8 | end 9 | 10 | def enabled? 11 | @config.character_limit_enabled.to_s == 'true' 12 | end 13 | 14 | private 15 | 16 | def character_limit 17 | @config.character_limit_number.to_i 18 | end 19 | 20 | def error_message 21 | "Every line of git commit message should be #{character_limit} chars or less" 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/poper/rule/generic.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | module Rule 3 | class Generic < Rule 4 | def check(message) 5 | words = message.scan(/[\w-]+/).compact 6 | error_message if words.all? { |word| generic?(word) } 7 | end 8 | 9 | def enabled? 10 | @config.disallow_generic_enabled.to_s == 'true' 11 | end 12 | 13 | private 14 | 15 | def generic?(word) 16 | disallowed_words.include?(word.downcase) 17 | end 18 | 19 | def disallowed_words 20 | if @config.disallow_generic_words.is_a? Array 21 | @config.disallow_generic_words 22 | else 23 | @config.disallow_generic_words.split(',').map(&:strip) 24 | end 25 | end 26 | 27 | def error_message 28 | 'Consider writing a more detailed, not as generic, commit message' 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/poper/rule/rule.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | module Rule 3 | class Rule 4 | @all = [] 5 | 6 | def self.all 7 | @all.clone 8 | end 9 | 10 | def self.inherited(subclass) 11 | @all << subclass 12 | end 13 | 14 | def initialize(config = Config.new) 15 | @config = config 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/poper/rule/single_word.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | module Rule 3 | class SingleWord < Rule 4 | def check(message) 5 | error_message if message.split.count < 2 6 | end 7 | 8 | def enabled? 9 | @config.disallow_single_word_enabled.to_s == 'true' 10 | end 11 | 12 | private 13 | 14 | def error_message 15 | 'Git commit message should consist of more than a single word' 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/poper/rule/summary_character_limit.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | module Rule 3 | class SummaryCharacterLimit < Rule 4 | def check(message) 5 | error_message if message.lines.first.chomp.length > character_limit 6 | end 7 | 8 | def enabled? 9 | @config.summary_character_limit_enabled.to_s == 'true' 10 | end 11 | 12 | private 13 | 14 | def character_limit 15 | @config.summary_character_limit_number.to_i 16 | end 17 | 18 | def error_message 19 | "Git commit message summary (first line) should be #{character_limit} chars or less" 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/poper/runner.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | 3 | module Poper 4 | class Runner 5 | def initialize(commit, repo_path = '.') 6 | @repo = Rugged::Repository.new(repo_path) 7 | oid = @repo.rev_parse_oid(commit) 8 | @commit = @repo.lookup(oid) 9 | end 10 | 11 | def run 12 | commits.flat_map { |c| check(c) }.compact 13 | end 14 | 15 | private 16 | 17 | def check(commit) 18 | rules.map do |rule| 19 | result = rule.check(commit.message) 20 | OpenStruct.new(commit: commit.oid, message: result) if result 21 | end 22 | end 23 | 24 | def rules 25 | Rule::Rule.all.map do |rule_klass| 26 | rule = rule_klass.new 27 | 28 | rule if rule.enabled? 29 | end.compact 30 | end 31 | 32 | def commits 33 | @commits ||= begin 34 | walker.reset 35 | walker.push(@repo.last_commit) 36 | walker.take_while { |c| c.oid != @commit.oid } << @commit 37 | end 38 | end 39 | 40 | def walker 41 | @walker ||= Rugged::Walker.new(@repo) 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/poper/version.rb: -------------------------------------------------------------------------------- 1 | module Poper 2 | VERSION = '0.2.3'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /poper.gemspec: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.push File.expand_path('lib', __dir__) 2 | 3 | require 'poper/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'poper' 7 | s.version = Poper::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.author = 'Mindaugas Mozūras' 10 | s.email = 'mindaugas.mozuras@gmail.com' 11 | s.homepage = 'https://github.com/mmozuras/poper' 12 | s.summary = 'Poper makes sure that your git commit messages are well-formed' 13 | 14 | s.required_rubygems_version = '>= 1.3.6' 15 | s.required_ruby_version = '>= 2.3.0' 16 | s.license = 'MIT' 17 | 18 | s.files = Dir.glob('{lib}/**/*') + %w[LICENSE README.md] 19 | s.test_files = `git ls-files -- {spec}/*`.split("\n") 20 | s.require_paths = ['lib'] 21 | s.executables << 'poper' 22 | 23 | s.add_runtime_dependency('rugged', '~> 0.23', '>= 0.23.0') 24 | s.add_runtime_dependency('thor', '~> 0.20.0') 25 | s.add_development_dependency('bundler', '>= 1.10') 26 | s.add_development_dependency('codeclimate-test-reporter', '~> 1.0') 27 | s.add_development_dependency('pry', '~> 0.10') 28 | s.add_development_dependency('rake', '~> 12.0') 29 | s.add_development_dependency('rspec', '~> 3.4') 30 | s.add_development_dependency('rspec-its', '~> 1.2') 31 | s.add_development_dependency('simplecov', '~> 0.14') 32 | end 33 | -------------------------------------------------------------------------------- /poper.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/poper.gif -------------------------------------------------------------------------------- /spec/poper/config_file_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'yaml' 3 | 4 | module Poper 5 | describe ConfigFile do 6 | let(:config_file) { described_class.new } 7 | 8 | describe '#to_h' do 9 | subject { config_file.to_h } 10 | 11 | context 'not existing config file' do 12 | it do 13 | should include( 14 | 'disallow_single_word' => { 15 | 'enabled' => true 16 | } 17 | ) 18 | end 19 | 20 | it do 21 | should include( 22 | 'character_limit' => { 23 | 'enabled' => true, 24 | 'number' => 72 25 | } 26 | ) 27 | end 28 | 29 | it do 30 | should include( 31 | 'summary_character_limit' => { 32 | 'enabled' => true, 33 | 'number' => 50 34 | } 35 | ) 36 | end 37 | 38 | it do 39 | should include( 40 | 'disallow_generic' => { 41 | 'enabled' => true, 42 | 'words' => %w[fix fixed fixes oops todo fixme commit changes hm hmm hmmm test tests quickfix] 43 | } 44 | ) 45 | end 46 | 47 | it do 48 | should include( 49 | 'enforce_capitalized' => { 50 | 'enabled' => true 51 | } 52 | ) 53 | end 54 | end 55 | 56 | context 'only summary character limit in file' do 57 | before do 58 | File.should_receive(:exist?) 59 | .and_return(true) 60 | 61 | YAML.should_receive(:load_file) 62 | .and_return('summary_character_limit' => { 63 | 'number' => 95 64 | }) 65 | end 66 | 67 | it do 68 | should include( 69 | 'summary_character_limit' => { 70 | 'enabled' => true, 71 | 'number' => 95 72 | } 73 | ) 74 | end 75 | end 76 | context 'a value is set to false' do 77 | before do 78 | File.should_receive(:exist?) 79 | .and_return(true) 80 | 81 | YAML.should_receive(:load_file) 82 | .and_return('summary_character_limit' => { 83 | 'enabled' => false, 84 | 'number' => 95 85 | }) 86 | end 87 | 88 | it do 89 | should include( 90 | 'summary_character_limit' => { 91 | 'enabled' => false, 92 | 'number' => 95 93 | } 94 | ) 95 | end 96 | end 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /spec/poper/config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | describe Config do 5 | let(:config) { described_class.new(config_hash) } 6 | let(:config_hash) { {} } 7 | 8 | describe '#disallow_generic_enabled' do 9 | subject { config.disallow_generic_enabled } 10 | 11 | context 'from env variable' do 12 | before { stub_const('ENV', 'POPER_DISALLOW_GENERIC_ENABLED' => 'false') } 13 | it { should == 'false' } 14 | end 15 | 16 | context 'from config hash' do 17 | let(:config_hash) { { 'disallow_generic' => { 'enabled' => false } } } 18 | it { should == false } 19 | end 20 | 21 | context 'default' do 22 | let(:config_hash) { ConfigFile::EMPTY } 23 | it { should == true } 24 | end 25 | end 26 | 27 | describe '#disallow_generic_words' do 28 | subject { config.disallow_generic_words } 29 | 30 | context 'from env variable' do 31 | before { stub_const('ENV', 'POPER_DISALLOW_GENERIC_WORDS' => 'in, envar,words,should, be, comma, separated') } 32 | it { should == 'in, envar,words,should, be, comma, separated' } 33 | end 34 | 35 | context 'from config hash' do 36 | let(:config_hash) { { 'disallow_generic' => { 'words' => %w[in config words should be array] } } } 37 | it { should == %w[in config words should be array] } 38 | end 39 | 40 | context 'default' do 41 | let(:config_hash) { ConfigFile::EMPTY } 42 | it { should == %w[fix fixed fixes oops todo fixme commit changes hm hmm hmmm test tests quickfix] } 43 | end 44 | end 45 | 46 | describe '#character_limit_enabled' do 47 | subject { config.character_limit_enabled } 48 | 49 | context 'from env variable' do 50 | before { stub_const('ENV', 'POPER_CHARACTER_LIMIT_ENABLED' => 'false') } 51 | it { should == 'false' } 52 | end 53 | 54 | context 'from config hash' do 55 | let(:config_hash) { { 'character_limit' => { 'enabled' => false } } } 56 | it { should == false } 57 | end 58 | 59 | context 'default' do 60 | let(:config_hash) { ConfigFile::EMPTY } 61 | it { should == true } 62 | end 63 | end 64 | 65 | describe '#character_limit_number' do 66 | subject { config.character_limit_number } 67 | 68 | context 'from env variable' do 69 | before { stub_const('ENV', 'POPER_CHARACTER_LIMIT_NUMBER' => 100) } 70 | it { should == 100 } 71 | end 72 | 73 | context 'from config hash' do 74 | let(:config_hash) { { 'character_limit' => { 'number' => 100 } } } 75 | it { should == 100 } 76 | end 77 | 78 | context 'default' do 79 | let(:config_hash) { ConfigFile::EMPTY } 80 | it { should == 72 } 81 | end 82 | end 83 | 84 | describe '#summary_character_limit_enabled' do 85 | subject { config.summary_character_limit_enabled } 86 | 87 | context 'from env variable' do 88 | before { stub_const('ENV', 'POPER_SUMMARY_CHARACTER_LIMIT_ENABLED' => 'false') } 89 | it { should == 'false' } 90 | end 91 | 92 | context 'from config hash' do 93 | let(:config_hash) { { 'summary_character_limit' => { 'enabled' => false } } } 94 | it { should == false } 95 | end 96 | 97 | context 'default' do 98 | let(:config_hash) { ConfigFile::EMPTY } 99 | it { should == true } 100 | end 101 | end 102 | 103 | describe '#summary_character_limit_number' do 104 | subject { config.summary_character_limit_number } 105 | 106 | context 'from env variable' do 107 | before { stub_const('ENV', 'POPER_SUMMARY_CHARACTER_LIMIT_NUMBER' => 80) } 108 | it { should == 80 } 109 | end 110 | 111 | context 'from config hash' do 112 | let(:config_hash) { { 'summary_character_limit' => { 'number' => 80 } } } 113 | it { should == 80 } 114 | end 115 | 116 | context 'default' do 117 | let(:config_hash) { ConfigFile::EMPTY } 118 | it { should == 50 } 119 | end 120 | end 121 | 122 | describe '#disallow_single_word_enabled' do 123 | subject { config.disallow_single_word_enabled } 124 | 125 | context 'from env variable' do 126 | before { stub_const('ENV', 'POPER_DISALLOW_SINGLE_WORD_ENABLED' => 'false') } 127 | it { should == 'false' } 128 | end 129 | 130 | context 'from config hash' do 131 | let(:config_hash) { { 'disallow_single_word' => { 'enabled' => false } } } 132 | it { should == false } 133 | end 134 | 135 | context 'default' do 136 | let(:config_hash) { ConfigFile::EMPTY } 137 | it { should == true } 138 | end 139 | end 140 | 141 | describe '#enforce_capitalized_enabled' do 142 | subject { config.enforce_capitalized_enabled } 143 | 144 | context 'from env variable' do 145 | before { stub_const('ENV', 'POPER_ENFORCE_CAPITALIZED_ENABLED' => 'false') } 146 | it { should == 'false' } 147 | end 148 | 149 | context 'from config hash' do 150 | let(:config_hash) { { 'enforce_capitalized' => { 'enabled' => false } } } 151 | it { should == false } 152 | end 153 | 154 | context 'default' do 155 | let(:config_hash) { ConfigFile::EMPTY } 156 | it { should == true } 157 | end 158 | end 159 | end 160 | end 161 | -------------------------------------------------------------------------------- /spec/poper/rule/capitalization_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | module Rule 5 | describe Capitalization do 6 | describe '#check' do 7 | let(:rule) { Capitalization.new } 8 | subject { rule.check(message) } 9 | 10 | context 'capitalized message' do 11 | let(:message) { 'Implement that feature' } 12 | it { should be_nil } 13 | end 14 | 15 | context 'non-capitalized message' do 16 | let(:message) { 'implement that feature' } 17 | it { should_not be_nil } 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/poper/rule/character_limit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | module Rule 5 | describe CharacterLimit do 6 | describe '#check' do 7 | let(:rule) { CharacterLimit.new } 8 | subject { rule.check(message) } 9 | 10 | context 'seventy-one char line' do 11 | let(:message) do 12 | 'Implement that feature - really really really well 13 | 14 | Write your commit message in the imperative: "Fix bug", not "Fixed bug"' 15 | end 16 | 17 | it { should be_nil } 18 | end 19 | 20 | context 'seventy-two char line' do 21 | let(:message) do 22 | 'Implement that feature - really really really well 23 | 24 | Write your commit message in the imperative: "Fix bug", not "Fixed bug"! 25 | More text here!' 26 | end 27 | 28 | it { should be_nil } 29 | end 30 | 31 | context 'seventy-two char last line' do 32 | let(:message) do 33 | 'Implement that feature - really really really well 34 | 35 | Write your commit message in the imperative: "Fix bug", not "Fixed bug"!' 36 | end 37 | 38 | it { should be_nil } 39 | end 40 | 41 | context 'seventy-three char line' do 42 | let(:message) do 43 | 'Implement that feature - really really really well 44 | 45 | Write your commit message in the imperative: "Fix bugs", not "Fixed bugs" 46 | More text here!' 47 | end 48 | 49 | it { should_not be_nil } 50 | end 51 | 52 | context 'seventy-three char last line' do 53 | let(:message) do 54 | 'Implement that feature - really really really well 55 | 56 | Write your commit message in the imperative: "Fix bugs", not "Fixed bugs"' 57 | end 58 | 59 | it { should_not be_nil } 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/poper/rule/generic_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | module Rule 5 | describe Generic do 6 | describe '#check' do 7 | let(:rule) { Generic.new } 8 | subject { rule.check(message) } 9 | 10 | context 'non-banned commit message' do 11 | let(:message) { 'Implement that feature' } 12 | it { should be_nil } 13 | end 14 | 15 | context 'banned commit messages' do 16 | context ' fix' do 17 | let(:message) { ' fix' } 18 | it { should_not be_nil } 19 | end 20 | 21 | context 'Oops ' do 22 | let(:message) { ' Oops' } 23 | it { should_not be_nil } 24 | end 25 | 26 | context ' ChaNges ' do 27 | let(:message) { ' ChaNges' } 28 | it { should_not be_nil } 29 | end 30 | 31 | context 'fix tests' do 32 | let(:message) { 'fix tests' } 33 | it { should_not be_nil } 34 | end 35 | 36 | context 'oops fixes' do 37 | let(:message) { "oops\n\nfixes" } 38 | it { should_not be_nil } 39 | end 40 | 41 | context 'oops, fix tests' do 42 | let(:message) { 'oops, fix tests' } 43 | it { should_not be_nil } 44 | end 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/poper/rule/single_word_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | module Rule 5 | describe SingleWord do 6 | describe '#check' do 7 | let(:rule) { SingleWord.new } 8 | subject { rule.check(message) } 9 | 10 | context 'zero word message' do 11 | let(:message) { ' ' } 12 | it { should_not be_nil } 13 | end 14 | 15 | context 'single word message' do 16 | let(:message) { 'Fix' } 17 | it { should_not be_nil } 18 | end 19 | 20 | context 'multiple word message' do 21 | let(:message) { 'Implement that feature' } 22 | it { should be_nil } 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/poper/rule/summary_character_limit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | module Rule 5 | describe SummaryCharacterLimit do 6 | describe '#check' do 7 | let(:rule) { SummaryCharacterLimit.new } 8 | subject { rule.check(msg) } 9 | 10 | context 'fifty char summary' do 11 | let(:msg) { 'Implement that feature - really really really well' } 12 | it { should be_nil } 13 | end 14 | 15 | context 'fifty-one char summary' do 16 | let(:msg) { 'Implement that feature - really really really well.' } 17 | it { should_not be_nil } 18 | end 19 | 20 | context 'fifty char summary with a newline' do 21 | let(:msg) { "Implement that feature - really really really well\n" } 22 | it { should be_nil } 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/poper/runner_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Poper 4 | describe Runner do 5 | describe '#run' do 6 | let(:runner) { Runner.new(commit, 'spec/specrepo.git') } 7 | subject { runner.run } 8 | 9 | context 'first commit' do 10 | let(:commit) { '85ae779' } 11 | 12 | its(:count) { should == 3 } 13 | its(:'first.commit') do 14 | should == '357c6fa883bb18682770b14168708df1531c958f' 15 | end 16 | its(:'first.message') do 17 | should == 'Git commit message should start with a capital letter' 18 | end 19 | its(:'last.commit') do 20 | should == '85ae779d5da760fb519309eefdbe59bf67240c4d' 21 | end 22 | its(:'last.message') do 23 | should == 24 | 'Consider writing a more detailed, not as generic, commit message' 25 | end 26 | end 27 | 28 | context 'head commit' do 29 | let(:commit) { 'd0fa2b8' } 30 | its(:count) { should == 0 } 31 | end 32 | 33 | context 'may reach to HEAD~2' do 34 | let(:commit) { 'HEAD~2' } 35 | 36 | its(:'last.commit') do 37 | should == '85ae779d5da760fb519309eefdbe59bf67240c4d' 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'support/coverage' 2 | 3 | require 'rspec' 4 | require 'rspec/its' 5 | 6 | require 'poper' 7 | 8 | RSpec.configure do |config| 9 | config.order = :random 10 | Kernel.srand config.seed 11 | 12 | config.expect_with(:rspec) { |c| c.syntax = :should } 13 | config.mock_with(:rspec) { |c| c.syntax = :should } 14 | end 15 | -------------------------------------------------------------------------------- /spec/specrepo.git/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /spec/specrepo.git/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | ignorecase = true 7 | precomposeunicode = false 8 | -------------------------------------------------------------------------------- /spec/specrepo.git/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /spec/specrepo.git/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/index -------------------------------------------------------------------------------- /spec/specrepo.git/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /spec/specrepo.git/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 85ae779d5da760fb519309eefdbe59bf67240c4d Mindaugas Mozūras 1381772859 +0300 commit (initial): Fix 2 | 85ae779d5da760fb519309eefdbe59bf67240c4d 357c6fa883bb18682770b14168708df1531c958f Mindaugas Mozūras 1381772871 +0300 commit: something something Danger Zone 3 | 357c6fa883bb18682770b14168708df1531c958f d0fa2b87c6dd106c3bdc00e02aef39b860d739dd Mindaugas Mozūras 1381772894 +0300 commit: Remove unnecessary line from README 4 | -------------------------------------------------------------------------------- /spec/specrepo.git/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 85ae779d5da760fb519309eefdbe59bf67240c4d Mindaugas Mozūras 1381772859 +0300 commit (initial): Fix 2 | 85ae779d5da760fb519309eefdbe59bf67240c4d 357c6fa883bb18682770b14168708df1531c958f Mindaugas Mozūras 1381772871 +0300 commit: something something Danger Zone 3 | 357c6fa883bb18682770b14168708df1531c958f d0fa2b87c6dd106c3bdc00e02aef39b860d739dd Mindaugas Mozūras 1381772894 +0300 commit: Remove unnecessary line from README 4 | -------------------------------------------------------------------------------- /spec/specrepo.git/objects/35/7c6fa883bb18682770b14168708df1531c958f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/35/7c6fa883bb18682770b14168708df1531c958f -------------------------------------------------------------------------------- /spec/specrepo.git/objects/3a/d1e5ae3f3d073106e31a7056494ca6c0b4e9f9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/3a/d1e5ae3f3d073106e31a7056494ca6c0b4e9f9 -------------------------------------------------------------------------------- /spec/specrepo.git/objects/73/0e6eec4eacc6572b578f94342971e8b518e8d7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/73/0e6eec4eacc6572b578f94342971e8b518e8d7 -------------------------------------------------------------------------------- /spec/specrepo.git/objects/85/ae779d5da760fb519309eefdbe59bf67240c4d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/85/ae779d5da760fb519309eefdbe59bf67240c4d -------------------------------------------------------------------------------- /spec/specrepo.git/objects/91/f80a89a5e00803427e63ac6004ab2e7e76ab5f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/91/f80a89a5e00803427e63ac6004ab2e7e76ab5f -------------------------------------------------------------------------------- /spec/specrepo.git/objects/a7/2832b303c4d4f1833da79fc8a566e8a0eb37af: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/a7/2832b303c4d4f1833da79fc8a566e8a0eb37af -------------------------------------------------------------------------------- /spec/specrepo.git/objects/d0/fa2b87c6dd106c3bdc00e02aef39b860d739dd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/poper/6d8e2db50468247d14c6b50fa9739c56c3867548/spec/specrepo.git/objects/d0/fa2b87c6dd106c3bdc00e02aef39b860d739dd -------------------------------------------------------------------------------- /spec/specrepo.git/refs/heads/master: -------------------------------------------------------------------------------- 1 | d0fa2b87c6dd106c3bdc00e02aef39b860d739dd 2 | -------------------------------------------------------------------------------- /spec/support/coverage.rb: -------------------------------------------------------------------------------- 1 | if ENV['COVERAGE'] 2 | require 'simplecov' 3 | 4 | SimpleCov.command_name "rspec_#{Process.pid}" 5 | SimpleCov.start do 6 | add_filter '/spec/' 7 | end 8 | end 9 | --------------------------------------------------------------------------------