milestoner 17.11.0 → 18.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.adoc +183 -79
  4. data/lib/milestoner/builders/ascii_doc.rb +30 -10
  5. data/lib/milestoner/builders/container.rb +1 -0
  6. data/lib/milestoner/builders/feed.rb +46 -0
  7. data/lib/milestoner/builders/markdown.rb +30 -10
  8. data/lib/milestoner/builders/stream.rb +19 -8
  9. data/lib/milestoner/builders/web.rb +46 -16
  10. data/lib/milestoner/cli/actions/build/basename.rb +27 -0
  11. data/lib/milestoner/cli/actions/build/format.rb +4 -4
  12. data/lib/milestoner/cli/actions/build/label.rb +3 -3
  13. data/lib/milestoner/cli/actions/build/layout.rb +3 -3
  14. data/lib/milestoner/cli/actions/build/max.rb +24 -0
  15. data/lib/milestoner/cli/actions/build/root.rb +3 -3
  16. data/lib/milestoner/cli/actions/build/stylesheet.rb +36 -0
  17. data/lib/milestoner/cli/actions/build/tail.rb +26 -0
  18. data/lib/milestoner/cli/actions/build/version.rb +7 -3
  19. data/lib/milestoner/cli/actions/cache/delete.rb +1 -1
  20. data/lib/milestoner/cli/actions/cache/find.rb +2 -2
  21. data/lib/milestoner/cli/actions/cache/info.rb +1 -1
  22. data/lib/milestoner/cli/actions/cache/list.rb +2 -2
  23. data/lib/milestoner/cli/actions/next.rb +2 -2
  24. data/lib/milestoner/cli/actions/publish.rb +19 -4
  25. data/lib/milestoner/cli/commands/build.rb +15 -25
  26. data/lib/milestoner/cli/commands/cache.rb +1 -1
  27. data/lib/milestoner/commits/categorizer.rb +2 -2
  28. data/lib/milestoner/commits/enricher.rb +1 -1
  29. data/lib/milestoner/commits/enrichers/body.rb +2 -2
  30. data/lib/milestoner/commits/enrichers/format.rb +2 -2
  31. data/lib/milestoner/commits/enrichers/issue.rb +2 -2
  32. data/lib/milestoner/commits/enrichers/milestone.rb +1 -1
  33. data/lib/milestoner/commits/enrichers/note.rb +2 -2
  34. data/lib/milestoner/commits/enrichers/review.rb +2 -2
  35. data/lib/milestoner/commits/enrichers/uri.rb +2 -2
  36. data/lib/milestoner/commits/tagger.rb +78 -0
  37. data/lib/milestoner/configuration/contract.rb +30 -8
  38. data/lib/milestoner/configuration/defaults.yml +49 -24
  39. data/lib/milestoner/configuration/model.rb +17 -8
  40. data/lib/milestoner/configuration/transformers/build/template_paths.rb +4 -4
  41. data/lib/milestoner/configuration/transformers/citations/description.rb +5 -3
  42. data/lib/milestoner/configuration/transformers/citations/label.rb +2 -3
  43. data/lib/milestoner/configuration/transformers/citations/uri.rb +48 -0
  44. data/lib/milestoner/configuration/transformers/gems/description.rb +8 -2
  45. data/lib/milestoner/configuration/transformers/gems/label.rb +6 -3
  46. data/lib/milestoner/configuration/transformers/gems/name.rb +8 -2
  47. data/lib/milestoner/configuration/transformers/gems/uri.rb +12 -3
  48. data/lib/milestoner/configuration/transformers/generator/label.rb +2 -2
  49. data/lib/milestoner/configuration/transformers/generator/uri.rb +2 -2
  50. data/lib/milestoner/configuration/transformers/generator/version.rb +2 -2
  51. data/lib/milestoner/configuration/transformers/project/author.rb +5 -2
  52. data/lib/milestoner/configuration/transformers/project/label.rb +2 -2
  53. data/lib/milestoner/configuration/transformers/project/version.rb +2 -2
  54. data/lib/milestoner/configuration/transformers/syndication/link.rb +33 -0
  55. data/lib/milestoner/container.rb +20 -16
  56. data/lib/milestoner/models/commit.rb +2 -0
  57. data/lib/milestoner/models/tag.rb +8 -0
  58. data/lib/milestoner/renderers/universal.rb +2 -2
  59. data/lib/milestoner/syndication/builder.rb +120 -0
  60. data/lib/milestoner/syndication/refine.rb +25 -0
  61. data/lib/milestoner/syndication/shared.rb +25 -0
  62. data/lib/milestoner/tags/creator.rb +11 -33
  63. data/lib/milestoner/tags/publisher.rb +13 -5
  64. data/lib/milestoner/tags/pusher.rb +22 -13
  65. data/lib/milestoner/templates/layouts/page.html.erb +7 -1
  66. data/lib/milestoner/templates/layouts/page.xml.erb +11 -0
  67. data/lib/milestoner/templates/milestones/_avatar.adoc.erb +1 -0
  68. data/lib/milestoner/templates/milestones/_avatar.html.erb +1 -1
  69. data/lib/milestoner/templates/milestones/_avatar.md.erb +2 -0
  70. data/lib/milestoner/templates/milestones/_avatar.xml.erb +5 -0
  71. data/lib/milestoner/templates/milestones/_commit.adoc.erb +1 -1
  72. data/lib/milestoner/templates/milestones/_commit.html.erb +18 -8
  73. data/lib/milestoner/templates/milestones/_commit.md.erb +1 -1
  74. data/lib/milestoner/templates/milestones/_commit.xml.erb +84 -0
  75. data/lib/milestoner/templates/milestones/_icon.adoc.erb +1 -0
  76. data/lib/milestoner/templates/milestones/_icon.html.erb +2 -2
  77. data/lib/milestoner/templates/milestones/_icon.md.erb +5 -0
  78. data/lib/milestoner/templates/milestones/_icon.xml.erb +5 -0
  79. data/lib/milestoner/templates/milestones/_profile.adoc.erb +1 -0
  80. data/lib/milestoner/templates/milestones/_profile.md.erb +1 -0
  81. data/lib/milestoner/templates/milestones/_profile.xml.erb +5 -0
  82. data/lib/milestoner/templates/milestones/_tag.adoc.erb +13 -0
  83. data/lib/milestoner/templates/milestones/_tag.html.erb +82 -0
  84. data/lib/milestoner/templates/milestones/_tag.md.erb +18 -0
  85. data/lib/milestoner/templates/milestones/_tag.stream.erb +14 -0
  86. data/lib/milestoner/templates/milestones/_tag.xml.erb +27 -0
  87. data/lib/milestoner/templates/milestones/show.adoc.erb +1 -11
  88. data/lib/milestoner/templates/milestones/show.html.erb +1 -46
  89. data/lib/milestoner/templates/milestones/show.md.erb +1 -11
  90. data/lib/milestoner/templates/milestones/show.stream.erb +1 -11
  91. data/lib/milestoner/templates/milestones/show.xml.erb +1 -0
  92. data/lib/milestoner/templates/public/page.css.erb +111 -60
  93. data/lib/milestoner/views/context.rb +7 -4
  94. data/lib/milestoner/views/milestones/show.rb +4 -29
  95. data/lib/milestoner/views/parts/commit.rb +8 -8
  96. data/lib/milestoner/views/parts/tag.rb +51 -0
  97. data/milestoner.gemspec +8 -7
  98. data.tar.gz.sig +0 -0
  99. metadata +58 -22
  100. metadata.gz.sig +3 -3
  101. data/lib/milestoner/configuration/transformers/project/generator.rb +0 -35
  102. data/lib/milestoner/configuration/transformers/project/name.rb +0 -21
  103. data/lib/milestoner/configuration/transformers/uri/avatar.rb +0 -20
  104. data/lib/milestoner/configuration/transformers/uri/commit.rb +0 -23
  105. data/lib/milestoner/configuration/transformers/uri/profile.rb +0 -20
  106. data/lib/milestoner/configuration/transformers/uri/review.rb +0 -23
  107. data/lib/milestoner/configuration/transformers/uri/tracker.rb +0 -23
  108. data/lib/milestoner/error.rb +0 -7
