shiplog 0.1.0
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/CHANGELOG.md +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +410 -0
- data/app/controllers/shiplog/admin/application_controller.rb +37 -0
- data/app/controllers/shiplog/admin/boards_controller.rb +60 -0
- data/app/controllers/shiplog/admin/entries/approvals_controller.rb +15 -0
- data/app/controllers/shiplog/admin/entries/discards_controller.rb +25 -0
- data/app/controllers/shiplog/admin/entries/rewrites_controller.rb +13 -0
- data/app/controllers/shiplog/admin/entries_controller.rb +55 -0
- data/app/controllers/shiplog/admin/syncs_controller.rb +21 -0
- data/app/controllers/shiplog/application_controller.rb +5 -0
- data/app/controllers/shiplog/changelogs_controller.rb +24 -0
- data/app/controllers/shiplog/frontends_controller.rb +21 -0
- data/app/controllers/shiplog/public_controller.rb +19 -0
- data/app/controllers/shiplog/roadmaps_controller.rb +14 -0
- data/app/controllers/shiplog/webhooks_controller.rb +121 -0
- data/app/frontend/shiplog/admin.css +137 -0
- data/app/frontend/shiplog/board.js +77 -0
- data/app/frontend/shiplog/shiplog.css +184 -0
- data/app/helpers/shiplog/admin/entries_helper.rb +27 -0
- data/app/helpers/shiplog/application_helper.rb +14 -0
- data/app/helpers/shiplog/frontend_helper.rb +17 -0
- data/app/helpers/shiplog/host_routes_helper.rb +25 -0
- data/app/helpers/shiplog/styling_helper.rb +24 -0
- data/app/jobs/shiplog/application_job.rb +14 -0
- data/app/jobs/shiplog/refresh_job.rb +38 -0
- data/app/jobs/shiplog/rewrite_entry_job.rb +9 -0
- data/app/jobs/shiplog/scan_changes_job.rb +36 -0
- data/app/jobs/shiplog/sync_job.rb +20 -0
- data/app/models/shiplog/application_record.rb +5 -0
- data/app/models/shiplog/board_arrangement.rb +38 -0
- data/app/models/shiplog/change_candidates.rb +77 -0
- data/app/models/shiplog/commit.rb +9 -0
- data/app/models/shiplog/drafter.rb +69 -0
- data/app/models/shiplog/entry.rb +42 -0
- data/app/models/shiplog/issue.rb +68 -0
- data/app/models/shiplog/issue_form.rb +43 -0
- data/app/models/shiplog/issue_link.rb +6 -0
- data/app/models/shiplog/pull_request.rb +11 -0
- data/app/models/shiplog/repository.rb +9 -0
- data/app/models/shiplog/rewriter/prompt.rb +71 -0
- data/app/models/shiplog/rewriter.rb +89 -0
- data/app/models/shiplog/roadmap/column_resolver.rb +46 -0
- data/app/models/shiplog/roadmap.rb +34 -0
- data/app/models/shiplog/sync/commit_scanner.rb +39 -0
- data/app/models/shiplog/sync/issue_importer.rb +50 -0
- data/app/models/shiplog/sync/node_attributes.rb +50 -0
- data/app/models/shiplog/sync/pull_request_importer.rb +54 -0
- data/app/models/shiplog/sync/repository_sync.rb +80 -0
- data/app/models/shiplog/webhook_delivery.rb +5 -0
- data/app/models/shiplog/webhook_signature.rb +16 -0
- data/app/views/layouts/shiplog/admin.html.erb +16 -0
- data/app/views/layouts/shiplog/application.html.erb +15 -0
- data/app/views/shiplog/_empty.html.erb +3 -0
- data/app/views/shiplog/_page.html.erb +6 -0
- data/app/views/shiplog/_page_header.html.erb +6 -0
- data/app/views/shiplog/_pagination.html.erb +12 -0
- data/app/views/shiplog/admin/_assets.html.erb +3 -0
- data/app/views/shiplog/admin/_custom_head.html.erb +0 -0
- data/app/views/shiplog/admin/_nav.html.erb +9 -0
- data/app/views/shiplog/admin/_page.html.erb +14 -0
- data/app/views/shiplog/admin/boards/_card.html.erb +22 -0
- data/app/views/shiplog/admin/boards/show.html.erb +17 -0
- data/app/views/shiplog/admin/entries/_badges.html.erb +10 -0
- data/app/views/shiplog/admin/entries/_entry_row.html.erb +21 -0
- data/app/views/shiplog/admin/entries/edit.html.erb +61 -0
- data/app/views/shiplog/admin/entries/index.html.erb +47 -0
- data/app/views/shiplog/admin/syncs/show.html.erb +71 -0
- data/app/views/shiplog/changelogs/_entry.html.erb +8 -0
- data/app/views/shiplog/changelogs/_month.html.erb +9 -0
- data/app/views/shiplog/changelogs/show.html.erb +16 -0
- data/app/views/shiplog/roadmaps/_card.html.erb +6 -0
- data/app/views/shiplog/roadmaps/_column.html.erb +17 -0
- data/app/views/shiplog/roadmaps/show.html.erb +9 -0
- data/config/admin_routes.rb +13 -0
- data/config/locales/en.yml +106 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20261026000001_create_shiplog_tables.rb +102 -0
- data/lib/generators/shiplog/install/install_generator.rb +94 -0
- data/lib/generators/shiplog/install/templates/initializer.rb.tt +86 -0
- data/lib/generators/shiplog/install/templates/shiplog-feature.yml +23 -0
- data/lib/shiplog/admin/engine.rb +9 -0
- data/lib/shiplog/classes.rb +38 -0
- data/lib/shiplog/configuration.rb +92 -0
- data/lib/shiplog/engine.rb +5 -0
- data/lib/shiplog/github/client.rb +96 -0
- data/lib/shiplog/github/errors.rb +17 -0
- data/lib/shiplog/github/queries.rb +108 -0
- data/lib/shiplog/version.rb +3 -0
- data/lib/shiplog.rb +16 -0
- metadata +154 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Shiplog
|
|
2
|
+
class WebhookSignature
|
|
3
|
+
def initialize(secret:, body:, header:)
|
|
4
|
+
@secret = secret
|
|
5
|
+
@body = body
|
|
6
|
+
@header = header
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def valid?
|
|
10
|
+
return false if @header.blank?
|
|
11
|
+
|
|
12
|
+
expected = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", @secret, @body)
|
|
13
|
+
Rack::Utils.secure_compare(expected, @header)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="<%= I18n.locale %>">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title><%= content_for(:title).presence || t("shiplog.admin.title") %></title>
|
|
7
|
+
<%= csrf_meta_tags %>
|
|
8
|
+
<%= csp_meta_tag %>
|
|
9
|
+
<%= render "shiplog/admin/custom_head" %>
|
|
10
|
+
</head>
|
|
11
|
+
<body class="shiplog-admin-body">
|
|
12
|
+
<main>
|
|
13
|
+
<%= yield %>
|
|
14
|
+
</main>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="<%= I18n.locale %>">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title><%= content_for(:title) %></title>
|
|
7
|
+
<%= csrf_meta_tags %>
|
|
8
|
+
<%= csp_meta_tag %>
|
|
9
|
+
</head>
|
|
10
|
+
<body class="shiplog-layout">
|
|
11
|
+
<main>
|
|
12
|
+
<%= yield %>
|
|
13
|
+
</main>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%# locals: (name:) %>
|
|
2
|
+
<%# rendered with `render layout:`; wraps a public page and emits the bundled stylesheet (unless config.stylesheet is false) %>
|
|
3
|
+
<%= shiplog_stylesheet_tag "shiplog" if Shiplog.config.stylesheet %>
|
|
4
|
+
<div class="<%= shiplog_class(:page, name) %>">
|
|
5
|
+
<%= yield %>
|
|
6
|
+
</div>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%# locals: (title:, lede: nil) %>
|
|
2
|
+
<%# the page heading; the texts come from I18n (shiplog.<page>.title / .lede) %>
|
|
3
|
+
<header class="<%= shiplog_class(:page_header) %>">
|
|
4
|
+
<h1 class="<%= shiplog_class(:page_title) %>"><%= title %></h1>
|
|
5
|
+
<% if lede.present? %><p class="<%= shiplog_class(:page_lede) %>"><%= lede %></p><% end %>
|
|
6
|
+
</header>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%# locals: (newer_path: nil, older_path: nil) %>
|
|
2
|
+
<%# previous/next page links; renders nothing when both are nil %>
|
|
3
|
+
<% if newer_path || older_path %>
|
|
4
|
+
<nav class="<%= shiplog_class(:pagination) %>" aria-label="<%= t("shiplog.changelog.pagination.label") %>">
|
|
5
|
+
<% if newer_path %>
|
|
6
|
+
<%= link_to t("shiplog.changelog.pagination.newer"), newer_path, class: shiplog_class(:pagination_link, :newer), rel: "prev" %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% if older_path %>
|
|
9
|
+
<%= link_to t("shiplog.changelog.pagination.older"), older_path, class: shiplog_class(:pagination_link, :older), rel: "next" %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</nav>
|
|
12
|
+
<% end %>
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%# locals: (hosted: false) %>
|
|
2
|
+
<%# the Drafts / Roadmap board / Sync links; `hosted` renders the compact variant used inside a host layout %>
|
|
3
|
+
<nav class="shiplog-admin-nav<%= " shiplog-admin-nav--sub" if hosted %>" aria-label="<%= t("shiplog.admin.title") %>">
|
|
4
|
+
<ul class="shiplog-admin-nav__list">
|
|
5
|
+
<li><%= link_to t("shiplog.admin.nav.drafts"), root_path, class: "shiplog-admin-nav__link" %></li>
|
|
6
|
+
<li><%= link_to t("shiplog.admin.nav.board"), board_path, class: "shiplog-admin-nav__link" %></li>
|
|
7
|
+
<li><%= link_to t("shiplog.admin.nav.sync"), sync_path, class: "shiplog-admin-nav__link" %></li>
|
|
8
|
+
</ul>
|
|
9
|
+
</nav>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%# locals: () %>
|
|
2
|
+
<%# rendered with `render layout:`; wraps an admin page in the scoped .shiplog-admin container with its assets, nav and flash %>
|
|
3
|
+
<% hosted = shiplog_admin_hosted? %>
|
|
4
|
+
<%= render "shiplog/admin/assets" %>
|
|
5
|
+
<div class="shiplog-admin<%= " shiplog-admin--standalone" unless hosted %>">
|
|
6
|
+
<%= render "shiplog/admin/nav", hosted: %>
|
|
7
|
+
<div class="shiplog-admin__main">
|
|
8
|
+
<% if !hosted || Shiplog.config.admin_flash %>
|
|
9
|
+
<% if notice %><p class="shiplog-admin__flash shiplog-admin__flash--notice" role="status"><%= notice %></p><% end %>
|
|
10
|
+
<% if alert %><p class="shiplog-admin__flash shiplog-admin__flash--alert" role="alert"><%= alert %></p><% end %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<%= yield %>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% issue = item.issue %>
|
|
2
|
+
<article class="shiplog-card" draggable="true" data-issue-id="<%= issue.id %>">
|
|
3
|
+
<h3 class="shiplog-card__title"><%= item.entry&.title.presence || issue.title %></h3>
|
|
4
|
+
<p class="shiplog-card__meta">
|
|
5
|
+
<%= shiplog_github_link("#{issue.repository.full_name}##{issue.number}", issue.url) %>
|
|
6
|
+
<% unless item.entry&.approved? %>
|
|
7
|
+
<span class="shiplog-badge shiplog-badge--warning"><%= t("shiplog.admin.board.not_approved") %></span>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if issue.pinned_column %>
|
|
10
|
+
<span class="shiplog-badge"><%= t("shiplog.admin.board.pinned") %></span>
|
|
11
|
+
<% end %>
|
|
12
|
+
</p>
|
|
13
|
+
<%= form_with url: board_path, method: :patch, class: "shiplog-card__form" do |form| %>
|
|
14
|
+
<%= form.hidden_field :issue_id, value: issue.id, id: nil %>
|
|
15
|
+
<%= form.label :column, t("shiplog.admin.board.move_to"), for: "column-#{issue.id}", class: "shiplog-card__label" %>
|
|
16
|
+
<%= form.select :column, Shiplog::Issue::COLUMNS.map { |column| [ t("shiplog.columns.#{column}"), column ] }, { selected: item.column }, id: "column-#{issue.id}" %>
|
|
17
|
+
<%= form.submit t("shiplog.admin.board.move"), class: "shiplog-button", data: { disable_with: false } %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% if issue.pinned_column %>
|
|
20
|
+
<%= button_to t("shiplog.admin.board.unpin"), board_path, method: :patch, params: { issue_id: issue.id, unpin: "1" }, class: "shiplog-button" %>
|
|
21
|
+
<% end %>
|
|
22
|
+
</article>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%= render layout: "shiplog/admin/page" do %>
|
|
2
|
+
<h1 class="shiplog-admin__title"><%= t("shiplog.admin.board.title") %></h1>
|
|
3
|
+
<p class="shiplog-admin__muted"><%= t("shiplog.admin.board.help") %></p>
|
|
4
|
+
|
|
5
|
+
<div class="shiplog-board" data-board-url="<%= board_path %>" data-csrf-token="<%= form_authenticity_token %>">
|
|
6
|
+
<% @columns.each do |column, items| %>
|
|
7
|
+
<section class="shiplog-board__column" aria-labelledby="shiplog-board-<%= column %>">
|
|
8
|
+
<h2 id="shiplog-board-<%= column %>" class="shiplog-board__title"><%= t("shiplog.columns.#{column}") %></h2>
|
|
9
|
+
<div class="shiplog-board__cards" data-column="<%= column %>">
|
|
10
|
+
<%= render partial: "shiplog/admin/boards/card", collection: items, as: :item %>
|
|
11
|
+
</div>
|
|
12
|
+
</section>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<%= shiplog_javascript_tag "board" %>
|
|
17
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% if entry.source_changed? %>
|
|
2
|
+
<span class="shiplog-badge shiplog-badge--warning"><%= t("shiplog.admin.entries.source_changed") %></span>
|
|
3
|
+
<% end %>
|
|
4
|
+
<% if entry.draft? && entry.rewritten_at.nil? && entry.rewrite_error.blank? %>
|
|
5
|
+
<span class="shiplog-badge"><%= t("shiplog.admin.entries.not_rewritten") %></span>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% if entry.rewrite_error.present? %>
|
|
8
|
+
<span class="shiplog-badge shiplog-badge--error"><%= t("shiplog.admin.entries.rewrite_error") %></span>
|
|
9
|
+
<div class="shiplog-admin__muted"><%= entry.rewrite_error %></div>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<tr class="shiplog-entry-row">
|
|
2
|
+
<td>
|
|
3
|
+
<%= link_to entry_heading(entry), edit_entry_path(entry) %>
|
|
4
|
+
<%= render "shiplog/admin/entries/badges", entry: %>
|
|
5
|
+
<% if entry.discarded? && entry.discard_reason.present? %>
|
|
6
|
+
<div class="shiplog-admin__muted"><%= entry.discard_reason %></div>
|
|
7
|
+
<% end %>
|
|
8
|
+
</td>
|
|
9
|
+
<td><%= t("shiplog.admin.entries.kinds.#{entry.kind}") %></td>
|
|
10
|
+
<td>
|
|
11
|
+
<%= shiplog_github_link(source_reference(entry.subject), entry.subject.url) %>
|
|
12
|
+
</td>
|
|
13
|
+
<td class="shiplog-admin-table__actions">
|
|
14
|
+
<%= link_to t("shiplog.admin.entries.edit"), edit_entry_path(entry), class: "shiplog-button" %>
|
|
15
|
+
<% if entry.draft? %>
|
|
16
|
+
<%= button_to t("shiplog.admin.entries.approve"), entry_approval_path(entry), class: "shiplog-button shiplog-button--primary" %>
|
|
17
|
+
<% elsif entry.discarded? %>
|
|
18
|
+
<%= button_to t("shiplog.admin.entries.restore"), entry_discard_path(entry), method: :delete, class: "shiplog-button" %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</td>
|
|
21
|
+
</tr>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<%= render layout: "shiplog/admin/page" do %>
|
|
2
|
+
<h1 class="shiplog-admin__title"><%= t("shiplog.admin.entries.edit_title", kind: t("shiplog.admin.entries.kinds.#{@entry.kind}")) %></h1>
|
|
3
|
+
<p>
|
|
4
|
+
<span class="shiplog-badge"><%= t("shiplog.admin.entries.statuses.#{@entry.status}") %></span>
|
|
5
|
+
<%= render "shiplog/admin/entries/badges", entry: @entry %>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<div class="shiplog-admin-split">
|
|
9
|
+
<section class="shiplog-admin-panel">
|
|
10
|
+
<h2><%= t("shiplog.admin.entries.public_text") %></h2>
|
|
11
|
+
<%= form_with model: @entry, url: entry_path(@entry), method: :patch, class: "shiplog-admin-form" do |form| %>
|
|
12
|
+
<% if @entry.errors.any? %>
|
|
13
|
+
<div class="shiplog-admin__flash shiplog-admin__flash--alert" role="alert">
|
|
14
|
+
<%= @entry.errors.full_messages.to_sentence %>
|
|
15
|
+
</div>
|
|
16
|
+
<% end %>
|
|
17
|
+
<p>
|
|
18
|
+
<%= form.label :title, t("shiplog.admin.entries.fields.title") %>
|
|
19
|
+
<%= form.text_field :title %>
|
|
20
|
+
</p>
|
|
21
|
+
<p>
|
|
22
|
+
<%= form.label :body, t("shiplog.admin.entries.fields.body") %>
|
|
23
|
+
<%= form.text_area :body, rows: 8 %>
|
|
24
|
+
</p>
|
|
25
|
+
<p>
|
|
26
|
+
<%= form.label :released_on, t("shiplog.admin.entries.fields.released_on") %>
|
|
27
|
+
<%= form.date_field :released_on %>
|
|
28
|
+
</p>
|
|
29
|
+
<%= form.submit t("shiplog.admin.entries.save"), class: "shiplog-button shiplog-button--primary" %>
|
|
30
|
+
<% end %>
|
|
31
|
+
|
|
32
|
+
<div class="shiplog-admin-actions">
|
|
33
|
+
<% if @entry.draft? %>
|
|
34
|
+
<%= button_to t("shiplog.admin.entries.approve"), entry_approval_path(@entry), class: "shiplog-button shiplog-button--primary" %>
|
|
35
|
+
<%= form_with url: entry_discard_path(@entry), class: "shiplog-admin-inline-form" do |form| %>
|
|
36
|
+
<%= form.label :reason, t("shiplog.admin.entries.fields.reason") %>
|
|
37
|
+
<%= form.text_field :reason %>
|
|
38
|
+
<%= form.submit t("shiplog.admin.entries.discard"), class: "shiplog-button" %>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% elsif @entry.discarded? %>
|
|
41
|
+
<%= button_to t("shiplog.admin.entries.restore"), entry_discard_path(@entry), method: :delete, class: "shiplog-button" %>
|
|
42
|
+
<% end %>
|
|
43
|
+
<%= button_to t("shiplog.admin.entries.regenerate"), entry_rewrite_path(@entry), class: "shiplog-button" %>
|
|
44
|
+
<% if @entry.approved? %>
|
|
45
|
+
<p class="shiplog-admin__muted"><%= t("shiplog.admin.entries.regenerate_warning") %></p>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<aside class="shiplog-admin-panel">
|
|
51
|
+
<h2><%= t("shiplog.admin.entries.source") %></h2>
|
|
52
|
+
<p>
|
|
53
|
+
<strong><%= source_title(@entry.subject) %></strong><br>
|
|
54
|
+
<%= shiplog_github_link(source_reference(@entry.subject), @entry.subject.url) %>
|
|
55
|
+
</p>
|
|
56
|
+
<pre class="shiplog-admin-source"><%= source_body(@entry.subject) %></pre>
|
|
57
|
+
</aside>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<p><%= link_to t("shiplog.admin.entries.back"), root_path %></p>
|
|
61
|
+
<% end %>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<%= render layout: "shiplog/admin/page" do %>
|
|
2
|
+
<h1 class="shiplog-admin__title"><%= t("shiplog.admin.entries.title") %></h1>
|
|
3
|
+
|
|
4
|
+
<nav class="shiplog-admin-filters" aria-label="<%= t("shiplog.admin.entries.filters") %>">
|
|
5
|
+
<ul class="shiplog-admin-filters__list">
|
|
6
|
+
<% Shiplog::Entry.statuses.each_key do |status| %>
|
|
7
|
+
<li><%= link_to t("shiplog.admin.entries.statuses.#{status}"), entries_filter_path(status:, kind: @kind),
|
|
8
|
+
class: "shiplog-admin-filters__link", "aria-current": (status == @status ? "true" : nil) %></li>
|
|
9
|
+
<% end %>
|
|
10
|
+
</ul>
|
|
11
|
+
<ul class="shiplog-admin-filters__list">
|
|
12
|
+
<li><%= link_to t("shiplog.admin.entries.all_kinds"), entries_filter_path(status: @status),
|
|
13
|
+
class: "shiplog-admin-filters__link", "aria-current": (@kind.nil? ? "true" : nil) %></li>
|
|
14
|
+
<% Shiplog::Entry.kinds.each_key do |kind| %>
|
|
15
|
+
<li><%= link_to t("shiplog.admin.entries.kinds.#{kind}"), entries_filter_path(status: @status, kind:),
|
|
16
|
+
class: "shiplog-admin-filters__link", "aria-current": (kind == @kind ? "true" : nil) %></li>
|
|
17
|
+
<% end %>
|
|
18
|
+
</ul>
|
|
19
|
+
</nav>
|
|
20
|
+
|
|
21
|
+
<% if @entries.empty? %>
|
|
22
|
+
<p class="shiplog-admin__empty"><%= t("shiplog.admin.entries.empty") %></p>
|
|
23
|
+
<% else %>
|
|
24
|
+
<table class="shiplog-admin-table">
|
|
25
|
+
<thead>
|
|
26
|
+
<tr>
|
|
27
|
+
<th scope="col"><%= t("shiplog.admin.entries.columns.entry") %></th>
|
|
28
|
+
<th scope="col"><%= t("shiplog.admin.entries.columns.kind") %></th>
|
|
29
|
+
<th scope="col"><%= t("shiplog.admin.entries.columns.source") %></th>
|
|
30
|
+
<th scope="col"><%= t("shiplog.admin.entries.columns.actions") %></th>
|
|
31
|
+
</tr>
|
|
32
|
+
</thead>
|
|
33
|
+
<tbody>
|
|
34
|
+
<%= render partial: "shiplog/admin/entries/entry_row", collection: @entries, as: :entry %>
|
|
35
|
+
</tbody>
|
|
36
|
+
</table>
|
|
37
|
+
<% end %>
|
|
38
|
+
|
|
39
|
+
<nav class="shiplog-admin-pagination" aria-label="<%= t("shiplog.admin.entries.pagination") %>">
|
|
40
|
+
<% if @page > 1 %>
|
|
41
|
+
<%= link_to t("shiplog.admin.entries.newer"), entries_filter_path(status: @status, kind: @kind, page: (@page - 1 if @page > 2)), class: "shiplog-admin-pagination__prev" %>
|
|
42
|
+
<% end %>
|
|
43
|
+
<% if @more %>
|
|
44
|
+
<%= link_to t("shiplog.admin.entries.older"), entries_filter_path(status: @status, kind: @kind, page: @page + 1), class: "shiplog-admin-pagination__next" %>
|
|
45
|
+
<% end %>
|
|
46
|
+
</nav>
|
|
47
|
+
<% end %>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<%= render layout: "shiplog/admin/page" do %>
|
|
2
|
+
<h1 class="shiplog-admin__title"><%= t("shiplog.admin.sync.title") %></h1>
|
|
3
|
+
|
|
4
|
+
<% if @webhook_secret_missing %>
|
|
5
|
+
<p class="shiplog-admin__flash shiplog-admin__flash--alert shiplog-warning" role="alert"><%= t("shiplog.admin.sync.webhook_secret_missing") %></p>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% if @github_token_missing %>
|
|
8
|
+
<p class="shiplog-admin__flash shiplog-admin__flash--alert shiplog-warning" role="alert"><%= t("shiplog.admin.sync.github_token_missing") %></p>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<div class="shiplog-admin-actions">
|
|
12
|
+
<%= button_to t("shiplog.admin.sync.sync_now"), sync_path, class: "shiplog-button shiplog-button--primary" %>
|
|
13
|
+
<%= button_to t("shiplog.admin.sync.sync_and_scan"), sync_path, params: { scan: "1" }, class: "shiplog-button" %>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<h2><%= t("shiplog.admin.sync.repositories") %></h2>
|
|
17
|
+
<% if @repositories.empty? %>
|
|
18
|
+
<p class="shiplog-admin__empty"><%= t("shiplog.admin.sync.no_repositories") %></p>
|
|
19
|
+
<% else %>
|
|
20
|
+
<table class="shiplog-admin-table">
|
|
21
|
+
<thead>
|
|
22
|
+
<tr>
|
|
23
|
+
<th scope="col"><%= t("shiplog.admin.sync.repository") %></th>
|
|
24
|
+
<th scope="col"><%= t("shiplog.admin.sync.synced_at") %></th>
|
|
25
|
+
<th scope="col"><%= t("shiplog.admin.sync.changes_scanned_at") %></th>
|
|
26
|
+
<th scope="col"><%= t("shiplog.admin.sync.last_error") %></th>
|
|
27
|
+
</tr>
|
|
28
|
+
</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
<% @repositories.each do |full_name, repository| %>
|
|
31
|
+
<tr class="shiplog-repository-row">
|
|
32
|
+
<td><%= full_name %></td>
|
|
33
|
+
<td><%= repository&.synced_at ? l(repository.synced_at, format: :short) : t("shiplog.admin.sync.never") %></td>
|
|
34
|
+
<td><%= repository&.changes_scanned_at ? l(repository.changes_scanned_at, format: :short) : t("shiplog.admin.sync.never") %></td>
|
|
35
|
+
<td><%= repository&.last_error %></td>
|
|
36
|
+
</tr>
|
|
37
|
+
<% end %>
|
|
38
|
+
</tbody>
|
|
39
|
+
</table>
|
|
40
|
+
<% end %>
|
|
41
|
+
|
|
42
|
+
<h2><%= t("shiplog.admin.sync.deliveries") %></h2>
|
|
43
|
+
<% if @deliveries.empty? %>
|
|
44
|
+
<p class="shiplog-admin__empty"><%= t("shiplog.admin.sync.no_deliveries") %></p>
|
|
45
|
+
<% else %>
|
|
46
|
+
<table class="shiplog-admin-table">
|
|
47
|
+
<thead>
|
|
48
|
+
<tr>
|
|
49
|
+
<th scope="col"><%= t("shiplog.admin.sync.event") %></th>
|
|
50
|
+
<th scope="col"><%= t("shiplog.admin.sync.action") %></th>
|
|
51
|
+
<th scope="col"><%= t("shiplog.admin.sync.repository") %></th>
|
|
52
|
+
<th scope="col"><%= t("shiplog.admin.sync.received_at") %></th>
|
|
53
|
+
<th scope="col"><%= t("shiplog.admin.sync.processed_at") %></th>
|
|
54
|
+
<th scope="col"><%= t("shiplog.admin.sync.error") %></th>
|
|
55
|
+
</tr>
|
|
56
|
+
</thead>
|
|
57
|
+
<tbody>
|
|
58
|
+
<% @deliveries.each do |delivery| %>
|
|
59
|
+
<tr class="shiplog-delivery-row">
|
|
60
|
+
<td><%= delivery.event %></td>
|
|
61
|
+
<td><%= delivery.action %></td>
|
|
62
|
+
<td><%= delivery.repository_full_name %></td>
|
|
63
|
+
<td><%= delivery.received_at && l(delivery.received_at, format: :short) %></td>
|
|
64
|
+
<td><%= delivery.processed_at && l(delivery.processed_at, format: :short) %></td>
|
|
65
|
+
<td><%= delivery.error %></td>
|
|
66
|
+
</tr>
|
|
67
|
+
<% end %>
|
|
68
|
+
</tbody>
|
|
69
|
+
</table>
|
|
70
|
+
<% end %>
|
|
71
|
+
<% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%# locals: (entry:) %>
|
|
2
|
+
<%# one changelog entry: category pill ("New" for features, "Improved" for changes), date, title and body %>
|
|
3
|
+
<li class="<%= token_list(shiplog_class(:entry, entry.kind), (shiplog_class(:entry_feature) if entry.feature?)) %>">
|
|
4
|
+
<span class="<%= shiplog_class(:entry_category) %>"><%= t("shiplog.categories.#{entry.kind}") %></span>
|
|
5
|
+
<time class="<%= shiplog_class(:entry_date) %>" datetime="<%= entry.released_on.iso8601 %>"><%= l(entry.released_on, format: t("shiplog.date_format", default: "%-d %B %Y")) %></time>
|
|
6
|
+
<h3 class="<%= shiplog_class(:entry_title) %>"><%= entry.title %></h3>
|
|
7
|
+
<div class="<%= shiplog_class(:entry_body) %>"><%= simple_format(h(entry.body)) %></div>
|
|
8
|
+
</li>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%# locals: (month:, entries:) %>
|
|
2
|
+
<%# one month of the changelog (month is its first day) and its entries, newest first %>
|
|
3
|
+
<% heading_id = "shiplog-month-#{month.strftime("%Y-%m")}" %>
|
|
4
|
+
<section class="<%= shiplog_class(:month) %>" aria-labelledby="<%= heading_id %>">
|
|
5
|
+
<h2 class="<%= shiplog_class(:month_title) %>" id="<%= heading_id %>"><%= l(month, format: "%B %Y") %></h2>
|
|
6
|
+
<ul class="<%= shiplog_class(:entries) %>">
|
|
7
|
+
<%= render partial: "shiplog/changelogs/entry", collection: entries, as: :entry %>
|
|
8
|
+
</ul>
|
|
9
|
+
</section>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% content_for :title, t("shiplog.changelog.title") %>
|
|
2
|
+
<%= render layout: "shiplog/page", locals: { name: "changelog" } do %>
|
|
3
|
+
<%= render "shiplog/page_header", title: t("shiplog.changelog.title"), lede: t("shiplog.changelog.lede") %>
|
|
4
|
+
<% if @months.empty? %>
|
|
5
|
+
<%= render "shiplog/empty" %>
|
|
6
|
+
<% else %>
|
|
7
|
+
<div class="<%= shiplog_class(:months) %>">
|
|
8
|
+
<% @months.each do |month, entries| %>
|
|
9
|
+
<%= render "shiplog/changelogs/month", month:, entries: %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<%= render "shiplog/pagination",
|
|
14
|
+
newer_path: (changelog_path(page: (@page - 1 if @page > 2)) if @page > 1),
|
|
15
|
+
older_path: (changelog_path(page: @page + 1) if @older) %>
|
|
16
|
+
<% end %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%# locals: (entry:) %>
|
|
2
|
+
<%# one roadmap item: its public title and body %>
|
|
3
|
+
<li class="<%= shiplog_class(:card) %>">
|
|
4
|
+
<h3 class="<%= shiplog_class(:card_title) %>"><%= entry.title %></h3>
|
|
5
|
+
<div class="<%= shiplog_class(:card_body) %>"><%= simple_format(h(entry.body)) %></div>
|
|
6
|
+
</li>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%# locals: (column:, entries:) %>
|
|
2
|
+
<%# one roadmap column (in_progress, coming_soon or later) and its approved entries %>
|
|
3
|
+
<% heading_id = "shiplog-column-#{column.dasherize}" %>
|
|
4
|
+
<section class="<%= shiplog_class(:column, column) %>" aria-labelledby="<%= heading_id %>">
|
|
5
|
+
<header class="<%= shiplog_class(:column_header) %>">
|
|
6
|
+
<h2 class="<%= shiplog_class(:column_title) %>" id="<%= heading_id %>"><%= t("shiplog.columns.#{column}") %></h2>
|
|
7
|
+
<span class="<%= shiplog_class(:column_count) %>"><%= entries.size %></span>
|
|
8
|
+
<p class="<%= shiplog_class(:column_description) %>"><%= t("shiplog.columns.#{column}_description") %></p>
|
|
9
|
+
</header>
|
|
10
|
+
<% if entries.empty? %>
|
|
11
|
+
<p class="<%= shiplog_class(:column_empty) %>"><%= t("shiplog.empty") %></p>
|
|
12
|
+
<% else %>
|
|
13
|
+
<ol class="<%= shiplog_class(:cards) %>">
|
|
14
|
+
<%= render partial: "shiplog/roadmaps/card", collection: entries, as: :entry %>
|
|
15
|
+
</ol>
|
|
16
|
+
<% end %>
|
|
17
|
+
</section>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<% content_for :title, t("shiplog.roadmap.title") %>
|
|
2
|
+
<%= render layout: "shiplog/page", locals: { name: "roadmap" } do %>
|
|
3
|
+
<%= render "shiplog/page_header", title: t("shiplog.roadmap.title"), lede: t("shiplog.roadmap.lede") %>
|
|
4
|
+
<div class="<%= shiplog_class(:columns) %>">
|
|
5
|
+
<% @columns.each do |column, items| %>
|
|
6
|
+
<%= render "shiplog/roadmaps/column", column:, entries: items.map(&:entry) %>
|
|
7
|
+
<% end %>
|
|
8
|
+
</div>
|
|
9
|
+
<% end %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Shiplog::Admin::Engine.routes.draw do
|
|
2
|
+
root to: "entries#index"
|
|
3
|
+
resources :entries, only: %i[index edit update] do
|
|
4
|
+
scope module: :entries do
|
|
5
|
+
resource :approval, only: :create
|
|
6
|
+
resource :discard, only: %i[create destroy]
|
|
7
|
+
resource :rewrite, only: :create
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
resource :board, only: %i[show update]
|
|
11
|
+
resource :sync, only: %i[show create]
|
|
12
|
+
get "assets/:version/:name", to: "/shiplog/frontends#show", as: :frontend, constraints: { version: /[^\/]+/, name: /[a-z_]+\.(css|js)/ }
|
|
13
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
en:
|
|
2
|
+
shiplog:
|
|
3
|
+
columns:
|
|
4
|
+
in_progress: In progress
|
|
5
|
+
coming_soon: Coming soon
|
|
6
|
+
later: Later
|
|
7
|
+
in_progress_description: What we're building now
|
|
8
|
+
coming_soon_description: Planned next
|
|
9
|
+
later_description: On our radar
|
|
10
|
+
categories:
|
|
11
|
+
feature: New
|
|
12
|
+
change: Improved
|
|
13
|
+
empty: Nothing here yet
|
|
14
|
+
date_format: "%-d %B %Y"
|
|
15
|
+
roadmap:
|
|
16
|
+
title: Roadmap
|
|
17
|
+
lede: What we're working on and what comes next.
|
|
18
|
+
changelog:
|
|
19
|
+
title: Changelog
|
|
20
|
+
lede: New features and improvements, as they ship.
|
|
21
|
+
pagination:
|
|
22
|
+
label: Pagination
|
|
23
|
+
older: Older
|
|
24
|
+
newer: Newer
|
|
25
|
+
admin:
|
|
26
|
+
title: Shiplog admin
|
|
27
|
+
nav:
|
|
28
|
+
drafts: Drafts
|
|
29
|
+
board: Roadmap board
|
|
30
|
+
sync: Sync
|
|
31
|
+
entries:
|
|
32
|
+
title: Entries
|
|
33
|
+
filters: Filter entries
|
|
34
|
+
all_kinds: All kinds
|
|
35
|
+
empty: No entries match.
|
|
36
|
+
pagination: Pagination
|
|
37
|
+
older: Older
|
|
38
|
+
newer: Newer
|
|
39
|
+
saved: Entry saved.
|
|
40
|
+
approved: Entry approved.
|
|
41
|
+
discarded: Entry discarded.
|
|
42
|
+
restored: Entry restored to draft.
|
|
43
|
+
invalid_transition: That action is not available for this entry in its current state.
|
|
44
|
+
rewrite_queued: Rewrite queued. Refresh in a moment.
|
|
45
|
+
edit: Edit
|
|
46
|
+
edit_title: "Edit %{kind}"
|
|
47
|
+
approve: Approve
|
|
48
|
+
discard: Discard
|
|
49
|
+
restore: Restore
|
|
50
|
+
save: Save
|
|
51
|
+
regenerate: Regenerate
|
|
52
|
+
regenerate_warning: Regenerating unpublishes this entry until re-approved.
|
|
53
|
+
source_changed: GitHub text changed since this draft
|
|
54
|
+
rewrite_error: Rewrite failed
|
|
55
|
+
not_rewritten: Not rewritten yet
|
|
56
|
+
public_text: Public text
|
|
57
|
+
source: GitHub source
|
|
58
|
+
back: Back to drafts
|
|
59
|
+
statuses:
|
|
60
|
+
draft: Drafts
|
|
61
|
+
approved: Approved
|
|
62
|
+
discarded: Discarded
|
|
63
|
+
kinds:
|
|
64
|
+
roadmap: Roadmap
|
|
65
|
+
feature: Feature
|
|
66
|
+
change: Change
|
|
67
|
+
columns:
|
|
68
|
+
entry: Entry
|
|
69
|
+
kind: Kind
|
|
70
|
+
source: Source
|
|
71
|
+
actions: Actions
|
|
72
|
+
fields:
|
|
73
|
+
title: Title
|
|
74
|
+
body: Text
|
|
75
|
+
released_on: Released on
|
|
76
|
+
reason: Reason
|
|
77
|
+
board:
|
|
78
|
+
title: Roadmap board
|
|
79
|
+
help: Drag cards between columns to pin and reorder them. Without JavaScript, use the Move button on each card.
|
|
80
|
+
updated: Board updated.
|
|
81
|
+
not_approved: Not approved
|
|
82
|
+
pinned: Pinned
|
|
83
|
+
move_to: Move to
|
|
84
|
+
move: Move
|
|
85
|
+
unpin: Unpin
|
|
86
|
+
sync:
|
|
87
|
+
title: Sync status
|
|
88
|
+
queued: Sync queued.
|
|
89
|
+
webhook_secret_missing: The webhook secret is not configured, so GitHub webhooks are rejected.
|
|
90
|
+
github_token_missing: The GitHub token is not configured, so syncing cannot run.
|
|
91
|
+
sync_now: Sync now
|
|
92
|
+
sync_and_scan: Sync and scan for changes
|
|
93
|
+
repositories: Repositories
|
|
94
|
+
no_repositories: No repositories are configured.
|
|
95
|
+
repository: Repository
|
|
96
|
+
synced_at: Last synced
|
|
97
|
+
changes_scanned_at: Changes scanned
|
|
98
|
+
last_error: Last error
|
|
99
|
+
never: Never
|
|
100
|
+
deliveries: Recent webhook deliveries
|
|
101
|
+
no_deliveries: No deliveries yet.
|
|
102
|
+
event: Event
|
|
103
|
+
action: Action
|
|
104
|
+
received_at: Received
|
|
105
|
+
processed_at: Processed
|
|
106
|
+
error: Error
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Shiplog::Engine.routes.draw do
|
|
2
|
+
get Shiplog.config.roadmap_path, to: "roadmaps#show", as: :roadmap
|
|
3
|
+
get Shiplog.config.changelog_path, to: "changelogs#show", as: :changelog
|
|
4
|
+
post "shiplog/github/webhook", to: "webhooks#create", as: :github_webhook
|
|
5
|
+
get "shiplog/assets/:version/:name", to: "frontends#show", as: :frontend, constraints: { version: /[^\/]+/, name: /[a-z_]+\.(css|js)/ }
|
|
6
|
+
end
|