gemstar 1.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +6 -0
- data/lib/gemstar/cli.rb +1 -0
- data/lib/gemstar/commands/diff.rb +26 -3
- data/lib/gemstar/outputs/markdown.rb +165 -0
- data/lib/gemstar/version.rb +1 -1
- data/lib/gemstar.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c795357616121ccda76004c6fefa92194688472dd45950396c24a1924cf70a6
|
|
4
|
+
data.tar.gz: d4922547dc024c923d751164698da5b672c48fd4b2e61384345e0de24b1f5430
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6caf7c992dcccd533da3cbcf8ed1e98ee44538faffbdf43c55e1ecc1af899497c70100c62e1ba5bbe9331fba6c78d2a65a571c860f4035b99dc63f44d94cb682
|
|
7
|
+
data.tar.gz: 88e7f1557e01332a6d5dbd69301b0fcfb30548a80333d835476994a9b9ae92a5fdb8559bfa268d242ad11eaad266b432947277d52b185e40dcc66f166ee01854
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.0.1
|
|
6
|
+
|
|
7
|
+
- Added `--format markdown` to `gemstar diff` command.
|
|
8
|
+
|
|
9
|
+
## 1.0
|
|
10
|
+
|
|
5
11
|
- Added `gemstar server`, your interactive Gemfile.lock explorer and more.
|
|
6
12
|
- Default location for `diff` is now a tmp file.
|
|
7
13
|
- Removed Railtie from this gem.
|
data/README.md
CHANGED
|
@@ -60,6 +60,12 @@ To examine a specific Gemfile.lock, pass it like this:
|
|
|
60
60
|
gemstar diff --lockfile=~/MyProject/Gemfile.lock
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
To write markdown instead of html:
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
gemstar diff --format markdown
|
|
67
|
+
```
|
|
68
|
+
|
|
63
69
|
## Contributing
|
|
64
70
|
|
|
65
71
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/FDj/gemstar](https://github.com/FDj/gemstar).
|
data/lib/gemstar/cli.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Gemstar
|
|
|
13
13
|
desc "diff", "Show changelogs for updated gems"
|
|
14
14
|
method_option :from, type: :string, desc: "Git ref or lockfile"
|
|
15
15
|
method_option :to, type: :string, desc: "Git ref or lockfile"
|
|
16
|
+
method_option :format, type: :string, desc: "Output format (html or markdown)"
|
|
16
17
|
method_option :output_file, type: :string, desc: "Output file path"
|
|
17
18
|
method_option :debug_gem_regex, type: :string, desc: "Debug matching gems", hide: true
|
|
18
19
|
def diff
|
|
@@ -15,6 +15,7 @@ module Gemstar
|
|
|
15
15
|
attr_reader :git_repo
|
|
16
16
|
attr_reader :lockfile_full_path
|
|
17
17
|
attr_reader :output_file
|
|
18
|
+
attr_reader :output_format
|
|
18
19
|
|
|
19
20
|
def initialize(options)
|
|
20
21
|
super
|
|
@@ -24,7 +25,8 @@ module Gemstar
|
|
|
24
25
|
@from = options[:from] || "HEAD"
|
|
25
26
|
@to = options[:to]
|
|
26
27
|
@lockfile = options[:lockfile] || "Gemfile.lock"
|
|
27
|
-
@
|
|
28
|
+
@output_format = normalize_output_format(options[:format] || options[:output_format])
|
|
29
|
+
@output_file = options[:output_file] || default_output_file
|
|
28
30
|
|
|
29
31
|
@git_repo = Gemstar::GitRepo.new(File.dirname(@lockfile))
|
|
30
32
|
end
|
|
@@ -44,8 +46,8 @@ module Gemstar
|
|
|
44
46
|
|
|
45
47
|
collect_updates(new_lockfile: new, old_lockfile: old)
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
File.write(output_file,
|
|
49
|
+
rendered_output = output_renderer.render_diff(self)
|
|
50
|
+
File.write(output_file, rendered_output)
|
|
49
51
|
puts "✅ Changelog report created: #{File.expand_path(output_file)}"
|
|
50
52
|
|
|
51
53
|
if failed.any?
|
|
@@ -56,6 +58,27 @@ module Gemstar
|
|
|
56
58
|
|
|
57
59
|
private
|
|
58
60
|
|
|
61
|
+
def normalize_output_format(value)
|
|
62
|
+
format = value.to_s.strip.downcase
|
|
63
|
+
return :markdown if %w[md markdown].include?(format)
|
|
64
|
+
|
|
65
|
+
:html
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def default_output_file
|
|
69
|
+
extension = output_format == :markdown ? "md" : "html"
|
|
70
|
+
File.join(Dir.tmpdir, "gem_update_changelog.#{extension}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def output_renderer
|
|
74
|
+
@output_renderer ||= case output_format
|
|
75
|
+
when :markdown
|
|
76
|
+
Outputs::Markdown.new
|
|
77
|
+
else
|
|
78
|
+
Outputs::HTML.new
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
59
82
|
def build_entry(gem_name:, old_version:, new_version:)
|
|
60
83
|
metadata = Gemstar::RubyGemsMetadata.new(gem_name)
|
|
61
84
|
repo_url = metadata.repo_uri
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "basic"
|
|
4
|
+
require "pathname"
|
|
5
|
+
require "cgi"
|
|
6
|
+
require "nokogiri"
|
|
7
|
+
|
|
8
|
+
module Gemstar
|
|
9
|
+
module Outputs
|
|
10
|
+
class Markdown < Basic
|
|
11
|
+
def render_diff(diff_command)
|
|
12
|
+
project_name = Pathname.getwd.basename.to_s
|
|
13
|
+
body = diff_command.updates.sort.map do |gem_name, info|
|
|
14
|
+
render_entry(gem_name, info)
|
|
15
|
+
end.join("\n\n---\n\n")
|
|
16
|
+
|
|
17
|
+
<<~MARKDOWN
|
|
18
|
+
# #{project_name}: Gem Updates
|
|
19
|
+
|
|
20
|
+
_Showing changes from #{diff_command.from} to #{diff_command.to || "now"}, generated on #{Time.now.strftime("%Y-%m-%d %H:%M:%S %z")}._
|
|
21
|
+
|
|
22
|
+
#{body}
|
|
23
|
+
MARKDOWN
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def render_entry(gem_name, info)
|
|
29
|
+
title = if info[:homepage_url]
|
|
30
|
+
"## [#{gem_name}](#{info[:homepage_url]})"
|
|
31
|
+
else
|
|
32
|
+
"## #{gem_name}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
parts = []
|
|
36
|
+
parts << title
|
|
37
|
+
parts << ""
|
|
38
|
+
parts << "*#{info[:old] || "new"} → #{info[:new]}*"
|
|
39
|
+
parts << ""
|
|
40
|
+
parts << info[:description].to_s unless info[:description].to_s.empty?
|
|
41
|
+
parts << "" unless info[:description].to_s.empty?
|
|
42
|
+
|
|
43
|
+
parts.concat(link_lines(info))
|
|
44
|
+
|
|
45
|
+
if info[:sections]
|
|
46
|
+
parts << render_sections(info[:sections])
|
|
47
|
+
elsif info[:release_urls]
|
|
48
|
+
parts << "No changelog entries found for this version."
|
|
49
|
+
else
|
|
50
|
+
parts << "No changelog entries found."
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
parts.join("\n").strip
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def link_lines(info)
|
|
57
|
+
lines = []
|
|
58
|
+
lines << "[Compare changes](#{info[:compare_url]})" if info[:compare_url]
|
|
59
|
+
lines << "[View all GitHub release notes](#{info[:release_page]})" if info[:release_page]
|
|
60
|
+
lines << "" unless lines.empty?
|
|
61
|
+
lines
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def render_sections(sections)
|
|
65
|
+
sections.map do |_version, lines|
|
|
66
|
+
Array(lines).flatten.map { |chunk| markdownize_chunk(chunk) }.join
|
|
67
|
+
end.join("\n\n").strip
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def markdownize_chunk(chunk)
|
|
71
|
+
text = chunk.to_s
|
|
72
|
+
return text unless html_fragment?(text)
|
|
73
|
+
|
|
74
|
+
fragment = Nokogiri::HTML::DocumentFragment.parse(text)
|
|
75
|
+
markdown = fragment.children.map { |node| node_to_markdown(node) }.join
|
|
76
|
+
markdown.gsub(/\n{3,}/, "\n\n").strip + "\n"
|
|
77
|
+
rescue StandardError
|
|
78
|
+
text
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def html_fragment?(text)
|
|
82
|
+
text.match?(%r{</?[a-z][^>]*>}i)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def node_to_markdown(node, list_depth: 0)
|
|
86
|
+
return CGI.unescapeHTML(node.text) if node.text?
|
|
87
|
+
return "" unless node.element?
|
|
88
|
+
|
|
89
|
+
case node.name
|
|
90
|
+
when "p"
|
|
91
|
+
"#{inline_children(node).strip}\n\n"
|
|
92
|
+
when "br"
|
|
93
|
+
" \n"
|
|
94
|
+
when "strong", "b"
|
|
95
|
+
"**#{inline_children(node).strip}**"
|
|
96
|
+
when "em", "i"
|
|
97
|
+
"*#{inline_children(node).strip}*"
|
|
98
|
+
when "code"
|
|
99
|
+
if node.ancestors.any? { |ancestor| ancestor.name == "pre" }
|
|
100
|
+
CGI.unescapeHTML(node.text)
|
|
101
|
+
else
|
|
102
|
+
"`#{CGI.unescapeHTML(node.text)}`"
|
|
103
|
+
end
|
|
104
|
+
when "pre"
|
|
105
|
+
code = CGI.unescapeHTML(node.text).rstrip
|
|
106
|
+
"```\n#{code}\n```\n\n"
|
|
107
|
+
when "a"
|
|
108
|
+
text = inline_children(node).strip
|
|
109
|
+
href = node["href"].to_s
|
|
110
|
+
return text if href.empty?
|
|
111
|
+
|
|
112
|
+
"[#{text.empty? ? href : text}](#{href})"
|
|
113
|
+
when "ul"
|
|
114
|
+
list_children(node, ordered: false, list_depth: list_depth)
|
|
115
|
+
when "ol"
|
|
116
|
+
list_children(node, ordered: true, list_depth: list_depth)
|
|
117
|
+
when "li"
|
|
118
|
+
"#{inline_children(node).strip}\n"
|
|
119
|
+
when /\Ah[1-6]\z/
|
|
120
|
+
level = node.name.delete_prefix("h").to_i
|
|
121
|
+
"#{"#" * level} #{inline_children(node).strip}\n\n"
|
|
122
|
+
when "blockquote"
|
|
123
|
+
quote = inline_children(node).strip.lines.map { |line| "> #{line.rstrip}" }.join("\n")
|
|
124
|
+
"#{quote}\n\n"
|
|
125
|
+
when "hr"
|
|
126
|
+
"\n---\n\n"
|
|
127
|
+
else
|
|
128
|
+
children_to_markdown(node, list_depth: list_depth)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def children_to_markdown(node, list_depth: 0)
|
|
133
|
+
node.children.map { |child| node_to_markdown(child, list_depth: list_depth) }.join
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def inline_children(node)
|
|
137
|
+
children_to_markdown(node).gsub(/\s+/, " ").strip
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def list_children(node, ordered:, list_depth:)
|
|
141
|
+
index = 0
|
|
142
|
+
|
|
143
|
+
rendered = node.element_children.filter_map do |child|
|
|
144
|
+
next unless child.name == "li"
|
|
145
|
+
|
|
146
|
+
index += 1
|
|
147
|
+
marker = ordered ? "#{index}." : "-"
|
|
148
|
+
prefix = "#{" " * list_depth}#{marker} "
|
|
149
|
+
|
|
150
|
+
item_parts = child.children.map do |grandchild|
|
|
151
|
+
if %w[ul ol].include?(grandchild.name)
|
|
152
|
+
"\n" + list_children(grandchild, ordered: grandchild.name == "ol", list_depth: list_depth + 1).rstrip
|
|
153
|
+
else
|
|
154
|
+
node_to_markdown(grandchild, list_depth: list_depth + 1)
|
|
155
|
+
end
|
|
156
|
+
end.join
|
|
157
|
+
|
|
158
|
+
"#{prefix}#{item_parts.strip}\n"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
rendered.join + "\n"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
data/lib/gemstar/version.rb
CHANGED
data/lib/gemstar.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gemstar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Dejako
|
|
@@ -234,6 +234,7 @@ files:
|
|
|
234
234
|
- lib/gemstar/lock_file.rb
|
|
235
235
|
- lib/gemstar/outputs/basic.rb
|
|
236
236
|
- lib/gemstar/outputs/html.rb
|
|
237
|
+
- lib/gemstar/outputs/markdown.rb
|
|
237
238
|
- lib/gemstar/project.rb
|
|
238
239
|
- lib/gemstar/remote_repository.rb
|
|
239
240
|
- lib/gemstar/request_logger.rb
|