@@ -9,6 +9,8 @@ module Milestoner
9
9
  body
10
10
  deletions
11
11
  files_changed
12
+ fingerprint
13
+ fingerprint_key
12
14
  insertions
13
15
  notes
14
16
  sha
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module Models
5
+ # Represents a Git tag comprised of multiple commits.
6
+ Tag = Struct.new :author, :commits, :committed_at, :sha, :signature, :version
7
+ end
8
+ end
@@ -4,14 +4,14 @@ module Milestoner
4
4
  module Renderers
5
5
  # The primary renderer for multiple input formats as HTML.
6
6
  class Universal
7
- include Import[:input]
7
+ include Import[:settings]
8
8
 
9
9
  DELEGATES = {asciidoc: Asciidoc.new, markdown: Markdown.new}.freeze
10
10
 
11
11
  def initialize(delegates: DELEGATES, **)
12
12
  super(**)
13
13
  @delegates = delegates
14
- @default_format = input.commit_format.to_sym
14
+ @default_format = settings.commit_format.to_sym
15
15
  end
16
16
 
17
17
  def call content, for: default_format
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "core"
4
+ require "dry/monads"
5
+ require "rss"
6
+
7
+ module Milestoner
8
+ module Syndication
9
+ # Builds feed in Atom format.
10
+ # :reek:DataClump
11
+ class Builder
12
+ include Import[:settings]
13
+ include Dry::Monads[:result]
14
+
15
+ using Refine
16
+
17
+ def initialize(client: RSS::Maker, view: Views::Milestones::Show.new, **)
18
+ super(**)
19
+ @client = client
20
+ @view = view
21
+ end
22
+
23
+ def call tags
24
+ return Failure "No content." if tags.empty?
25
+
26
+ Success build_feed(tags).to_s
27
+ rescue NoMethodError, RSS::Error => error
28
+ Failure "#{self.class}: #{error.message.capitalize}."
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :client, :view
34
+
35
+ def build_feed tags
36
+ client.make "atom" do |node|
37
+ build_channel node.channel, tags
38
+ build_tags node, tags
39
+ end
40
+ end
41
+
42
+ def build_channel node, tags
43
+ at = tags.first.committed_at
44
+
45
+ node.merge id: settings.project_uri_home,
46
+ title: settings.syndication_label,
47
+ subtitle: settings.project_description,
48
+ icon: settings.project_uri_icon,
49
+ logo: settings.project_uri_logo,
50
+ rights: at.strftime("%Y"),
51
+ updated: at
52
+
53
+ build_channel_elements node, tags
54
+ end
55
+
56
+ def build_channel_elements node, tags
57
+ build_links node
58
+ build_generator node
59
+
60
+ build_authors node,
61
+ tags.flat_map { |tag| tag.commits.map(&:author) }
62
+ .uniq
63
+
64
+ node.categories.build label: settings.project_label, term: settings.project_name
65
+ end
66
+
67
+ def build_links node
68
+ node.links.build_for settings.syndication_links,
69
+ label: :title,
70
+ uri: :href,
71
+ relation: :rel,
72
+ mime: :type
73
+ end
74
+
75
+ def build_generator node
76
+ node.generator do |generator|
77
+ generator.merge content: settings.generator_label,
78
+ version: settings.generator_version,
79
+ uri: settings.generator_uri
80
+ end
81
+ end
82
+
83
+ def build_tags node, tags = Core::EMPTY_ARRAY
84
+ tags.each { |tag| build_item node.items, tag }
85
+ end
86
+
87
+ def build_item node, tag
88
+ node.new_item do |item|
89
+ build_item_metadata item, tag
90
+ build_item_content item.content, tag
91
+ build_authors item, tag.commits.map(&:author).uniq.select(&:name)
92
+ item.categories.build_for settings.syndication_categories, label: :label, name: :term
93
+ end
94
+ end
95
+
96
+ def build_item_metadata node, tag
97
+ committed_at = tag.committed_at
98
+ version = tag.version
99
+
100
+ node.merge id: format(settings.syndication_id, id: version),
101
+ title: format(settings.syndication_entry_label, id: version),
102
+ link: format(settings.syndication_entry_uri, id: version),
103
+ rights: committed_at.strftime("%Y"),
104
+ published: committed_at,
105
+ updated: committed_at
106
+ end
107
+
108
+ def build_item_content node, tag
109
+ content = view.call tag:, layout: settings.build_layout, format: :xml
110
+ node.merge content:, type: :html
111
+ end
112
+
113
+ def build_authors node, users = Core::EMPTY_ARRAY
114
+ users.each do |user|
115
+ node.authors.build name: user.name, uri: format(settings.profile_uri, id: user.handle)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rss"
4
+
5
+ module Milestoner
6
+ module Syndication
7
+ # Smooths out the rough edges of the RSS gem object which are harder to work with.
8
+ module Refine
9
+ refine(RSS::Maker::Atom::Feed::Channel) { import_methods Shared }
10
+ refine(RSS::Maker::Atom::Feed::Channel::Authors) { import_methods Shared }
11
+ refine(RSS::Maker::Atom::Feed::Channel::Authors::Author) { import_methods Shared }
12
+ refine(RSS::Maker::Atom::Feed::Channel::Categories) { import_methods Shared }
13
+ refine(RSS::Maker::Atom::Feed::Channel::Categories::Category) { import_methods Shared }
14
+ refine(RSS::Maker::Atom::Feed::Channel::Generator) { import_methods Shared }
15
+ refine(RSS::Maker::Atom::Feed::Channel::Links::Link) { import_methods Shared }
16
+ refine(RSS::Maker::Atom::Feed::Items::Item) { import_methods Shared }
17
+ refine(RSS::Maker::Atom::Feed::Channel::Links) { import_methods Shared }
18
+ refine(RSS::Maker::Atom::Feed::Items::Item::Authors) { import_methods Shared }
19
+ refine(RSS::Maker::Atom::Feed::Items::Item::Authors::Author) { import_methods Shared }
20
+ refine(RSS::Maker::Atom::Feed::Items::Item::Categories) { import_methods Shared }
21
+ refine(RSS::Maker::Atom::Feed::Items::Item::Categories::Category) { import_methods Shared }
22
+ refine(RSS::Maker::Atom::Feed::Items::Item::Content) { import_methods Shared }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module Syndication
5
+ # Provides shared functionality for refinements.
6
+ module Shared
7
+ def merge(**attributes) = attributes.each { |key, value| public_send :"#{key}=", value }
8
+
9
+ def build_for(collection, **)
10
+ collection.each { |attributes| build(**attributes.transform_keys(**)) }
11
+ end
12
+
13
+ def build(**attributes)
14
+ node = public_send :"new_#{kind}"
15
+ attributes.each { |key, value| node.public_send :"#{key}=", value }
16
+ end
17
+
18
+ private
19
+
20
+ def kind
21
+ self.class.name.downcase.split("::").last.sub("categories", "category").delete_suffix("s")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,50 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "core"
4
+ require "dry/monads"
4
5
  require "refinements/string_io"
5
- require "versionaire"
6
6
 
7
7
  module Milestoner
8
8
  module Tags
9
9
  # Handles the creation of project repository tags.
10
10
  class Creator
11
- include Import[:input, :git, :logger]
11
+ include Import[:git, :logger]
12
+ include Dry::Monads[:result]
12
13
 
13
14
  using Refinements::StringIO
14
- using Versionaire::Cast
15
15
 
16
- def initialize(
17
- collector: Commits::Collector.new,
18
- builder: Builders::Stream.new(kernel: StringIO.new),
19
- **
20
- )
16
+ def initialize(collector: Commits::Collector.new, builder: Builders::Stream.new, **)
21
17
  @collector = collector
22
18
  @builder = builder
23
19
  super(**)
24
20
  end
25
21
 
26
- def call override = nil
27
- version = compute_version override
22
+ def call version
23
+ return Success version if local? version
28
24
 
29
- return false if local? version
30
-
31
- collection = collector.call.value_or Core::EMPTY_ARRAY
32
-
33
- fail Error, "Unable to tag without commits." if collection.empty?
34
-
35
- create version
25
+ collect.bind { create version }
36
26
  end
37
27
 
38
28
  private
39
29
 
40
30
  attr_reader :collector, :builder
41
31
 
42
- def compute_version value
43
- Version value || input.project_version
44
- rescue Versionaire::Error => error
45
- raise Error, error
46
- end
47
-
48
32
  def local? version
49
33
  if git.tag_local? version
50
34
  logger.warn { "Local tag exists: #{version}. Skipped." }
@@ -54,17 +38,11 @@ module Milestoner
54
38
  end
55
39
  end
56
40
 
57
- def create version
58
- build(version).fmap { |body| git.tag_create version, body }
59
- .or { |error| fail Error, error }
60
- .bind { true }
61
- end
41
+ def collect = collector.call.alt_map { |message| message.sub("fatal: y", "Y").sub("\n", ".") }
62
42
 
63
- def build version
64
- builder.call.fmap do |body|
65
- "Version #{version}\n\n#{body.reread}\n\n"
66
- end
67
- end
43
+ def create(version) = build(version).bind { |body| git.tag_create version, body }
44
+
45
+ def build(version) = builder.call.fmap { |body| "Version #{version}\n\n#{body.reread}\n\n" }
68
46
  end
69
47
  end
70
48
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry/monads"
4
+
3
5
  module Milestoner
4
6
  module Tags
5
7
  # Handles the tagging and pushing of a tag to a remote repository.
6
8
  class Publisher
7
- include Import[:input, :logger]
9
+ include Import[:logger]
10
+ include Dry::Monads[:result]
8
11
 
9
12
  def initialize(creator: Tags::Creator.new, pusher: Tags::Pusher.new, **)
10
13
  super(**)
@@ -12,15 +15,20 @@ module Milestoner
12
15
  @pusher = pusher
13
16
  end
14
17
 
15
- def call override = nil
16
- creator.call override
17
- pusher.call override
18
- logger.info { "Published: #{input.project_version}!" }
18
+ def call version
19
+ creator.call(version)
20
+ .bind { pusher.call version }
21
+ .bind { log_info version }
19
22
  end
20
23
 
21
24
  private
22
25
 
23
26
  attr_reader :creator, :pusher
27
+
28
+ def log_info version
29
+ logger.info { "Published: #{version}" }
30
+ Success version
31
+ end
24
32
  end
25
33
  end
26
34
  end
@@ -1,29 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "versionaire"
3
+ require "dry/monads"
4
4
 
5
5
  module Milestoner
6
6
  module Tags
7
7
  # Handles publishing of tags to a remote repository.
8
8
  class Pusher
9
- include Import[:input, :git, :logger]
9
+ include Import[:git, :logger]
10
+ include Dry::Monads[:result]
10
11
 
11
- using Versionaire::Cast
12
+ def call version
13
+ check_remote_repo(version).bind { check_remote_tag version }
14
+ .bind { push version }
15
+ end
16
+
17
+ private
12
18
 
13
- def call override = nil
14
- version = compute_version override
19
+ def check_remote_repo version
20
+ git.origin? ? Success(version) : Failure("Remote repository not configured.")
21
+ end
15
22
 
16
- fail Error, "Remote repository not configured." unless git.origin?
17
- fail Error, "Remote tag exists: #{version}." if git.tag_remote? version
18
- fail Error, "Tags could not be pushed to remote repository." if git.tags_push.failure?
23
+ def check_remote_tag version
24
+ git.tag_remote?(version) ? Failure("Remote tag exists: #{version}.") : Success(version)
25
+ end
19
26
 
20
- logger.debug "Local tag pushed: #{version}."
27
+ def push version
28
+ git.tags_push
29
+ .either proc { debug version },
30
+ proc { Failure "Tags could not be pushed to remote repository." }
21
31
  end
22
32
 
23
- def compute_version value
24
- Version value || input.project_version
25
- rescue Versionaire::Error => error
26
- raise Error, error
33
+ def debug version
34
+ logger.debug { "Local tag pushed: #{version}." }
35
+ Success version
27
36
  end
28
37
  end
29
38
  end
