changi 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/changi/changelog.rb +29 -0
- data/lib/changi/configuration.rb +34 -0
- data/lib/changi/entry.rb +48 -0
- data/lib/changi/entry_set.rb +50 -0
- data/lib/changi/flexible_attributes.rb +33 -0
- data/lib/changi/reader/category_reader.rb +24 -0
- data/lib/changi/reader/multiline_reader.rb +63 -0
- data/lib/changi/reader/string_reader.rb +21 -0
- data/lib/changi/release.rb +20 -0
- data/lib/changi/renderer.rb +18 -0
- data/lib/changi/tasks.rb +20 -0
- data/lib/changi/updater/prepend_updater.rb +15 -0
- data/lib/changi.rb +27 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d7fbfb1689d7effa8bed7d6ca6ca66073b6f31f
|
4
|
+
data.tar.gz: c436fe107d78b2479e66f578e0a92ba783eb3344
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4e10adc77fde7170e214922b6a9845f17a0af8d4b039bb53944076e424287e126c4236ca66480da5ebf0b46f9f5534a4b1135f86929808c36cbd71ec886da23
|
7
|
+
data.tar.gz: 0e8c75ba30d82a81ff03acfd492db5f8f3cd4abb75628ce3f85a15450722b748f0ab7c9d58201d09ed26d75b5fe626b4889ab49bcd859e9e8e4a17c6b6274ebf
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Changi
|
2
|
+
class Changelog
|
3
|
+
def initialize config
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def new_entry
|
8
|
+
entry_set.new_entry
|
9
|
+
end
|
10
|
+
|
11
|
+
def render demo: false
|
12
|
+
release = demo ? Release.demo : Release.build
|
13
|
+
renderer = Renderer.new @config, entry_set, release
|
14
|
+
renderer.render
|
15
|
+
end
|
16
|
+
|
17
|
+
def update
|
18
|
+
updater = @config.updater.new
|
19
|
+
updater.update @config.changelog_path, render
|
20
|
+
entry_set.destroy_all
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def entry_set
|
26
|
+
@entry_set ||= EntrySet.new @config
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Changi
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :changelog_path
|
4
|
+
attr_accessor :entries_path
|
5
|
+
attr_accessor :default_categories
|
6
|
+
attr_accessor :updater
|
7
|
+
attr_accessor :changelog_template
|
8
|
+
|
9
|
+
def self.default
|
10
|
+
new.tap do |config|
|
11
|
+
config.changelog_path = File.join Dir.pwd, 'changelog.md'
|
12
|
+
config.entries_path = File.join Dir.pwd, 'changelog'
|
13
|
+
config.default_categories = []
|
14
|
+
config.updater = Updater::PrependUpdater
|
15
|
+
config.changelog_template = <<-eod
|
16
|
+
# <%= release.version %>, <%= Time.now.strftime('%Y-%m-%d') %><%= release.notes and ", \#{release.notes}" %>
|
17
|
+
<% entry_set.entries_by_category.each do |category, entries| %>
|
18
|
+
## <%= category %>
|
19
|
+
|
20
|
+
<%=
|
21
|
+
entries.map do |e|
|
22
|
+
if e.text.is_a?(Array)
|
23
|
+
e.text.map.with_index { |l, i| i == 0 ? "* \#{l}" : " \#{l}" }.join("\n")
|
24
|
+
else
|
25
|
+
"* \#{e.text.gsub("\n", ' ')}"
|
26
|
+
end
|
27
|
+
end.join("\n")
|
28
|
+
%>
|
29
|
+
<% end %>
|
30
|
+
eod
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/changi/entry.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Changi
|
4
|
+
class Entry
|
5
|
+
include FlexibleAttributes
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build entry_set, yaml_path
|
9
|
+
new(entry_set, yaml_path).tap &:read_in_attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def load entry_set, yaml_path
|
13
|
+
new(entry_set, yaml_path).tap &:read_in_yaml
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
define_attribute :category, reader: Reader::CategoryReader
|
18
|
+
define_attribute :text, reader: Reader::MultilineReader, opts: { required: true }
|
19
|
+
|
20
|
+
attr_reader :entry_set
|
21
|
+
|
22
|
+
def initialize entry_set, path
|
23
|
+
@entry_set = entry_set
|
24
|
+
@path = path
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_in_yaml
|
28
|
+
data = YAML.load_file @path
|
29
|
+
self.class.attribute_names.each { |name| send "#{name}=".to_sym, data[name] }
|
30
|
+
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
File.open(@path, 'w') { |fd| fd.puts to_yaml }
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
unless system "git rm '#{@path}' >/dev/null 2>&1"
|
38
|
+
FileUtils.rm_f @path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def to_yaml
|
45
|
+
self.class.attribute_names.map { |name| [name, send(name.to_sym)] }.to_h.to_yaml
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Changi
|
4
|
+
class EntrySet
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize config
|
8
|
+
@config = config
|
9
|
+
FileUtils.mkdir_p @config.entries_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_entry
|
13
|
+
entries << Entry.build(self, make_entry_path).tap(&:save)
|
14
|
+
end
|
15
|
+
|
16
|
+
def entries
|
17
|
+
@entries ||= yamls.map { |yml| Entry.load self, yml }
|
18
|
+
end
|
19
|
+
|
20
|
+
def entries_by_category
|
21
|
+
entries.group_by &:category
|
22
|
+
end
|
23
|
+
|
24
|
+
def previous_categories
|
25
|
+
entries.map &:category
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy_all
|
29
|
+
entries.each &:destroy
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def yamls
|
35
|
+
Dir["#{@config.entries_path}/*yml"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def make_entry_path
|
39
|
+
File.join @config.entries_path, make_entry_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def make_entry_name
|
43
|
+
if git_branch = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
|
44
|
+
"#{Time.now.strftime('%y%m%d%H%M%S')}_from_#{git_branch}.yml"
|
45
|
+
else
|
46
|
+
"#{Time.now.strftime('%y%m%d%H%M%S')}.yml"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Changi
|
2
|
+
module FlexibleAttributes
|
3
|
+
module ClassMethods
|
4
|
+
def attributes
|
5
|
+
@attributes ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def attribute_names
|
9
|
+
attributes.map { |x| x[:name] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def define_attribute name, reader: Reader::StringReader, opts: {}, &block
|
13
|
+
attr_accessor name.to_sym
|
14
|
+
|
15
|
+
attributes << {
|
16
|
+
name: name,
|
17
|
+
reader: (block_given? ? block : ->(*args) { reader.new.read *args }),
|
18
|
+
opts: opts
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.included base
|
24
|
+
base.extend ClassMethods
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_in_attributes
|
28
|
+
self.class.attributes.each do |data|
|
29
|
+
send "#{data[:name]}=".to_sym, data[:reader].call(data, self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Changi
|
2
|
+
module Reader
|
3
|
+
class CategoryReader < StringReader
|
4
|
+
def read attribute, owner
|
5
|
+
fail 'CategoryReader can only be used for Entry attributes.' unless owner.is_a? Entry
|
6
|
+
|
7
|
+
cli.say "#{attribute[:name].capitalize}:\n"
|
8
|
+
cli.choose do |menu|
|
9
|
+
menu.choices *categories(owner.entry_set)
|
10
|
+
menu.choice 'Other (create new)' do
|
11
|
+
cli.ask "Please insert #{attribute[:name]}:"
|
12
|
+
end
|
13
|
+
menu.choice('Abort') { exit }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def categories entry_set
|
20
|
+
(entry_set.previous_categories + entry_set.config.default_categories).uniq
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module Changi
|
4
|
+
module Reader
|
5
|
+
class MultilineReader
|
6
|
+
def read attribute, owner
|
7
|
+
tmpfile = Tempfile.new 'changi'
|
8
|
+
|
9
|
+
intro tmpfile, attribute
|
10
|
+
|
11
|
+
unless system "#{editor} '#{tmpfile.path.strip}'"
|
12
|
+
abort 'editor returned with non-zero exit status, abort'
|
13
|
+
end
|
14
|
+
|
15
|
+
read_and_strip(tmpfile).tap do |data|
|
16
|
+
if attribute[:opts][:required] && data.empty?
|
17
|
+
abort "required #{attribute[:name]} attribute empty, abort"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
ensure
|
21
|
+
tmpfile.close
|
22
|
+
tmpfile.unlink
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def intro tmpfile, attribute
|
28
|
+
tmpfile.puts
|
29
|
+
tmpfile.puts
|
30
|
+
tmpfile.puts "# Please enter #{attribute[:name]} above."
|
31
|
+
tmpfile.puts "# Lines starting with # will be ignored."
|
32
|
+
tmpfile.puts "# Empty tmpfile will abort the process." if attribute[:opts][:required]
|
33
|
+
tmpfile.sync
|
34
|
+
tmpfile.close
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_and_strip tmpfile
|
38
|
+
tmpfile.open
|
39
|
+
tmpfile.read.strip.split("\n").reject { |x| x =~ /^\s*#/ }
|
40
|
+
.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse
|
41
|
+
end
|
42
|
+
|
43
|
+
def editor
|
44
|
+
editor_tests.lazy.map(&:call).find { |e| !(e.nil? || e.empty?) }.tap do |e|
|
45
|
+
fail 'could not detect editor' unless e
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def editor_tests
|
50
|
+
[
|
51
|
+
-> { ENV['EDITOR'] },
|
52
|
+
-> { `git config core.editor`.strip },
|
53
|
+
-> { editor_exists?('nano') && 'nano' },
|
54
|
+
-> { editor_exists?('vim') && 'vim' }
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def editor_exists? app
|
59
|
+
system "command -v #{app} >/dev/null 2>&1"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'highline'
|
2
|
+
|
3
|
+
module Changi
|
4
|
+
module Reader
|
5
|
+
class StringReader
|
6
|
+
def read attribute, owner
|
7
|
+
cli.ask("#{attribute[:name]}: ").tap do |x|
|
8
|
+
if attribute[:opts][:required] && [nil, ''].include?(x)
|
9
|
+
abort "required #{attribute[:name]} attribute empty, abort"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def cli
|
17
|
+
@cli ||= HighLine.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Changi
|
2
|
+
class Release
|
3
|
+
include FlexibleAttributes
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def build
|
7
|
+
new.tap &:read_in_attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def demo
|
11
|
+
new.tap do |inst|
|
12
|
+
attribute_names.each { |name| inst.send "#{name}=".to_sym, "<#{name}>" }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
define_attribute :version
|
18
|
+
define_attribute :notes
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Changi
|
4
|
+
class Renderer
|
5
|
+
attr_reader :entry_set
|
6
|
+
attr_reader :release
|
7
|
+
|
8
|
+
def initialize config, entry_set, release
|
9
|
+
@config = config
|
10
|
+
@entry_set = entry_set
|
11
|
+
@release = release
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
ERB.new(@config.changelog_template).result binding
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/changi/tasks.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'changi'
|
2
|
+
|
3
|
+
namespace :changi do
|
4
|
+
desc 'Create a new changelog entry'
|
5
|
+
task :new do
|
6
|
+
Changi.changelog.new_entry
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'Show changelog for next release'
|
10
|
+
task :diff do
|
11
|
+
puts Changi.changelog.render demo: true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Update project changelog for release'
|
15
|
+
task :update do
|
16
|
+
Changi.changelog.update
|
17
|
+
puts 'Changelog updated, and changelog directory cleared.'
|
18
|
+
puts 'Check if there were manual changes to changelog.md and merge them before committing.'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Changi
|
2
|
+
module Updater
|
3
|
+
class PrependUpdater
|
4
|
+
def update changelog_path, release_data
|
5
|
+
previous = File.read changelog_path if File.exists? changelog_path
|
6
|
+
|
7
|
+
File.open changelog_path, 'w' do |fd|
|
8
|
+
fd.puts release_data
|
9
|
+
fd.puts
|
10
|
+
fd.puts previous if previous
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/changi.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'changi/reader/string_reader'
|
2
|
+
require 'changi/reader/multiline_reader'
|
3
|
+
require 'changi/reader/category_reader'
|
4
|
+
require 'changi/updater/prepend_updater'
|
5
|
+
require 'changi/flexible_attributes'
|
6
|
+
require 'changi/changelog'
|
7
|
+
require 'changi/configuration'
|
8
|
+
require 'changi/entry'
|
9
|
+
require 'changi/entry_set'
|
10
|
+
require 'changi/release'
|
11
|
+
require 'changi/renderer'
|
12
|
+
|
13
|
+
module Changi
|
14
|
+
class << self
|
15
|
+
def configuration
|
16
|
+
@configuration ||= Configuration.default
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure
|
20
|
+
yield configuration
|
21
|
+
end
|
22
|
+
|
23
|
+
def changelog
|
24
|
+
@changelog ||= Changelog.new configuration
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: changi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mobisol GmbH
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: highline
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
description: Manages a set of changelog entries for you and combines them to a changelog
|
42
|
+
file when you release.
|
43
|
+
email: dev@plugintheworld.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/changi.rb
|
49
|
+
- lib/changi/changelog.rb
|
50
|
+
- lib/changi/configuration.rb
|
51
|
+
- lib/changi/entry.rb
|
52
|
+
- lib/changi/entry_set.rb
|
53
|
+
- lib/changi/flexible_attributes.rb
|
54
|
+
- lib/changi/reader/category_reader.rb
|
55
|
+
- lib/changi/reader/multiline_reader.rb
|
56
|
+
- lib/changi/reader/string_reader.rb
|
57
|
+
- lib/changi/release.rb
|
58
|
+
- lib/changi/renderer.rb
|
59
|
+
- lib/changi/tasks.rb
|
60
|
+
- lib/changi/updater/prepend_updater.rb
|
61
|
+
homepage: https://github.org/plugintheworld/changi
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.4.5.1
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Tiny library of changelog management rake tasks.
|
85
|
+
test_files: []
|
86
|
+
has_rdoc:
|