schmersion 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/VERSION +1 -0
- data/bin/schmersion +20 -0
- data/cli/help.rb +21 -0
- data/cli/init.rb +49 -0
- data/cli/lint-prepare.rb +14 -0
- data/cli/lint-validate.rb +28 -0
- data/cli/log.rb +28 -0
- data/cli/pending.rb +49 -0
- data/cli/release.rb +40 -0
- data/cli/setup-linting.rb +22 -0
- data/cli/versions.rb +14 -0
- data/lib/schmersion.rb +4 -0
- data/lib/schmersion/commit.rb +23 -0
- data/lib/schmersion/commit_parser.rb +60 -0
- data/lib/schmersion/config.rb +67 -0
- data/lib/schmersion/error.rb +6 -0
- data/lib/schmersion/formatter.rb +30 -0
- data/lib/schmersion/formatters.rb +15 -0
- data/lib/schmersion/formatters/markdown.rb +108 -0
- data/lib/schmersion/formatters/yaml.rb +63 -0
- data/lib/schmersion/helpers.rb +32 -0
- data/lib/schmersion/host.rb +25 -0
- data/lib/schmersion/hosts.rb +25 -0
- data/lib/schmersion/hosts/github.rb +50 -0
- data/lib/schmersion/linter.rb +147 -0
- data/lib/schmersion/message.rb +95 -0
- data/lib/schmersion/message_validator.rb +67 -0
- data/lib/schmersion/releaser.rb +135 -0
- data/lib/schmersion/repo.rb +127 -0
- data/lib/schmersion/schmersion_version.rb +12 -0
- data/lib/schmersion/version.rb +32 -0
- data/lib/schmersion/version_calculator.rb +71 -0
- metadata +207 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b913b0f1449212f78614fa4d033a16ee85a839d9992b847978869c35dc4dfc29
|
4
|
+
data.tar.gz: a1452b8b38d15ce8676e20dca1ef936d783b1a66124ab7eedb054a8220cf80e9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 380925581f98a27eb2b5fb45ab4a0e2e1b09dce184040881e8c4bf8541a71e7d74915e18cae2b09580e2454ab801e959be82ff84d3f005c9258d48079193da9e
|
7
|
+
data.tar.gz: 27720896c464ed1d871cd7841e0caa7b705135d23efe3cdc14db9644eac65b91dcae3b2588e42dabc9778f0724025189a1c3bc857afc4db3f5b8376c3744a13f
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/bin/schmersion
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
5
|
+
|
6
|
+
require 'schmersion'
|
7
|
+
require 'swamp/cli'
|
8
|
+
require 'git'
|
9
|
+
require 'colorize'
|
10
|
+
|
11
|
+
begin
|
12
|
+
cli = Swamp::CLI.new(:schmersion, version: Schmersion::VERSION)
|
13
|
+
cli.load_from_directory(File.expand_path('../cli', __dir__))
|
14
|
+
cli.dispatch(ARGV.empty? ? ['help'] : ARGV)
|
15
|
+
rescue Swamp::Error, Git::GitExecuteError, Schmersion::Error => e
|
16
|
+
warn "\e[31mError: #{e.message}\e[0m"
|
17
|
+
exit 2
|
18
|
+
rescue Interrupt
|
19
|
+
exit 3
|
20
|
+
end
|
data/cli/help.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
command :help do
|
4
|
+
desc 'Display this help text'
|
5
|
+
action do |context|
|
6
|
+
puts "\e[35mWelcome to Schmersion v#{Schmersion::VERSION}\e[0m"
|
7
|
+
puts 'For documentation see https://github.com/krystal/schmersion.'
|
8
|
+
puts
|
9
|
+
|
10
|
+
puts 'The following commands are supported:'
|
11
|
+
puts
|
12
|
+
context.cli.commands.sort_by { |k, _v| k.to_s }.each do |_, command|
|
13
|
+
if command.description
|
14
|
+
puts " \e[36m#{command.name.to_s.ljust(18, ' ')}\e[0m #{command.description}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
puts
|
18
|
+
puts 'For details for the options available for each command, use the --help option.'
|
19
|
+
puts "For example 'schmersion pending --help'."
|
20
|
+
end
|
21
|
+
end
|
data/cli/init.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
command :init do
|
4
|
+
desc 'Create a .schmersion.yml config file'
|
5
|
+
|
6
|
+
option '-f', '--force', 'Override existing hooks if they exist' do |_, options|
|
7
|
+
options[:force] = true
|
8
|
+
end
|
9
|
+
|
10
|
+
action do |context|
|
11
|
+
if File.exist?('.schmersion.yaml') && !context.options[:force]
|
12
|
+
puts 'A file already exists at .schmersion.yaml'
|
13
|
+
puts 'Use --force if you wish to re-generate it.'
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
example = <<~EXAMPLE
|
18
|
+
# Welcome to the Schmersion config file
|
19
|
+
#
|
20
|
+
# This is an array of all the commit types that are supported. You can
|
21
|
+
# override this to define your own set as needed per-project.
|
22
|
+
#
|
23
|
+
# types: [feat, fix, style, chore, test, refactor, perf, docs, ci, build, revert]
|
24
|
+
#
|
25
|
+
# By default, you can use any scope (the word in brackets following the type).
|
26
|
+
# If you wish to restrict this, you can define a list of valid scopes.
|
27
|
+
# scopes: [auth, api, ...]
|
28
|
+
#
|
29
|
+
# A key part of the configuration is how you wish to export your CHANGELOG files.w
|
30
|
+
exports:
|
31
|
+
- name: CHANGELOG.md
|
32
|
+
formatter: markdown
|
33
|
+
options:
|
34
|
+
title: CHANGELOG
|
35
|
+
description: This file contains all the latest changes and updates to this application.
|
36
|
+
sections:
|
37
|
+
- title: Features
|
38
|
+
types: [feat]
|
39
|
+
- title: Bug Fixes
|
40
|
+
types: [fix]
|
41
|
+
#
|
42
|
+
# There are additional options you can define, check out the README for details.
|
43
|
+
# https://github.com/krystal/schmersion
|
44
|
+
EXAMPLE
|
45
|
+
|
46
|
+
File.write('.schmersion.yaml', example)
|
47
|
+
puts 'Created new .schmersion.yaml file'.green
|
48
|
+
end
|
49
|
+
end
|
data/cli/lint-prepare.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# rubocop:disable Naming/FileName
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
command :'lint-prepare' do
|
5
|
+
action do |context|
|
6
|
+
require 'schmersion/repo'
|
7
|
+
require 'schmersion/linter'
|
8
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
9
|
+
linter = Schmersion::Linter.new(repo)
|
10
|
+
linter.prepare(context.args[0], context.args[1])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# rubocop:enable Naming/FileName
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# rubocop:disable Naming/FileName
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
command :'lint-validate' do
|
5
|
+
action do |context|
|
6
|
+
require 'schmersion/repo'
|
7
|
+
require 'schmersion/linter'
|
8
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
9
|
+
linter = Schmersion::Linter.new(repo)
|
10
|
+
errors = linter.validate_file(context.args[0])
|
11
|
+
if errors.empty?
|
12
|
+
exit 0
|
13
|
+
end
|
14
|
+
|
15
|
+
puts
|
16
|
+
puts ' Schmersion Commit Linting'.cyan
|
17
|
+
puts
|
18
|
+
puts " Uh oh. There's a few things wrong with your commit message...".red
|
19
|
+
puts ' Please correct these and re-commit to continue.'.red
|
20
|
+
puts
|
21
|
+
errors.each do |error|
|
22
|
+
puts " => #{error}"
|
23
|
+
end
|
24
|
+
puts
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# rubocop:enable Naming/FileName
|
data/cli/log.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
command :log do
|
4
|
+
desc 'Print all commits in a range nicely'
|
5
|
+
|
6
|
+
option '--from [REF]', 'Commit to start from (default: beginning of time)' do |value, options|
|
7
|
+
options[:from] = value
|
8
|
+
end
|
9
|
+
|
10
|
+
option '--to [REF]', 'Commit to finish with (default: current branch)' do |value, options|
|
11
|
+
options[:to] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
action do |context|
|
15
|
+
require 'schmersion/repo'
|
16
|
+
require 'schmersion/helpers'
|
17
|
+
require 'colorize'
|
18
|
+
|
19
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
20
|
+
|
21
|
+
from = context.options[:from] || :start
|
22
|
+
to = context.options[:to] || repo.current_branch
|
23
|
+
|
24
|
+
commits = repo.commits(from, to, exclude_with_invalid_messages: true)
|
25
|
+
|
26
|
+
Schmersion::Helpers.print_commit_list(commits)
|
27
|
+
end
|
28
|
+
end
|
data/cli/pending.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
command :pending do
|
4
|
+
desc 'Displays commits pending'
|
5
|
+
|
6
|
+
option '--from [REF]', 'Version to start from (default: latest version)' do |value, options|
|
7
|
+
options[:from] = value
|
8
|
+
end
|
9
|
+
|
10
|
+
option '--to [REF]', 'Commit to end at (default: HEAD)' do |value, options|
|
11
|
+
options[:to] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
option '--pre [PREFIX]', 'Create a pre-release version' do |value, options|
|
15
|
+
options[:pre] = value || true
|
16
|
+
end
|
17
|
+
|
18
|
+
action do |context|
|
19
|
+
require 'schmersion/repo'
|
20
|
+
require 'schmersion/helpers'
|
21
|
+
|
22
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
23
|
+
current_version, next_version = repo.pending_version(
|
24
|
+
from: context.options[:from],
|
25
|
+
to: context.options[:to],
|
26
|
+
version_options: {
|
27
|
+
pre: context.options[:pre]
|
28
|
+
}
|
29
|
+
)
|
30
|
+
|
31
|
+
puts
|
32
|
+
if current_version
|
33
|
+
puts 'The current published version is:'
|
34
|
+
puts
|
35
|
+
puts " #{current_version.to_s.cyan}"
|
36
|
+
puts
|
37
|
+
puts 'The next version will be:'
|
38
|
+
else
|
39
|
+
puts 'The first version will be:'
|
40
|
+
end
|
41
|
+
puts
|
42
|
+
puts " #{next_version.version.to_s.green}"
|
43
|
+
puts
|
44
|
+
puts 'The following commits will be included:'
|
45
|
+
puts
|
46
|
+
Schmersion::Helpers.print_commit_list(next_version.commits, prefix: ' ')
|
47
|
+
puts
|
48
|
+
end
|
49
|
+
end
|
data/cli/release.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
command :release do
|
4
|
+
desc 'Create a release'
|
5
|
+
|
6
|
+
option '-r [VERSION]', 'Override the version number' do |value, options|
|
7
|
+
options[:version] = value
|
8
|
+
end
|
9
|
+
|
10
|
+
option '--dry-run', "Don't actually do anything, just show a preview" do |_, options|
|
11
|
+
options[:dry_run] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
option '--pre [PREFIX]', 'Create a pre-release version' do |value, options|
|
15
|
+
options[:pre] = value || true
|
16
|
+
end
|
17
|
+
|
18
|
+
option '--skip-export', "Don't generate new CHANGELOG exports" do |_, options|
|
19
|
+
options[:skips] ||= []
|
20
|
+
options[:skips] << :export
|
21
|
+
end
|
22
|
+
|
23
|
+
option '--skip-commit', "Don't commit anything" do |_, options|
|
24
|
+
options[:skips] ||= []
|
25
|
+
options[:skips] << :commit
|
26
|
+
end
|
27
|
+
|
28
|
+
option '--skip-tag', "Don't create a tag" do |_, options|
|
29
|
+
options[:skips] ||= []
|
30
|
+
options[:skips] << :tag
|
31
|
+
end
|
32
|
+
|
33
|
+
action do |context|
|
34
|
+
require 'schmersion/repo'
|
35
|
+
require 'schmersion/releaser'
|
36
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
37
|
+
releaser = Schmersion::Releaser.new(repo, context.options)
|
38
|
+
releaser.release
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# rubocop:disable Naming/FileName
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
command :'setup-linting' do
|
5
|
+
desc 'Install git hooks to lint commit messages'
|
6
|
+
|
7
|
+
option '-f', '--force', 'Override existing hooks if they exist' do |_, options|
|
8
|
+
options[:force] = true
|
9
|
+
end
|
10
|
+
|
11
|
+
action do |context|
|
12
|
+
require 'schmersion/repo'
|
13
|
+
require 'schmersion/linter'
|
14
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
15
|
+
linter = Schmersion::Linter.new(repo)
|
16
|
+
linter.setup(force: context.options[:force])
|
17
|
+
|
18
|
+
puts 'Installed hooks successfully'.green
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# rubocop:enable Naming/FileName
|
data/cli/versions.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
command :versions do
|
4
|
+
desc 'Print a list of all versions in version order'
|
5
|
+
action do
|
6
|
+
require 'schmersion/repo'
|
7
|
+
require 'colorize'
|
8
|
+
|
9
|
+
repo = Schmersion::Repo.new(FileUtils.pwd)
|
10
|
+
repo.versions.each do |version, _|
|
11
|
+
puts version
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/schmersion.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'schmersion/message'
|
4
|
+
|
5
|
+
module Schmersion
|
6
|
+
class Commit
|
7
|
+
|
8
|
+
attr_reader :message
|
9
|
+
attr_reader :ref
|
10
|
+
attr_reader :date
|
11
|
+
attr_reader :author
|
12
|
+
attr_reader :raw_commit
|
13
|
+
|
14
|
+
def initialize(commit)
|
15
|
+
@message = Message.new(commit.message)
|
16
|
+
@ref = commit.sha
|
17
|
+
@date = commit.date
|
18
|
+
@author = commit.author.name
|
19
|
+
@raw_commit = commit
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'schmersion/commit'
|
4
|
+
require 'schmersion/version_calculator'
|
5
|
+
|
6
|
+
module Schmersion
|
7
|
+
class CommitParser
|
8
|
+
|
9
|
+
MAX_COMMITS = 100_000_000
|
10
|
+
|
11
|
+
attr_reader :commits
|
12
|
+
|
13
|
+
def initialize(repo, start_commit, end_commit, **options)
|
14
|
+
@start_commit = start_commit
|
15
|
+
@end_commit = end_commit
|
16
|
+
@options = options
|
17
|
+
|
18
|
+
@commits = []
|
19
|
+
|
20
|
+
if @start_commit == :start
|
21
|
+
# If start is provided, use the first commit as the initial reference.
|
22
|
+
# Ideally this would actually start from the beginning of time and include
|
23
|
+
# this commit.
|
24
|
+
first_commit = repo.log(MAX_COMMITS).last
|
25
|
+
@start_commit = first_commit.sha
|
26
|
+
end
|
27
|
+
|
28
|
+
@raw_commits = repo.log(MAX_COMMITS).between(@start_commit, @end_commit)
|
29
|
+
|
30
|
+
parse
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse
|
34
|
+
@commits = []
|
35
|
+
@raw_commits.each do |commit|
|
36
|
+
commit = Commit.new(commit)
|
37
|
+
|
38
|
+
next if skip_commit?(commit)
|
39
|
+
|
40
|
+
@commits << commit
|
41
|
+
end
|
42
|
+
@commits = @commits.reverse
|
43
|
+
@commits
|
44
|
+
end
|
45
|
+
|
46
|
+
def next_version_after(current_version, **options)
|
47
|
+
calculator = VersionCalculator.new(current_version, commits, **options)
|
48
|
+
calculator.calculate
|
49
|
+
end
|
50
|
+
|
51
|
+
def skip_commit?(commit)
|
52
|
+
# We never want to see merge commits...
|
53
|
+
return true if commit.raw_commit.parents.size > 1
|
54
|
+
return true unless commit.message.valid?
|
55
|
+
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Schmersion
|
4
|
+
class Config
|
5
|
+
|
6
|
+
DEFAULT_TYPES = %w[feat fix style chore test refactor perf docs ci build revert].freeze
|
7
|
+
|
8
|
+
DEFAULT_VERSION_OPTIONS = {
|
9
|
+
breaking_change_not_major: false
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
DEFAULT_LINTING_OPTIONS = {
|
13
|
+
max_description_length: 60
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(hash)
|
17
|
+
@hash = hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def types
|
21
|
+
@hash['types'] || DEFAULT_TYPES
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_type?(type)
|
25
|
+
return true if types.empty?
|
26
|
+
|
27
|
+
types.include?(type.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
def scopes
|
31
|
+
@hash['scopes'] || []
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_scope?(scope)
|
35
|
+
return true if scopes.empty?
|
36
|
+
|
37
|
+
scopes.include?(scope.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def exports
|
41
|
+
return [] if @hash['exports'].nil?
|
42
|
+
|
43
|
+
@hash['exports'].map { |e| create_export(e) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def linting
|
47
|
+
DEFAULT_LINTING_OPTIONS.merge(@hash['linting']&.transform_keys(&:to_sym) || {})
|
48
|
+
end
|
49
|
+
|
50
|
+
def version_options
|
51
|
+
DEFAULT_VERSION_OPTIONS.merge(@hash['version_options']&.transform_keys(&:to_sym) || {})
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def create_export(export)
|
57
|
+
name = export['name']
|
58
|
+
formatter = Formatters::FORMATTERS[export['formatter'].to_sym]
|
59
|
+
if formatter.nil?
|
60
|
+
raise Error, "Invalid formatter '#{export['formatter']}' for #{name}"
|
61
|
+
end
|
62
|
+
|
63
|
+
formatter.new(name, export['options'] || {})
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|