devist 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/devist +155 -0
- data/lib/devist.rb +94 -0
- data/lib/devist/compiler.rb +50 -0
- data/lib/devist/export/html/_default.html.erb +277 -0
- data/lib/devist/export/html/_polar.html.erb +281 -0
- data/lib/devist/extractor.rb +26 -0
- data/lib/devist/models/project.rb +12 -0
- data/lib/devist/models/tags.rb +32 -0
- data/lib/devist/models/version.rb +25 -0
- data/lib/devist/parser.rb +101 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 56284ad08775f7a152ddc16b24b1f3250f87a4c1
|
4
|
+
data.tar.gz: 809589a83532dde27f85530ec0e81892efe8cae0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf5cf1693998e7b3b00c53690872e00437478a5548ff2aa1d55df70ec9bed643bb7586fccb44c6cb07bf06c8907f99268be0f7237a583f9f87e3744237964b94
|
7
|
+
data.tar.gz: 61f38f87748e6530c838715817abd9757e37c1d87e7541d96eebd44739f3ab102529ce78f5dadb02572027345ba5e4a6d6a517736ec653fada95c52dd007e514
|
data/bin/devist
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'devist'
|
4
|
+
|
5
|
+
##############################################################
|
6
|
+
#
|
7
|
+
# bin/devist
|
8
|
+
#
|
9
|
+
# Main action point. Devist binary.
|
10
|
+
# stacklog/devist @ github
|
11
|
+
#
|
12
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
13
|
+
#
|
14
|
+
##############################################################
|
15
|
+
|
16
|
+
# bin/devist.rb
|
17
|
+
# This file is a part of the devist package.
|
18
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
19
|
+
#
|
20
|
+
# This is the main entry point of the Devist. It creates a
|
21
|
+
# binary and parse and execute actions based on given CLI
|
22
|
+
# arguments.
|
23
|
+
class DevistBinary
|
24
|
+
|
25
|
+
# Print banner image.
|
26
|
+
def print_logo
|
27
|
+
puts <<EOL
|
28
|
+
__ _ __
|
29
|
+
____/ /__ _ __(_)____/ /_
|
30
|
+
/ __ / _ \\ | / / / ___/ __/
|
31
|
+
/ /_/ / __/ |/ / (__ ) /_
|
32
|
+
\\__,_/\\___/|___/_/____/\\__/
|
33
|
+
- Release notes generator.
|
34
|
+
https://github.com/stacklog/devist
|
35
|
+
|
36
|
+
Use --help for details.
|
37
|
+
|
38
|
+
EOL
|
39
|
+
end
|
40
|
+
|
41
|
+
# Print help menu.
|
42
|
+
def print_help
|
43
|
+
puts <<EOL
|
44
|
+
Use devist to generate beautiful release notes. All you need to have is CHANGELOG.md with
|
45
|
+
.devist line at the end of the file.
|
46
|
+
|
47
|
+
$ devist | run devist
|
48
|
+
$ devist --help | usage / this menu
|
49
|
+
$ devist --v | show current version
|
50
|
+
$ devist --new | create changelog template
|
51
|
+
|
52
|
+
-
|
53
|
+
$ devist file_name theme_name
|
54
|
+
|
55
|
+
Example:
|
56
|
+
$ devist changelog
|
57
|
+
$ devist changelog polar
|
58
|
+
EOL
|
59
|
+
end
|
60
|
+
|
61
|
+
# Entry point. Main.
|
62
|
+
def ep (arguments)
|
63
|
+
@arguments = arguments
|
64
|
+
|
65
|
+
print_logo
|
66
|
+
|
67
|
+
# Recieved filename + theme as args
|
68
|
+
if @arguments.count.equal?(2)
|
69
|
+
return theme
|
70
|
+
end
|
71
|
+
|
72
|
+
# Check other arguments
|
73
|
+
case @arguments[0]
|
74
|
+
when '--help'
|
75
|
+
print_help
|
76
|
+
when '--v'
|
77
|
+
version
|
78
|
+
when '--new'
|
79
|
+
create # Create new changelog (fresh project)
|
80
|
+
else
|
81
|
+
default # Try to export with filename and default theme
|
82
|
+
return
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Generate with default theme.
|
87
|
+
def default
|
88
|
+
file_name = @arguments[0]
|
89
|
+
|
90
|
+
unless file_name.nil?
|
91
|
+
decompile(file_name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Generate by specifying theme.
|
96
|
+
def theme
|
97
|
+
file_name = @arguments[0]
|
98
|
+
theme_name = @arguments[1]
|
99
|
+
|
100
|
+
unless file_name.nil?
|
101
|
+
decompile(file_name, theme_name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Display devist version.
|
106
|
+
def version
|
107
|
+
v = `git rev-parse HEAD`
|
108
|
+
d = `git log -1 --format=%cd`
|
109
|
+
print " * devist version: #{v[0, 7]} - #{d[0, 10]}\n"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Create new changelog.
|
113
|
+
def create
|
114
|
+
print " * Generating CHANGELOG.md ...\n"
|
115
|
+
|
116
|
+
if File.file?(Dir.pwd + '/CHANGELOG.md')
|
117
|
+
print " * File CHANGELOG.md already exists. Backup it, remove, and try again.\n"
|
118
|
+
print " * You may try to run 'devist changelog' to try generate the export.\n"
|
119
|
+
abort
|
120
|
+
end
|
121
|
+
|
122
|
+
new = File.new(Dir.pwd + '/CHANGELOG.md', 'w')
|
123
|
+
print " * Output .devist as a way of thinkering ...\n"
|
124
|
+
thinkering(new)
|
125
|
+
print " -\n"
|
126
|
+
print " ** All done! Continue editing CHANGELOG.md.\n"
|
127
|
+
end
|
128
|
+
|
129
|
+
# Fill new changelog.
|
130
|
+
def thinkering(new)
|
131
|
+
new.puts('@project: MyProject ')
|
132
|
+
new.puts('@author: Your Name <email@address.com> ')
|
133
|
+
new.puts('@homepage: https://example.com ')
|
134
|
+
new.puts('')
|
135
|
+
new.puts("### Version 1.0.0 of #{Time.now.strftime("%d %b %Y")}")
|
136
|
+
new.puts('+ #added: something goes here')
|
137
|
+
new.puts('')
|
138
|
+
new.puts('.devist')
|
139
|
+
new.close
|
140
|
+
end
|
141
|
+
|
142
|
+
# Decompile ep.
|
143
|
+
def decompile(file_name, theme_name = 'default')
|
144
|
+
print " * devist will try to generate export for file '#{file_name}' with theme '#{theme_name}'\n"
|
145
|
+
|
146
|
+
@devist = Devist.new(file_name, theme_name)
|
147
|
+
@devist.decompile
|
148
|
+
@devist.recompile
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
# Create new bin/ep with argv
|
154
|
+
devistbin = DevistBinary.new
|
155
|
+
devistbin.ep(ARGV)
|
data/lib/devist.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# devist.rb
|
2
|
+
# This file is a part of the devist package.
|
3
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
4
|
+
#
|
5
|
+
# This is the place where the ep action is being taken.
|
6
|
+
# Devist check if setup is ok and proceed with parsing
|
7
|
+
# and creating the static export.
|
8
|
+
class Devist
|
9
|
+
|
10
|
+
#
|
11
|
+
# @project: stacklog/devist
|
12
|
+
# @author: Halis Duraki <duraki.halis@nsoft.ba>
|
13
|
+
# @homepage: https://github.com/stacklog/devist
|
14
|
+
#
|
15
|
+
|
16
|
+
attr_reader :parser, :compiler
|
17
|
+
|
18
|
+
# Default files to look for
|
19
|
+
@@default_search = %w[CHANGELOG RELEASES NEWS]
|
20
|
+
|
21
|
+
# Init
|
22
|
+
def initialize(filename, theme = 'default')
|
23
|
+
|
24
|
+
@parser = Parser.new
|
25
|
+
@filename = filename + ".md"
|
26
|
+
@themename = "_" + theme + ".html.erb"
|
27
|
+
|
28
|
+
# Theme directory is absolute dirpath + subs
|
29
|
+
theme_dir = __dir__ + "/devist/export/html/"
|
30
|
+
|
31
|
+
# Check if both theme and filename exists
|
32
|
+
if File.file?(@filename)
|
33
|
+
print " * File '#{@filename}' exists; continuing ...\n"
|
34
|
+
else
|
35
|
+
print " * File '#{@filename}' does NOT exists; exit ...\n"
|
36
|
+
print " > Do you want to allow devist to search the file for you? (y\/N) "
|
37
|
+
devist_as = STDIN.gets.chomp
|
38
|
+
|
39
|
+
# Search for changelogs automatically
|
40
|
+
if devist_as.downcase.eql?("y")
|
41
|
+
print " * Searching for changelog data ...\n"
|
42
|
+
available
|
43
|
+
|
44
|
+
if @@available.count > 0
|
45
|
+
print " * Try to run with filename #{@@available_list}\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
if File.file?(theme_dir + @themename)
|
53
|
+
print " * Theme '#{@themename}' exists; continuing ...\n"
|
54
|
+
else
|
55
|
+
print " * Theme '#{@themename}' does NOT exists; exit ...\n"
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# Auto-search for available changelog
|
62
|
+
def available
|
63
|
+
@@available = []
|
64
|
+
@@available_list = String.new
|
65
|
+
|
66
|
+
@@default_search.each do |filename|
|
67
|
+
filename.concat(".md")
|
68
|
+
if File.file?(filename)
|
69
|
+
@@available_list.concat(filename + " ")
|
70
|
+
@@available.push(filename)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
print " * Found #{@@available.count} results.\n"
|
75
|
+
|
76
|
+
@@available.count
|
77
|
+
end
|
78
|
+
|
79
|
+
# Decompile .md file.
|
80
|
+
def decompile
|
81
|
+
@parser.parse_data(@filename)
|
82
|
+
@parser.project
|
83
|
+
end
|
84
|
+
|
85
|
+
# Recompile .md file.
|
86
|
+
def recompile
|
87
|
+
@compiler = Compiler.new(@parser.project, @parser.changelog, @themename)
|
88
|
+
@compiler.compile_data
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
require 'devist/parser'
|
94
|
+
require 'devist/compiler'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
# compiler.rb
|
4
|
+
# This file is a part of the devist package.
|
5
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
6
|
+
#
|
7
|
+
# This is the main compiler class. It takes arguments
|
8
|
+
# project (model), changelog (model), and theme name.
|
9
|
+
# Object offers a version listing plus save operation
|
10
|
+
# and binding to the theme erb.
|
11
|
+
class Devist::Compiler
|
12
|
+
|
13
|
+
# Init.
|
14
|
+
def initialize(project, changelog, theme)
|
15
|
+
@project = project
|
16
|
+
@changelog = changelog
|
17
|
+
@theme = theme
|
18
|
+
end
|
19
|
+
|
20
|
+
# Save compiled.
|
21
|
+
def save
|
22
|
+
print " -\n"
|
23
|
+
print " * Trying to compile set ...\n"
|
24
|
+
|
25
|
+
print " * Creating new export from erb ...\n"
|
26
|
+
asset = "#{__dir__}/export/html/#{@theme}"
|
27
|
+
erb = ERB.new(File.open(asset).read, 0, '>')
|
28
|
+
|
29
|
+
print " * Injecting parsed results to the erb ...\n"
|
30
|
+
erb.result binding
|
31
|
+
|
32
|
+
print " * Writing compiled data to changelog file ...\n"
|
33
|
+
File.open("#{Dir.pwd}/changelog.html", 'w') do |f|
|
34
|
+
f.write erb.result binding
|
35
|
+
end
|
36
|
+
|
37
|
+
print " -\n"
|
38
|
+
print " ** All done! Check changelog.html file in your browser :)\n"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Compile data.
|
42
|
+
def compile_data
|
43
|
+
@changelog.each do |version|
|
44
|
+
print " * Found version #{version.version}; registered ...\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
save
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
|
4
|
+
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
|
5
|
+
<meta content="<%= @project.author %>/<%= @project.name %>" name="description" />
|
6
|
+
<meta content="width=device-width, initial-scale=1, user-scalable=no" name="viewport" />
|
7
|
+
|
8
|
+
<!-- Stylesheets -->
|
9
|
+
<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css' rel="stylesheet" type="text/css" />
|
10
|
+
<link href='https://fonts.googleapis.com/css?family=Abel|Roboto:400|Roboto+Condensed:400|Roboto+Mono:400' rel='stylesheet' type='text/css'>
|
11
|
+
|
12
|
+
<!-- Title -->
|
13
|
+
<title><%= @project.author.gsub(/\<.*?>/,"").chomp.strip! + '/' + @project.name %> - devist notes</title>
|
14
|
+
|
15
|
+
<body>
|
16
|
+
<header><h2>Release Notes - <%= @project.name %></h2></header>
|
17
|
+
<nav class="side-nav">
|
18
|
+
<ul style="background: none; color: #555; padding: 0; margin: 0;">
|
19
|
+
<li><a class="side-nav-item" href="<%= @project.homepage %>"><%= @project.author.gsub(/\<.*?\>/,"").chomp.strip! + '/' + @project.name %></a> </li>
|
20
|
+
<li><a style="font-size:12px;" class="side-nav-item " href="https://github.com/stacklog/devist">Created with devist</a></li>
|
21
|
+
</ul>
|
22
|
+
</nav>
|
23
|
+
|
24
|
+
<section class="container">
|
25
|
+
|
26
|
+
<% @changelog.each do |version| %>
|
27
|
+
|
28
|
+
<div id="<%= version.version %>">
|
29
|
+
<a href="#<%= version.version %>"><h3>Version <%= version.version %></h3></a>
|
30
|
+
<p><%= version.date.to_s %></p>
|
31
|
+
|
32
|
+
<ul>
|
33
|
+
|
34
|
+
<% version.tags.get[0].each do |added| %>
|
35
|
+
<li><span class="label label--add">added</span> <%= added.chomp %></li>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<% version.tags.get[1].each do |fixed| %>
|
39
|
+
<li><span class="label label--fixed">fixed</span> <%= fixed.chomp %></li>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<% version.tags.get[2].each do |removed| %>
|
43
|
+
<li><span class="label label--removed">removed</span> <%= removed.chomp %></li>
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
<% version.tags.get[3].each do |improved| %>
|
47
|
+
<li><span class="label label--improve">improved</span> <%= improved.chomp %></li>
|
48
|
+
<% end %>
|
49
|
+
|
50
|
+
</ul>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
|
54
|
+
<% end %>
|
55
|
+
|
56
|
+
</section>
|
57
|
+
|
58
|
+
|
59
|
+
</body>
|
60
|
+
|
61
|
+
<!-- Custom style -->
|
62
|
+
<style>
|
63
|
+
|
64
|
+
:root {
|
65
|
+
background: #292929;
|
66
|
+
color: #bbb;
|
67
|
+
}
|
68
|
+
|
69
|
+
.label {
|
70
|
+
background: rgba(41, 41, 41, 0.27);
|
71
|
+
padding: 3px;
|
72
|
+
border-radius: 5px;
|
73
|
+
}
|
74
|
+
|
75
|
+
code {
|
76
|
+
border-color: rgba(255,255,255,0);;
|
77
|
+
}
|
78
|
+
|
79
|
+
h2 {
|
80
|
+
color: #555;
|
81
|
+
}
|
82
|
+
|
83
|
+
header {
|
84
|
+
padding-top: 4.5rem;
|
85
|
+
}
|
86
|
+
|
87
|
+
kbd {
|
88
|
+
background: rgba(255,255,255,.1);
|
89
|
+
border-color: rgba(0,0,0,.35);;
|
90
|
+
}
|
91
|
+
|
92
|
+
nav {
|
93
|
+
position: fixed;
|
94
|
+
left: 1.8rem;
|
95
|
+
top: 1.8rem;
|
96
|
+
}
|
97
|
+
|
98
|
+
section.container {
|
99
|
+
margin-bottom: 7.2rem;
|
100
|
+
max-width: 30rem;
|
101
|
+
}
|
102
|
+
|
103
|
+
section.container li {
|
104
|
+
text-align: left;
|
105
|
+
text-transform: capitalize;
|
106
|
+
}
|
107
|
+
|
108
|
+
section.container ul {
|
109
|
+
background: rgba(255,255,255,.05);
|
110
|
+
border-radius: .225rem;
|
111
|
+
margin: .9rem 0 2.7rem;
|
112
|
+
padding: 1rem 1.5rem;
|
113
|
+
}
|
114
|
+
|
115
|
+
.side-nav-item.current:after {
|
116
|
+
content: ' -';
|
117
|
+
}
|
118
|
+
|
119
|
+
nav {
|
120
|
+
font-family: "Roboto Condensed", sans-serif;
|
121
|
+
font-size: 1.2rem;
|
122
|
+
text-align: left;
|
123
|
+
}
|
124
|
+
|
125
|
+
.label--add {
|
126
|
+
color: #da139a;
|
127
|
+
text-transform: capitalize;
|
128
|
+
}
|
129
|
+
|
130
|
+
.label--improve {
|
131
|
+
color: #79b;
|
132
|
+
text-transform: capitalize;
|
133
|
+
}
|
134
|
+
|
135
|
+
.label--removed {
|
136
|
+
color: #9e6565;
|
137
|
+
text-transform: capitalize;
|
138
|
+
}
|
139
|
+
|
140
|
+
.label--fixed {
|
141
|
+
color: #7b9;
|
142
|
+
text-transform: capitalize;
|
143
|
+
}
|
144
|
+
|
145
|
+
p {
|
146
|
+
color: #555;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* grid
|
150
|
+
------------------------------ */
|
151
|
+
|
152
|
+
.container {
|
153
|
+
margin: 0 auto;
|
154
|
+
max-width: 60rem;
|
155
|
+
position: relative;
|
156
|
+
width: 100%;
|
157
|
+
}
|
158
|
+
|
159
|
+
.row {
|
160
|
+
display: flex;
|
161
|
+
flex-direction: row;
|
162
|
+
margin: 0 -1.0rem;
|
163
|
+
}
|
164
|
+
|
165
|
+
.row .column {
|
166
|
+
margin-bottom: 2.7rem;
|
167
|
+
padding: 0 1.0rem;
|
168
|
+
width: 100%;
|
169
|
+
}
|
170
|
+
|
171
|
+
:root {
|
172
|
+
font-family: 'Roboto', sans-serif;
|
173
|
+
font-size: 15px;
|
174
|
+
height: 100%;
|
175
|
+
text-align: center;
|
176
|
+
}
|
177
|
+
|
178
|
+
::selection,
|
179
|
+
::-moz-selection {
|
180
|
+
background: rgba(0,0,0,.1);
|
181
|
+
}
|
182
|
+
|
183
|
+
a {
|
184
|
+
color: inherit;
|
185
|
+
text-decoration: none;
|
186
|
+
}
|
187
|
+
|
188
|
+
body {
|
189
|
+
line-height: 1.8rem;
|
190
|
+
height: 100%;
|
191
|
+
width: 100%;
|
192
|
+
}
|
193
|
+
|
194
|
+
code {
|
195
|
+
border: 1px solid #fff;
|
196
|
+
border-bottom: 2px solid #fff;
|
197
|
+
border-radius: 3px;
|
198
|
+
color: #ba9;
|
199
|
+
font-family: "Roboto Mono", monospace;
|
200
|
+
font-size: .85rem;
|
201
|
+
line-height: 0;
|
202
|
+
padding: .2rem .3rem .1rem;
|
203
|
+
white-space: nowrap;
|
204
|
+
}
|
205
|
+
|
206
|
+
h1, h2, h3 {
|
207
|
+
line-height: inherit;
|
208
|
+
font-size: inherit;
|
209
|
+
font-weight: normal;
|
210
|
+
margin: 0;
|
211
|
+
}
|
212
|
+
|
213
|
+
h1 {
|
214
|
+
font-family: "Abel", sans-serif;
|
215
|
+
}
|
216
|
+
|
217
|
+
h2 {
|
218
|
+
font-family: "Abel", sans-serif;
|
219
|
+
font-size: 1.8rem;
|
220
|
+
font-weight: 300;
|
221
|
+
margin-bottom: 4.5rem;
|
222
|
+
}
|
223
|
+
|
224
|
+
h3 {
|
225
|
+
font-family: "Roboto Condensed", sans-serif;
|
226
|
+
font-size: 1.2rem;
|
227
|
+
}
|
228
|
+
|
229
|
+
hr {
|
230
|
+
border: none;
|
231
|
+
border-bottom: 1px solid #444;
|
232
|
+
}
|
233
|
+
|
234
|
+
kbd {
|
235
|
+
background: #fff;
|
236
|
+
border: 1px solid #ddd;
|
237
|
+
border-bottom: 2px solid #ddd;
|
238
|
+
border-radius: 3px;
|
239
|
+
font-family: "Roboto Mono", monospace;
|
240
|
+
font-size: .85rem;
|
241
|
+
line-height: 0;
|
242
|
+
padding: .2rem .3rem .1rem;
|
243
|
+
text-transform: capitalize;
|
244
|
+
white-space: nowrap;
|
245
|
+
}
|
246
|
+
|
247
|
+
p {
|
248
|
+
margin: 0;
|
249
|
+
}
|
250
|
+
|
251
|
+
p a {
|
252
|
+
text-decoration: underline;
|
253
|
+
}
|
254
|
+
|
255
|
+
ul {
|
256
|
+
list-style: none;
|
257
|
+
margin: 0;
|
258
|
+
padding: 0;
|
259
|
+
}
|
260
|
+
|
261
|
+
/* navigation
|
262
|
+
------------------------------ */
|
263
|
+
|
264
|
+
.nav-item {
|
265
|
+
display: inline-block;
|
266
|
+
}
|
267
|
+
|
268
|
+
.nav-item:before {
|
269
|
+
content: '\00a0 | \00a0';
|
270
|
+
}
|
271
|
+
|
272
|
+
.nav-item:first-child:before {
|
273
|
+
content: '';
|
274
|
+
}
|
275
|
+
|
276
|
+
</style>
|
277
|
+
</html>
|
@@ -0,0 +1,281 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
|
4
|
+
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
|
5
|
+
<meta content="<%= @project.author.delete(' ') %>/<%= @project.name %>" name="description" />
|
6
|
+
<meta content="width=device-width, initial-scale=1, user-scalable=no" name="viewport" />
|
7
|
+
|
8
|
+
<!-- Stylesheets -->
|
9
|
+
<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css' rel="stylesheet" type="text/css" />
|
10
|
+
<link href='https://fonts.googleapis.com/css?family=Abel|Roboto:400|Roboto+Condensed:400|Roboto+Mono:400' rel='stylesheet' type='text/css'>
|
11
|
+
|
12
|
+
<!-- Title -->
|
13
|
+
<title><%= @project.author.gsub(/\<.*?>/,"").chomp.strip! + '/' + @project.name %> - devist notes</title>
|
14
|
+
|
15
|
+
<body>
|
16
|
+
<header><h2>Release Notes - <%= @project.name %></h2></header>
|
17
|
+
<nav class="side-nav">
|
18
|
+
<ul style="background: none; color: #555; padding: 0; margin: 0;">
|
19
|
+
<li><a class="side-nav-item" href="<%= @project.homepage %>"><%= @project.author.gsub(/\<.*?\>/,"").chomp.strip! + '/' + @project.name %></a> </li>
|
20
|
+
<li><a style="font-size:12px;" class="side-nav-item " href="https://github.com/stacklog/devist">Created with devist</a></li>
|
21
|
+
</ul>
|
22
|
+
</nav>
|
23
|
+
|
24
|
+
<section class="container">
|
25
|
+
|
26
|
+
<% @changelog.each do |version| %>
|
27
|
+
|
28
|
+
<div id="<%= version.version %>">
|
29
|
+
<a href="#<%= version.version %>"><h3>Version <%= version.version %></h3></a>
|
30
|
+
<p><%= version.date.to_s %></p>
|
31
|
+
|
32
|
+
<ul>
|
33
|
+
|
34
|
+
<% version.tags.get[0].each do |added| %>
|
35
|
+
<li><span class="label label--add">added</span> <%= added.chomp %></li>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<% version.tags.get[1].each do |fixed| %>
|
39
|
+
<li><span class="label label--fixed">fixed</span> <%= fixed.chomp %></li>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<% version.tags.get[2].each do |removed| %>
|
43
|
+
<li><span class="label label--removed">removed</span> <%= removed.chomp %></li>
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
<% version.tags.get[3].each do |improved| %>
|
47
|
+
<li><span class="label label--improve">improved</span> <%= improved.chomp %></li>
|
48
|
+
<% end %>
|
49
|
+
|
50
|
+
</ul>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
<% end %>
|
54
|
+
|
55
|
+
</section>
|
56
|
+
|
57
|
+
|
58
|
+
</body>
|
59
|
+
|
60
|
+
<!-- Custom style -->
|
61
|
+
<style>
|
62
|
+
|
63
|
+
:root {
|
64
|
+
background: #ffefef;
|
65
|
+
color: #777575;
|
66
|
+
}
|
67
|
+
|
68
|
+
.label {
|
69
|
+
background: rgba(41, 41, 41, 0.81);
|
70
|
+
padding: 3px;
|
71
|
+
border-radius: 5px;
|
72
|
+
}
|
73
|
+
|
74
|
+
code {
|
75
|
+
border-color: rgba(255,255,255,0);;
|
76
|
+
}
|
77
|
+
|
78
|
+
h2 {
|
79
|
+
color: #635b5b;
|
80
|
+
}
|
81
|
+
|
82
|
+
header {
|
83
|
+
padding-top: 4.5rem;
|
84
|
+
}
|
85
|
+
|
86
|
+
kbd {
|
87
|
+
background: rgba(255,255,255,.1);
|
88
|
+
border-color: rgba(0,0,0,.35);;
|
89
|
+
}
|
90
|
+
|
91
|
+
nav {
|
92
|
+
position: fixed;
|
93
|
+
left: 1.8rem;
|
94
|
+
top: 1.8rem;
|
95
|
+
}
|
96
|
+
|
97
|
+
section.container {
|
98
|
+
margin-bottom: 7.2rem;
|
99
|
+
max-width: 30rem;
|
100
|
+
}
|
101
|
+
|
102
|
+
section.container li {
|
103
|
+
text-align: left;
|
104
|
+
text-transform: capitalize;
|
105
|
+
}
|
106
|
+
|
107
|
+
section.container ul {
|
108
|
+
background: rgba(230, 230, 230, 0.48);
|
109
|
+
border-radius: .225rem;
|
110
|
+
margin: .9rem 0 2.7rem;
|
111
|
+
padding: 1rem 1.5rem;
|
112
|
+
}
|
113
|
+
|
114
|
+
.side-nav-item.current:after {
|
115
|
+
content: ' -';
|
116
|
+
}
|
117
|
+
|
118
|
+
nav {
|
119
|
+
font-family: "Roboto Condensed", sans-serif;
|
120
|
+
font-size: 1.2rem;
|
121
|
+
text-align: left;
|
122
|
+
}
|
123
|
+
|
124
|
+
.label--add {
|
125
|
+
color: #cfd0c5;
|
126
|
+
text-transform: capitalize;
|
127
|
+
}
|
128
|
+
|
129
|
+
.label--improve {
|
130
|
+
color: #79b;
|
131
|
+
text-transform: capitalize;
|
132
|
+
}
|
133
|
+
|
134
|
+
.label--removed {
|
135
|
+
color: #e46f6f;
|
136
|
+
text-transform: capitalize;
|
137
|
+
}
|
138
|
+
|
139
|
+
.label--fixed {
|
140
|
+
color: #7b9;
|
141
|
+
text-transform: capitalize;
|
142
|
+
}
|
143
|
+
|
144
|
+
p {
|
145
|
+
color: #555;
|
146
|
+
}
|
147
|
+
|
148
|
+
/* grid
|
149
|
+
------------------------------ */
|
150
|
+
|
151
|
+
.container {
|
152
|
+
margin: 0 auto;
|
153
|
+
max-width: 60rem;
|
154
|
+
position: relative;
|
155
|
+
width: 100%;
|
156
|
+
}
|
157
|
+
|
158
|
+
.row {
|
159
|
+
display: flex;
|
160
|
+
flex-direction: row;
|
161
|
+
margin: 0 -1.0rem;
|
162
|
+
}
|
163
|
+
|
164
|
+
.row .column {
|
165
|
+
margin-bottom: 2.7rem;
|
166
|
+
padding: 0 1.0rem;
|
167
|
+
width: 100%;
|
168
|
+
}
|
169
|
+
|
170
|
+
:root {
|
171
|
+
font-family: 'Roboto', sans-serif;
|
172
|
+
font-size: 15px;
|
173
|
+
height: 100%;
|
174
|
+
text-align: center;
|
175
|
+
}
|
176
|
+
|
177
|
+
::selection,
|
178
|
+
::-moz-selection {
|
179
|
+
background: rgba(0,0,0,.1);
|
180
|
+
}
|
181
|
+
|
182
|
+
a {
|
183
|
+
color: inherit;
|
184
|
+
text-decoration: none;
|
185
|
+
}
|
186
|
+
|
187
|
+
body {
|
188
|
+
line-height: 1.8rem;
|
189
|
+
height: 100%;
|
190
|
+
width: 100%;
|
191
|
+
}
|
192
|
+
|
193
|
+
code {
|
194
|
+
border: 1px solid #fff;
|
195
|
+
border-bottom: 2px solid #fff;
|
196
|
+
border-radius: 3px;
|
197
|
+
color: #ba9;
|
198
|
+
font-family: "Roboto Mono", monospace;
|
199
|
+
font-size: .85rem;
|
200
|
+
line-height: 0;
|
201
|
+
padding: .2rem .3rem .1rem;
|
202
|
+
white-space: nowrap;
|
203
|
+
}
|
204
|
+
|
205
|
+
h1, h2, h3 {
|
206
|
+
line-height: inherit;
|
207
|
+
font-size: inherit;
|
208
|
+
font-weight: normal;
|
209
|
+
margin: 0;
|
210
|
+
}
|
211
|
+
|
212
|
+
h1 {
|
213
|
+
font-family: "Abel", sans-serif;
|
214
|
+
}
|
215
|
+
|
216
|
+
h2 {
|
217
|
+
font-family: "Abel", sans-serif;
|
218
|
+
font-size: 1.8rem;
|
219
|
+
font-weight: 300;
|
220
|
+
margin-bottom: 4.5rem;
|
221
|
+
}
|
222
|
+
|
223
|
+
h3 {
|
224
|
+
color: black;
|
225
|
+
font-family: "Roboto Condensed", sans-serif;
|
226
|
+
font-size: 1.2rem;
|
227
|
+
}
|
228
|
+
|
229
|
+
hr {
|
230
|
+
border: none;
|
231
|
+
border-bottom: 1px solid #444;
|
232
|
+
}
|
233
|
+
|
234
|
+
kbd {
|
235
|
+
background: #fff;
|
236
|
+
border: 1px solid #ddd;
|
237
|
+
border-bottom: 2px solid #ddd;
|
238
|
+
border-radius: 3px;
|
239
|
+
font-family: "Roboto Mono", monospace;
|
240
|
+
font-size: .85rem;
|
241
|
+
line-height: 0;
|
242
|
+
padding: .2rem .3rem .1rem;
|
243
|
+
text-transform: capitalize;
|
244
|
+
white-space: nowrap;
|
245
|
+
}
|
246
|
+
|
247
|
+
p {
|
248
|
+
margin: 0;
|
249
|
+
}
|
250
|
+
|
251
|
+
p a {
|
252
|
+
text-decoration: underline;
|
253
|
+
}
|
254
|
+
|
255
|
+
ul {
|
256
|
+
list-style: none;
|
257
|
+
margin: 0;
|
258
|
+
padding: 0;
|
259
|
+
}
|
260
|
+
|
261
|
+
il {
|
262
|
+
color: #644190;
|
263
|
+
}
|
264
|
+
|
265
|
+
/* navigation
|
266
|
+
------------------------------ */
|
267
|
+
|
268
|
+
.nav-item {
|
269
|
+
display: inline-block;
|
270
|
+
}
|
271
|
+
|
272
|
+
.nav-item:before {
|
273
|
+
content: '\00a0 | \00a0';
|
274
|
+
}
|
275
|
+
|
276
|
+
.nav-item:first-child:before {
|
277
|
+
content: '';
|
278
|
+
}
|
279
|
+
|
280
|
+
</style>
|
281
|
+
</html>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# extractor.rb
|
2
|
+
# This file is a part of the devist package.
|
3
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
4
|
+
#
|
5
|
+
# The extractor class extracts all necessary elements
|
6
|
+
# from the line, or changelog revision. All methods
|
7
|
+
# shall be static.
|
8
|
+
class Devist::Extractor
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def extract_version(line)
|
13
|
+
line[/n (.*) of/, 1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def extract_change(line)
|
17
|
+
line.split(': ')[-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract_info(line)
|
21
|
+
line.split(': ')[-1]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# project.rb
|
2
|
+
# This file is a part of the devist package.
|
3
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
4
|
+
#
|
5
|
+
# Create a project instance model. This allows us to reuse
|
6
|
+
# project name, author and homepage whenever we want. The
|
7
|
+
# object is not necessary to be created.
|
8
|
+
class Devist::Project
|
9
|
+
|
10
|
+
attr_accessor :name, :author, :homepage
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# tags.rb:Marray
|
2
|
+
#
|
3
|
+
# This is the helper class that allows us to
|
4
|
+
# recreate a multidimensional array.
|
5
|
+
class Marray < Array
|
6
|
+
def [](i)
|
7
|
+
super.nil? ? self[i] = Marray.new : super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# tags.rb
|
12
|
+
# This file is a part of the devist package.
|
13
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
14
|
+
#
|
15
|
+
# Model class `tags` is dynamic object that have parent
|
16
|
+
# `version` and is made of version tag info.
|
17
|
+
class Devist::Tag
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@list = %w[added fixed removed improved]
|
21
|
+
@change = Marray.new(@list.count)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add (type, change)
|
25
|
+
@change[@list.index(type)][@change[@list.index(type)].count] = change
|
26
|
+
end
|
27
|
+
|
28
|
+
def get
|
29
|
+
@change
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# version.rb
|
2
|
+
# This file is a part of the devist package.
|
3
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
4
|
+
#
|
5
|
+
# Model class `version` is dynamic object that creates a
|
6
|
+
# new changelog version instance with appropriate tag(s)
|
7
|
+
# data.
|
8
|
+
class Devist::Version
|
9
|
+
|
10
|
+
attr_accessor :version, :date, :tags
|
11
|
+
|
12
|
+
def initialize(version, date)
|
13
|
+
@version = version
|
14
|
+
@date = date
|
15
|
+
@tags = Devist::Tag.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create a new tag.
|
19
|
+
def tag (type, change)
|
20
|
+
@tags.add(type, change)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'devist/models/tags'
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
# parser.rb
|
4
|
+
# This file is a part of the devist package.
|
5
|
+
# Halis Duraki <duraki.halis@nsoft.ba>
|
6
|
+
#
|
7
|
+
# Parser will allow a building routine for the given
|
8
|
+
# changelog by investigating every line in the file.
|
9
|
+
# The Parser created project info, and build changelog,
|
10
|
+
# but it also check if the given file is proper devist
|
11
|
+
# format.
|
12
|
+
class Devist::Parser
|
13
|
+
|
14
|
+
attr_reader :project, :changelog
|
15
|
+
|
16
|
+
# Project builder.
|
17
|
+
def build_info(line)
|
18
|
+
case line
|
19
|
+
when /@project:+/
|
20
|
+
@project.name = Devist::Extractor.extract_info(line)
|
21
|
+
print " * Extracting project name ... [#{@project.name.chomp.strip!}]\n"
|
22
|
+
when /@author:.+/
|
23
|
+
@project.author = Devist::Extractor.extract_info(line)
|
24
|
+
print " * Extracting project author ... [#{@project.author.chomp.strip!}]\n"
|
25
|
+
when /@homepage:.+/
|
26
|
+
@project.homepage = Devist::Extractor.extract_info(line)
|
27
|
+
print " * Extracting project homepage ... [#{@project.homepage.chomp.strip!}]\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Changelog builder.
|
32
|
+
def build_changelog(line)
|
33
|
+
build_version(line)
|
34
|
+
build_tags(line)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Build tags.
|
38
|
+
def build_tags(line)
|
39
|
+
case line
|
40
|
+
when /#added.+/
|
41
|
+
@changelog[@version].tag 'added', Devist::Extractor.extract_change(line)
|
42
|
+
when /#fixed.+/
|
43
|
+
@changelog[@version].tag 'fixed', Devist::Extractor.extract_change(line)
|
44
|
+
when /#removed.+/
|
45
|
+
@changelog[@version].tag 'removed', Devist::Extractor.extract_change(line)
|
46
|
+
when /#improved.+/
|
47
|
+
@changelog[@version].tag 'improved', Devist::Extractor.extract_change(line)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Build version.
|
52
|
+
def build_version(line)
|
53
|
+
case line
|
54
|
+
when /### Version+/
|
55
|
+
@date = Date.parse(line) # Extract version date
|
56
|
+
@version += 1 # Increment version
|
57
|
+
@changelog[@version] = Devist::Version.new (Devist::Extractor.extract_version line), @date
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Is file devist configured.
|
62
|
+
def devist?(file_name)
|
63
|
+
is_devist = File.open(file_name).to_a
|
64
|
+
|
65
|
+
if is_devist.last.equal?("")
|
66
|
+
is_devist.pop is_devist.last
|
67
|
+
end
|
68
|
+
|
69
|
+
print " * Checking if changelog is devist configured ...\n"
|
70
|
+
if is_devist.last.chomp != '.devist'
|
71
|
+
print " * The file is not configured for devist. Are you missing .devist at the end of the file?\n"
|
72
|
+
print " * Skipping ...\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
print " * Found .devist signature.\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Line parser.
|
79
|
+
def parse_data(file_name)
|
80
|
+
@project = Devist::Project.new
|
81
|
+
@changelog = []
|
82
|
+
@version = -1 # Start from 0
|
83
|
+
|
84
|
+
devist?(file_name) # Check if file is configured for usage
|
85
|
+
|
86
|
+
print " * Building model from file data ...\n"
|
87
|
+
|
88
|
+
File.foreach(file_name) do |line|
|
89
|
+
build_info(line) # Build project info
|
90
|
+
build_changelog(line) # Build changelog data
|
91
|
+
end
|
92
|
+
|
93
|
+
@changelog
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'devist/extractor'
|
99
|
+
require 'devist/models/project'
|
100
|
+
require 'devist/models/tags'
|
101
|
+
require 'devist/models/version'
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devist
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Halis Duraki
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A Ruby gem that will help you export your Markdown changelog into beautiful
|
14
|
+
HTML pages.
|
15
|
+
email: duraki.halis@nosft.ba
|
16
|
+
executables:
|
17
|
+
- devist
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/devist
|
22
|
+
- lib/devist.rb
|
23
|
+
- lib/devist/compiler.rb
|
24
|
+
- lib/devist/export/html/_default.html.erb
|
25
|
+
- lib/devist/export/html/_polar.html.erb
|
26
|
+
- lib/devist/extractor.rb
|
27
|
+
- lib/devist/models/project.rb
|
28
|
+
- lib/devist/models/tags.rb
|
29
|
+
- lib/devist/models/version.rb
|
30
|
+
- lib/devist/parser.rb
|
31
|
+
homepage: https://github.com/stacklog/devist
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.5.1
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Generate beautiful changelogs!
|
55
|
+
test_files: []
|