git-semver-cop 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5d93939abae0b642d056eddda577cb8b1bb91b6d
4
+ data.tar.gz: e9946821a38aab84976d21d6568915b052532360
5
+ SHA512:
6
+ metadata.gz: 66ddb5aed3bdfe0b788d1ea63987ff1a20ee64a097154a724fdde494f544716c1cec93220addd3fe9b2fedf4a88162be457b597ce0135bd13f3b1d9a0a57e5a1
7
+ data.tar.gz: bab402eab470a92b94cf47da8b55b87b183df81f588fd4b58adc76ea4c7ba6f080d782e5af1885e38355b00268a361ffaff0c7a98c96740fdd69244759644925
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Bernardo Farah
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,42 @@
1
+ # GitSemverCop
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'git-semver-cop'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install git-semver-cop
18
+
19
+ ## Usage
20
+
21
+ ```
22
+ git semver-cop init
23
+ # makes a pre-commit hook that enforces semantic versioning
24
+
25
+ git semver-cop destroy
26
+ # removes said hook
27
+ ```
28
+
29
+ You are prompted before your commit goes through:
30
+ ```
31
+ $ git add .
32
+ $ git commit
33
+ Choose a version increment for this commit:
34
+ [patch|minor|major|none]
35
+ $ none
36
+ # Nothing happened
37
+ ```
38
+
39
+ ## Contributing
40
+
41
+ Bug reports and pull requests are welcome on GitHub at https://github.com/berfarah/git-semver-cop.
42
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require "git-semver-cop/generator"
3
+ GitSemverCop::Generator.start
@@ -0,0 +1,14 @@
1
+ require "git-semver-cop/semver"
2
+ Dir[File.expand_path("../git-semver-cop/version_files/*", __FILE__)
3
+ ].each(&method(:require))
4
+
5
+ # Public method for updating all of our version files
6
+ module GitSemverCop
7
+ def self.update_version_files
8
+ GitSemverCop.constants.each do |klass_sym|
9
+ klass = GitSemverCop.const_get(klass_sym)
10
+ next unless klass < GitSemverCop::VersionFile
11
+ klass.exist? && klass.new.update
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ require "thor"
2
+
3
+ module GitSemverCop
4
+ # Our generator that creates the commit hook
5
+ class Generator < Thor
6
+ include Thor::Actions
7
+
8
+ def self.source_root
9
+ File.expand_path("../../hooks", __FILE__)
10
+ end
11
+
12
+ def self.destination
13
+ File.expand_path(".git/hooks/pre-commit", Dir.pwd)
14
+ end
15
+
16
+ desc "init", "Generates a pre-commit hook to ensure you update your .semver"
17
+ def init
18
+ copy_file "pre-commit", self.class.destination
19
+ chmod self.class.destination, 0744
20
+ end
21
+
22
+ desc "destroy", "Get rid of pre-commit hook"
23
+ def destroy
24
+ remove_file self.class.destination
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,68 @@
1
+ # This is not the best example of "Single responsibility principle", is it?
2
+ module GitSemverCop
3
+ # Class for modifying and detecting semver
4
+ class Semver
5
+ def initialize
6
+ @semver = `git status -z .semver`.strip
7
+ end
8
+
9
+ def enforce
10
+ if none? then semver_init
11
+ elsif added? # Nothing to do
12
+ elsif modified? then ask_add_changes
13
+ else ask_increment
14
+ end
15
+ end
16
+
17
+ # #
18
+ # Get user input
19
+ def ask_add_changes
20
+ puts ".semver was modified. Add the file to the commit?\n[y|n]"
21
+ add_to_git if gets.chomp =~ /^y$/i
22
+ end
23
+
24
+ def ask_increment
25
+ puts "How big of an increment is this?\n[#{inc_opts.join('|')}]"
26
+ inc = gets.chomp
27
+ inc_opts.include?(inc) ? semver_inc(inc) : exit(0)
28
+ end
29
+
30
+ # #
31
+ # Check status of semver file
32
+ def none?
33
+ !File.exist?(".semver")
34
+ end
35
+
36
+ def added?
37
+ @semver =~ /(M|A) .semver/
38
+ end
39
+
40
+ def modified?
41
+ @semver =~ /(M|A) .semver/
42
+ end
43
+
44
+ private
45
+
46
+ # #
47
+ # Valid increment options
48
+ def inc_opts
49
+ %w(patch minor major none)
50
+ end
51
+
52
+ # #
53
+ # Command line operations
54
+ def add_to_git
55
+ system("git add .semver")
56
+ end
57
+
58
+ def semver_init
59
+ puts "Creating a new .semver file"
60
+ system("semver init")
61
+ end
62
+
63
+ def semver_inc(inc)
64
+ system("semver inc #{inc}")
65
+ add_to_git
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "version_file"
2
+
3
+ module GitSemverCop
4
+ # Class for updating Gemspec
5
+ class Gemspec < VersionFile
6
+ self.path = Dir["*.gemspec"].first
7
+
8
+ private
9
+
10
+ def update_version
11
+ contents.sub!(/(\.version\s+=\s+).+$/,
12
+ '\1"' + current_version + '"')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "version_file"
2
+
3
+ module GitSemverCop
4
+ # Class for updating package.json
5
+ class PackageJson < VersionFile
6
+ self.path = "package.json"
7
+
8
+ private
9
+
10
+ def update_version
11
+ contents.sub!(/("version"\s*:\s*)(".+?")/,
12
+ '\1"' + current_version + '"')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ require "semver"
2
+
3
+ module GitSemverCop
4
+ class VersionFile
5
+ class << self
6
+ attr_accessor :path
7
+
8
+ def exist?
9
+ File.exist?(path)
10
+ end
11
+ end
12
+
13
+ def update
14
+ update_version
15
+ write_update
16
+ add_to_git
17
+ end
18
+
19
+ def path
20
+ self.class.path
21
+ end
22
+
23
+ private
24
+
25
+ def contents
26
+ @contents ||= File.read(path)
27
+ end
28
+
29
+ def current_version
30
+ SemVer.find.to_s[1..-1]
31
+ end
32
+
33
+ def write_update
34
+ puts "Changing #{path} version"
35
+ File.write(path, contents)
36
+ end
37
+
38
+ def add_to_git
39
+ system "git add #{path}"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require "git-semver-cop"
3
+
4
+ # Make sure we're not inside of .git
5
+ Dir.chdir("..") if ENV["GIT_DIR"] == "."
6
+
7
+ # Reopen STDIN for user response
8
+ $stdin.reopen(File.open("/dev/tty", "r"))
9
+
10
+ # Enforce our semver
11
+ GitSemverCop::Semver.new.enforce
12
+
13
+ # Update version files (gemspec, package.json)
14
+ GitSemverCop.update_version_files
@@ -0,0 +1,20 @@
1
+ describe GitSemverCop do
2
+ subject { GitSemverCop }
3
+ before do
4
+ @gemspec = GitSemverCop::Gemspec
5
+ @package = GitSemverCop::PackageJson
6
+ end
7
+
8
+ describe ".update_version_files" do
9
+ it "should update only existing files" do
10
+ # Our project here only has a gemspec
11
+ expect(@gemspec.exist?).to be true
12
+ expect(@package.exist?).to be false
13
+
14
+ expect_any_instance_of(@gemspec).to receive(:update)
15
+ expect_any_instance_of(@package).not_to receive(:update)
16
+
17
+ subject.update_version_files
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,66 @@
1
+ describe GitSemverCop::Semver do
2
+ subject { described_class.new }
3
+ before do
4
+ allow_any_instance_of(Kernel).to receive(:puts).and_return("")
5
+ allow_any_instance_of(Kernel).to receive(:gets).and_return("")
6
+ end
7
+
8
+ describe "#enforce" do
9
+ before do
10
+ allow(Kernel).to receive(:`).and_return("")
11
+ end
12
+
13
+ context "when .semver doesn't exist" do
14
+ it "invokes semver init" do
15
+ expect(File).to receive(:exist?).and_return(false)
16
+ expect(subject).to receive(:semver_init)
17
+ subject.enforce
18
+ end
19
+ end
20
+
21
+ context "when .semver has been updated and added" do
22
+ it "does nothing" do
23
+ expect_any_instance_of(Kernel).to receive(:`).and_return("M .semver\0")
24
+ expect(subject.enforce).to be nil
25
+ end
26
+ end
27
+
28
+ context "when .semver has been modified but not added" do
29
+ it "asks to add it" do
30
+ expect_any_instance_of(Kernel).to receive(:`).and_return("M .semver\0")
31
+ expect(subject).to receive(:ask_add_changes)
32
+ subject.enforce
33
+ end
34
+ end
35
+
36
+ context "when .semver exists but was not modified" do
37
+ it "asks to increment it" do
38
+ expect_any_instance_of(Kernel).to receive(:`).and_return("")
39
+ expect(subject).to receive(:ask_increment)
40
+ subject.enforce
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "#ask_add_changes" do
46
+ it "adds to git if given [y]" do
47
+ expect_any_instance_of(Kernel).to receive(:gets).and_return("y")
48
+ expect(subject).to receive(:add_to_git)
49
+ subject.ask_add_changes
50
+ end
51
+ end
52
+
53
+ describe "#ask_increment" do
54
+ it "goes [none] by default" do
55
+ expect_any_instance_of(Kernel).to receive(:gets).and_return("")
56
+ expect_any_instance_of(Kernel).to receive(:exit)
57
+ subject.ask_increment
58
+ end
59
+
60
+ it "passes a given patch to #semver_inc" do
61
+ expect_any_instance_of(Kernel).to receive(:gets).and_return("patch")
62
+ expect(subject).to receive(:semver_inc).with("patch")
63
+ subject.ask_increment
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,101 @@
1
+ require "simplecov"
2
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
3
+ SimpleCov.start { add_filter "_spec" }
4
+ require "git-semver-cop"
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
9
+ # this file to always be loaded, without a need to explicitly require it in any
10
+ # files.
11
+ #
12
+ # Given that it is always loaded, you are encouraged to keep this file as
13
+ # light-weight as possible. Requiring heavyweight dependencies from this file
14
+ # will add to the boot time of your test suite on EVERY test run, even for an
15
+ # individual file that may not need all of that loaded. Instead, consider making
16
+ # a separate helper file that requires the additional dependencies and performs
17
+ # the additional setup, and require it from the spec files that actually need
18
+ # it.
19
+ #
20
+ # The `.rspec` file also contains a few flags that are not defaults but that
21
+ # users commonly want.
22
+ #
23
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
24
+ RSpec.configure do |config|
25
+ # rspec-expectations config goes here. You can use an alternate
26
+ # assertion/expectation library such as wrong or the stdlib/minitest
27
+ # assertions if you prefer.
28
+ config.expect_with :rspec do |expectations|
29
+ # This option will default to `true` in RSpec 4. It makes the `description`
30
+ # and `failure_message` of custom matchers include text for helper methods
31
+ # defined using `chain`, e.g.:
32
+ # be_bigger_than(2).and_smaller_than(4).description
33
+ # # => "be bigger than 2 and smaller than 4"
34
+ # ...rather than:
35
+ # # => "be bigger than 2"
36
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
37
+ end
38
+
39
+ # rspec-mocks config goes here. You can use an alternate test double
40
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
41
+ config.mock_with :rspec do |mocks|
42
+ # Prevents you from mocking or stubbing a method that does not exist on
43
+ # a real object. This is generally recommended, and will default to
44
+ # `true` in RSpec 4.
45
+ mocks.verify_partial_doubles = true
46
+ end
47
+
48
+ # The settings below are suggested to provide a good initial experience
49
+ # with RSpec, but feel free to customize to your heart's content.
50
+ =begin
51
+ # These two settings work together to allow you to limit a spec run
52
+ # to individual examples or groups you care about by tagging them with
53
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
54
+ # get run.
55
+ config.filter_run :focus
56
+ config.run_all_when_everything_filtered = true
57
+
58
+ # Allows RSpec to persist some state between runs in order to support
59
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
60
+ # you configure your source control system to ignore this file.
61
+ config.example_status_persistence_file_path = "spec/examples.txt"
62
+
63
+ # Limits the available syntax to the non-monkey patched syntax that is
64
+ # recommended. For more details, see:
65
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
66
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
67
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
68
+ config.disable_monkey_patching!
69
+
70
+ # This setting enables warnings. It's recommended, but in some cases may
71
+ # be too noisy due to issues in dependencies.
72
+ config.warnings = true
73
+
74
+ # Many RSpec users commonly either run the entire suite or an individual
75
+ # file, and it's useful to allow more verbose output when running an
76
+ # individual spec file.
77
+ if config.files_to_run.one?
78
+ # Use the documentation formatter for detailed output,
79
+ # unless a formatter has already been configured
80
+ # (e.g. via a command-line flag).
81
+ config.default_formatter = 'doc'
82
+ end
83
+
84
+ # Print the 10 slowest examples and example groups at the
85
+ # end of the spec run, to help surface which specs are running
86
+ # particularly slow.
87
+ config.profile_examples = 10
88
+
89
+ # Run specs in random order to surface order dependencies. If you find an
90
+ # order dependency and want to debug it, you can fix the order by providing
91
+ # the seed, which is printed after each run.
92
+ # --seed 1234
93
+ config.order = :random
94
+
95
+ # Seed global randomization in this process using the `--seed` CLI option.
96
+ # Setting this allows you to use `--seed` to deterministically reproduce
97
+ # test failures related to randomization by passing the same `--seed` value
98
+ # as the one that triggered the failure.
99
+ Kernel.srand config.seed
100
+ =end
101
+ end
@@ -0,0 +1,36 @@
1
+ describe GitSemverCop::Gemspec do
2
+ subject { described_class.new }
3
+
4
+ describe ".path" do
5
+ it "finds the gemspec file in the directory" do
6
+ expect(described_class.path).to eq("git-semver-cop.gemspec")
7
+ end
8
+ end
9
+
10
+ describe ".exist?" do
11
+ it "checks if the path exists" do
12
+ expect(described_class).to receive(:path).and_call_original
13
+ expect(described_class.exist?).to be true
14
+ end
15
+ end
16
+
17
+ describe "#update" do
18
+ before do
19
+ require "tempfile"
20
+ @tmp = Tempfile.new("example.gemspec")
21
+ @tmp.write('spec.version = "1.1.0"')
22
+ @tmp.close
23
+ end
24
+ after { @tmp.unlink }
25
+
26
+ it "updates the version in the file and adds it to git" do
27
+ allow(subject).to receive(:path).and_return(@tmp.path)
28
+ expect_any_instance_of(Kernel).to receive(:system).and_return(true)
29
+ expect(SemVer).to receive(:find).and_return("v1.1.1")
30
+
31
+ subject.update
32
+
33
+ expect(File.read(@tmp.path)).to eq('spec.version = "1.1.1"')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ describe GitSemverCop::PackageJson do
2
+ subject { described_class.new }
3
+
4
+ describe ".path" do
5
+ it "is package.json" do
6
+ expect(described_class.path).to eq("package.json")
7
+ end
8
+ end
9
+
10
+ describe ".exist?" do
11
+ it "checks if the path exists" do
12
+ expect(described_class).to receive(:path).and_call_original
13
+ expect(described_class.exist?).to be false
14
+ end
15
+ end
16
+
17
+ describe "#update" do
18
+ before do
19
+ require "tempfile"
20
+ @tmp = Tempfile.new("package.json")
21
+ @tmp.write('{ "name": "My Name", "version": "1.1.0" }')
22
+ @tmp.close
23
+ end
24
+ after { @tmp.unlink }
25
+
26
+ it "updates the version in the file and adds it to git" do
27
+ allow(subject).to receive(:path).and_return(@tmp.path)
28
+ expect_any_instance_of(Kernel).to receive(:system).and_return(true)
29
+ expect(SemVer).to receive(:find).and_return("v1.1.1")
30
+
31
+ subject.update
32
+
33
+ expect(File.read(@tmp.path)).to eq('{ "name": "My Name", "version": "1.1.1" }')
34
+ end
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-semver-cop
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Bernardo Farah
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: semver2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.4'
69
+ description: A tool that generates a pre-commit hook to enforce updated sematic versioning
70
+ for each commit.
71
+ email:
72
+ - ber@bernardo.me
73
+ executables:
74
+ - git-semver-cop
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - bin/git-semver-cop
82
+ - lib/git-semver-cop.rb
83
+ - lib/git-semver-cop/generator.rb
84
+ - lib/git-semver-cop/semver.rb
85
+ - lib/git-semver-cop/version_files/gemspec.rb
86
+ - lib/git-semver-cop/version_files/package_json.rb
87
+ - lib/git-semver-cop/version_files/version_file.rb
88
+ - lib/hooks/pre-commit
89
+ - spec/git-semver-cop_spec.rb
90
+ - spec/semver_spec.rb
91
+ - spec/spec_helper.rb
92
+ - spec/version_files/gemspec_spec.rb
93
+ - spec/version_files/package_json_spec.rb
94
+ homepage: https://github.com/berfarah/git-semver-cop
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.4.5
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: A small gem to enforce semver versioning before commits
118
+ test_files: []
119
+ has_rdoc: