changi 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|