@@ -19,7 +19,13 @@
19
19
  href="https://alchemists.io/images/projects/milestoner/logo.png"
20
20
  type="image/png"
21
21
  sizes="200x200">
22
- <link title="<%= project_label %>: Stylesheet" rel="stylesheet" href="page.css" type="text/css">
22
+
23
+ <% if build_stylesheet %>
24
+ <link title="<%= project_label %>: Stylesheet"
25
+ rel="stylesheet"
26
+ href="<%= build_stylesheet %>.css"
27
+ type="text/css">
28
+ <% end %>
23
29
 
24
30
  <script src="https://unpkg.com/alpinejs@3.14" defer></script>
25
31
  </head>
@@ -0,0 +1,11 @@
1
+ <article>
2
+ <%= yield %>
3
+
4
+ <hr/>
5
+
6
+ <footer>
7
+ <em>
8
+ Generated by <a href="<%= generator_uri %>"><%= generator_label %> <%= generator_version %></a>.
9
+ </em>
10
+ </footer>
11
+ </article>
@@ -0,0 +1 @@
1
+ <% if name %>image:<%= url %>[<%= name %>,24,24]<% else %><%= render :icon, name: "missing", alt: "Unknown" %><% end %>
@@ -1,5 +1,5 @@
1
1
  <% if name %>
2
2
  <img src="<%= url %>" alt="<%= name %>" class="avatar" width="24" height="24" loading="lazy">
3
3
  <% else %>
4
- <%= render :icon, icon: "missing_avatar", kind: "Unknown" %>
4
+ <%= render :icon, name: "missing", alt: "Unknown" %>
5
5
  <% end %>
@@ -0,0 +1,2 @@
1
+ <% if name %><img src="<%= url %>" alt="<%= name %>" width="24" height="24" loading="lazy"><% else %>
2
+ <%= render :icon, name: "missing", alt: "Unknown" %><% end %>
@@ -0,0 +1,5 @@
1
+ <% if name %>
2
+ <img src="<%= url %>" alt="<%= name %>" width="24" height="24" loading="lazy">
3
+ <% else %>
4
+ <%= render :icon, name: "missing", alt: "Unknown" %>
5
+ <% end %>
@@ -1 +1 @@
1
- * <%= commit.emoji %> <%= commit.subject %> - _<%= commit.author.name %>_
1
+ * <%= commit.emoji %> <%= commit.subject %> - <%= render :profile, name: commit.author.name, url: commit.profile_url(commit.author) %>
@@ -1,13 +1,13 @@
1
1
  <details id="<%= commit.sha %>" class="row" x-bind:open="open">
2
2
  <summary class="summary <%= commit.kind %>">
3
3
  <div class="title">
4
- <%= render :icon, icon: commit.icon, kind: commit.kind %>
4
+ <%= render :icon, name: commit.icon, alt: commit.kind %>
5
5
  <span class="subject"><%= commit.subject %></span>
6
6
  </div>
7
7
 
8
- <div class="author">
8
+ <a href="<%= commit.profile_url commit.author %>" class="author">
9
9
  <%= render :avatar, name: commit.author.name, url: commit.avatar_url(commit.author) %>
10
- </div>
10
+ </a>
11
11
 
12
12
  <a href="<%= commit.uri %>" class="files"><%= commit.files_changed %></a>
13
13
 
@@ -48,7 +48,7 @@
48
48
  <div class="panel">
49
49
  <h3 class="label">Author</h3>
50
50
 
51
- <p class="item">
51
+ <p class="line">
52
52
  <%= render :avatar, name: commit.author.name, url: commit.avatar_url(commit.author) %>
53
53
  <%= render :profile, name: commit.author.name, url: commit.profile_url(commit.author) %>
54
54
  </p>
@@ -57,7 +57,7 @@
57
57
  <h3 class="label">Collaborators</h3>
58
58
 
59
59
  <% commit.collaborators.each do |collaborator| %>
60
- <p class="item">
60
+ <p class="line">
61
61
  <%= render :avatar, name: collaborator.name, url: commit.avatar_url(collaborator) %>
62
62
  <%= render :profile, name: collaborator.name, url: commit.profile_url(collaborator) %>
63
63
  </p>
