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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65596efbc0ff5c10ad950ca3cc8bb19e9ca9030db58b73d046ebce447200aa07
4
- data.tar.gz: e5ab0cbb7f3db4b3d9949011f3b6446aecb725ee1863fd61dd196660332606de
3
+ metadata.gz: 4c795357616121ccda76004c6fefa92194688472dd45950396c24a1924cf70a6
4
+ data.tar.gz: d4922547dc024c923d751164698da5b672c48fd4b2e61384345e0de24b1f5430
5
5
  SHA512:
6
- metadata.gz: ef75cdf526345f289eff5f0a8bff44520eb1d0aea55eff617de71d585832721655fd70c680e0e27ef7584c15dd4f304d9ee3b079c70fbafc7e4ccc5c8360c4ad
7
- data.tar.gz: 13e3d871e1772c7f8ec183a3801504123896e8bd5365bba2a3e6fae5668415dcf2a40ef29f3494a2e9bef47251a3b913c37ae122879c3e8367cba9cf4490af1e
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
- @output_file = options[:output_file] || File.join(Dir.tmpdir, "gem_update_changelog.html")
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
- html = Outputs::HTML.new.render_diff(self)
48
- File.write(output_file, html)
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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gemstar # :nodoc:
4
- VERSION = "1.0"
4
+ VERSION = "1.0.1"
5
5
 
6
6
  def self.debug?
7
7
  return @debug if defined?(@debug)
data/lib/gemstar.rb CHANGED
@@ -11,6 +11,7 @@ require "gemstar/commands/server"
11
11
  require "gemstar/config"
12
12
  require "gemstar/outputs/basic"
13
13
  require "gemstar/outputs/html"
14
+ require "gemstar/outputs/markdown"
14
15
  require "gemstar/cache"
15
16
  require "gemstar/change_log"
16
17
  require "gemstar/git_hub"
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: '1.0'
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