@@ -68,7 +68,7 @@
68
68
  <h3 class="label">Signers</h3>
69
69
 
70
70
  <% commit.signers.each do |signer| %>
71
- <p class="item">
71
+ <p class="line">
72
72
  <%= render :avatar, name: signer.name, url: commit.avatar_url(signer) %>
73
73
  <%= render :profile, name: signer.name, url: commit.profile_url(signer) %>
74
74
  </p>
@@ -77,13 +77,23 @@
77
77
 
78
78
  <h3 class="label">Signature</h3>
79
79
 
80
- <p class="item pill <%= commit.security %>">
80
+ <p class="line pill <%= commit.security %>">
81
81
  <span class="signature"><%= commit.signature %></span>
82
82
  </p>
83
83
 
84
+ <% unless String(commit.fingerprint).empty? %>
85
+ <h3 class="label">Fingerprint</h3>
86
+ <p class="line"><%= commit.fingerprint %></p>
87
+ <% end %>
88
+
89
+ <% unless String(commit.fingerprint_key).empty? %>
90
+ <h3 class="label">Fingerprint Primary Key</h3>
91
+ <p class="line"><%= commit.fingerprint_key %></p>
92
+ <% end %>
93
+
84
94
  <h3 class="label">Created</h3>
85
95
 
86
- <p class="item at">
96
+ <p class="line at">
87
97
  <time datetime="<%= commit.at %>" class="date"><%= commit.date %></time>
88
98
  <time datetime="<%= commit.at %>" class="weekday"><%= commit.weekday %></time>
89
99
  <time datetime="<%= commit.at %>" class="time"><%= commit.time %></time>
@@ -1 +1 @@
1
- - <%= commit.emoji %> <%= commit.subject %> - *<%= commit.author.name %>*
1
+ - <%= commit.emoji %> <%= commit.subject %> - <%= render :profile, name: commit.author.name, url: commit.profile_url(commit.author) %>
@@ -0,0 +1,84 @@
1
+ <details>
2
+ <summary style="display: inline-flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;">
3
+ <%= render :icon, name: commit.icon, alt: commit.kind %>
4
+ <%= commit.subject %>
5
+ </summary>
6
+
7
+ <h4>Message</h4>
8
+
9
+ <% if commit.body.empty? %>
10
+ <p>No message.</p>
11
+ <% else %>
12
+ <%= commit.safe_body %>
13
+ <% end %>
14
+
15
+ <% unless commit.notes.empty? %>
16
+ <details>
17
+ <summary>Notes</summary>
18
+ <%= commit.notes.html_safe %>
19
+ </details>
20
+ <% end %>
21
+
22
+ <h4>Author</h4>
23
+
24
+ <span style="display: inline-flex; align-items: center; gap: 0.5rem;">
25
+ <%= render :avatar, name: commit.author.name, url: commit.avatar_url(commit.author) %>
26
+ <%= render :profile, name: commit.author.name, url: commit.profile_url(commit.author) %>
27
+ </span>
28
+
29
+ <% if commit.collaborators.any? %>
30
+ <h4>Collaborators</h4>
31
+
32
+ <ul>
33
+ <% commit.collaborators.each do |collaborator| %>
34
+ <li style="display: inline-flex; align-items: center; gap: 0.5rem;">
35
+ <%= render :avatar, name: collaborator.name, url: commit.avatar_url(collaborator) %>
36
+ <%= render :profile, name: collaborator.name, url: commit.profile_url(collaborator) %>
37
+ </li>
38
+ <% end %>
39
+ </ul>
40
+ <% end %>
41
+
42
+ <% if commit.signers.any? %>
43
+ <h4>Signers</h4>
44
+
45
+ <ul>
46
+ <% commit.signers.each do |signer| %>
47
+ <li style="display: inline-flex; align-items: center; gap: 0.5rem;">
48
+ <%= render :avatar, name: signer.name, url: commit.avatar_url(signer) %>
49
+ <%= render :profile, name: signer.name, url: commit.profile_url(signer) %>
50
+ </li>
51
+ <% end %>
52
+ </ul>
53
+ <% end %>
54
+
55
+ <h4>Details</h4>
56
+
57
+ <ul>
58
+ <li>Milestone: <%= commit.tag.capitalize %></li>
59
+ <li>Signature: <%= commit.signature %></li>
60
+ <li>Files: <a href="<%= commit.uri %>"><%= commit.files_changed %></a></li>
61
+
62
+ <li>
63
+ Lines:
64
+ <span><%= commit.total_deletions %></span>
65
+ <span>/</span>
66
+ <span><%= commit.total_insertions %></span>
67
+ </li>
68
+
69
+ <li>Issue: <a href="<%= commit.issue.uri %>"><%= commit.issue.id %></a></li>
70
+ <li>Review: <a href="<%= commit.review.uri %>"><%= commit.review.id %></a></li>
71
+ </ul>
72
+
73
+ <p>
74
+ <em style="display: inline-flex; align-items: center; gap: 0.5rem; margin-top: 1rem;">
75
+ <time datetime="<%= commit.at %>"><%= commit.date %></time>
76
+ <time datetime="<%= commit.at %>">(<%= commit.weekday %>)</time>
77
+ at
78
+ <time datetime="<%= commit.at %>"><%= commit.time %></time>
79
+ <time datetime="<%= commit.at %>"><%= commit.zone %></time>
80
+ </em>
81
+ </p>
82
+
83
+ <hr/>
84
+ </details>
@@ -0,0 +1 @@
1
+ image::https://alchemists.io/images/projects/milestoner/icons/<%= name %>[<%= alt %>,24,24]
@@ -1,5 +1,5 @@
1
- <img src="https://alchemists.io/images/projects/milestoner/icons/commits/<%= icon %>.png"
2
- alt="<%= kind %>"
1
+ <img src="https://alchemists.io/images/projects/milestoner/icons/<%= name %>.png"
2
+ alt="<%= alt %>"
3
3
  class="icon"
4
4
  width="24"
5
5
  height="24"
@@ -0,0 +1,5 @@
1
+ <img src="https://alchemists.io/images/projects/milestoner/icons/<%= name %>.png"
2
+ alt="<%= alt %>"
3
+ width="24"
4
+ height="24"
5
+ loading="lazy">
@@ -0,0 +1,5 @@
1
+ <img src="https://alchemists.io/images/projects/milestoner/icons/<%= name %>.png"
2
+ alt="<%= alt %>"
3
+ width="24"
4
+ height="24"
5
+ loading="lazy">
@@ -0,0 +1 @@
1
+ <% if name %>link:<%= url %>[<%= name %>]<% else %>Unknown<% end %>
@@ -0,0 +1 @@
1
+ <% if name %>[<%= name %>](<%= url %>)<% else %>Unknown<% end %>
@@ -0,0 +1,5 @@
1
+ <% if name %>
2
+ <a href="<%= url %>"><%= name %></a>
3
+ <% else %>
4
+ <span>Unknown</span>
5
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <% if tag.empty? %>
2
+ = pass:[ ]<% if project_uri_logo %>image:<%= project_uri_logo %>[Logo,50,50]pass:[ ]<% end %>link:<%= project_uri_home %>[<%= project_label %>] (<%= tag.committed_date %>)
3
+
4
+ *<%= tag.total_commits %>. <%= tag.total_files %>. <%= tag.total_deletions %>. <%= tag.total_insertions %>.*
5
+ <% else %>
6
+ = pass:[ ]<% if project_uri_logo %>image:<%= project_uri_logo %>[Logo,50,50]pass:[ ]<% end %>link:<%= project_uri_home %>[<%= project_label %>] <%= project_version %> (<%= tag.committed_date %>)
7
+
8
+ <%= render :avatar, name: tag.author.name, url: tag.avatar_url(tag.author) %> <%= render :profile, name: tag.author.name, url: tag.profile_url(tag.author) %> | <%= tag.security %>
9
+
10
+ <% tag.commits.each do |commit| %><%= commit.render :commit %><% end %>
11
+
12
+ *<%= tag.total_commits %>. <%= tag.total_files %>. <%= tag.total_deletions %>. <%= tag.total_insertions %>.*
13
+ <% end %>