shiplog 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +59 -0
- data/README.md +140 -5
- data/app/controllers/shiplog/admin/area_detections_controller.rb +15 -0
- data/app/controllers/shiplog/admin/area_translations_controller.rb +19 -0
- data/app/controllers/shiplog/admin/areas/merges_controller.rb +19 -0
- data/app/controllers/shiplog/admin/areas/moves_controller.rb +13 -0
- data/app/controllers/shiplog/admin/areas_controller.rb +48 -0
- data/app/controllers/shiplog/admin/entries/images/moves_controller.rb +20 -0
- data/app/controllers/shiplog/admin/entries/images_controller.rb +76 -0
- data/app/controllers/shiplog/admin/entries/translations/retranslations_controller.rb +23 -0
- data/app/controllers/shiplog/admin/entries/translations_controller.rb +35 -0
- data/app/controllers/shiplog/admin/entries_controller.rb +9 -3
- data/app/controllers/shiplog/changelog_entries_controller.rb +26 -0
- data/app/controllers/shiplog/changelog_feeds_controller.rb +19 -0
- data/app/controllers/shiplog/changelogs_controller.rb +21 -5
- data/app/controllers/shiplog/public_controller.rb +55 -0
- data/app/controllers/shiplog/roadmaps_controller.rb +3 -1
- data/app/frontend/shiplog/admin.css +9 -0
- data/app/frontend/shiplog/shiplog.css +47 -2
- data/app/helpers/shiplog/admin/entries_helper.rb +15 -0
- data/app/helpers/shiplog/permalink_helper.rb +113 -0
- data/app/helpers/shiplog/roadmap_helper.rb +9 -0
- data/app/helpers/shiplog/unseen_helper.rb +9 -0
- data/app/jobs/shiplog/detect_areas_job.rb +18 -0
- data/app/jobs/shiplog/rewrite_entry_job.rb +4 -1
- data/app/jobs/shiplog/translate_area_job.rb +25 -0
- data/app/jobs/shiplog/translate_entry_job.rb +25 -0
- data/app/models/shiplog/area.rb +156 -0
- data/app/models/shiplog/area_detector.rb +146 -0
- data/app/models/shiplog/area_translator.rb +77 -0
- data/app/models/shiplog/entry/images.rb +127 -0
- data/app/models/shiplog/entry.rb +204 -5
- data/app/models/shiplog/entry_translation.rb +15 -0
- data/app/models/shiplog/image_fetcher/downloader.rb +98 -0
- data/app/models/shiplog/image_fetcher/sources.rb +41 -0
- data/app/models/shiplog/image_fetcher.rb +72 -0
- data/app/models/shiplog/image_type.rb +111 -0
- data/app/models/shiplog/rewriter/prompt.rb +31 -7
- data/app/models/shiplog/rewriter.rb +41 -23
- data/app/models/shiplog/setting.rb +19 -0
- data/app/models/shiplog/translator/prompt.rb +40 -0
- data/app/models/shiplog/translator.rb +99 -0
- data/app/views/layouts/shiplog/application.html.erb +3 -0
- data/app/views/shiplog/_empty.html.erb +1 -1
- data/app/views/shiplog/_pagination.html.erb +3 -3
- data/app/views/shiplog/admin/_nav.html.erb +2 -1
- data/app/views/shiplog/admin/areas/index.html.erb +78 -0
- data/app/views/shiplog/admin/entries/_entry_row.html.erb +3 -0
- data/app/views/shiplog/admin/entries/_images.html.erb +41 -0
- data/app/views/shiplog/admin/entries/_translations.html.erb +42 -0
- data/app/views/shiplog/admin/entries/edit.html.erb +12 -0
- data/app/views/shiplog/changelog_entries/show.html.erb +25 -0
- data/app/views/shiplog/changelog_feeds/show.atom.builder +24 -0
- data/app/views/shiplog/changelogs/_areas.html.erb +8 -0
- data/app/views/shiplog/changelogs/_entry.html.erb +2 -5
- data/app/views/shiplog/changelogs/_entry_content.html.erb +10 -0
- data/app/views/shiplog/changelogs/_search.html.erb +4 -2
- data/app/views/shiplog/changelogs/show.html.erb +11 -4
- data/app/views/shiplog/roadmaps/_card.html.erb +2 -2
- data/app/views/shiplog/roadmaps/_column.html.erb +3 -3
- data/app/views/shiplog/roadmaps/_shipped.html.erb +1 -1
- data/app/views/shiplog/roadmaps/show.html.erb +2 -2
- data/config/admin_routes.rb +16 -0
- data/config/locales/en.yml +85 -0
- data/config/locales/es.yml +223 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20261027000002_shiplog_0_3_0.rb +105 -0
- data/lib/generators/shiplog/install/install_generator.rb +10 -1
- data/lib/generators/shiplog/install/templates/initializer.rb.tt +45 -1
- data/lib/shiplog/classes.rb +10 -1
- data/lib/shiplog/configuration.rb +92 -3
- data/lib/shiplog/engine.rb +3 -0
- data/lib/shiplog/github/queries.rb +13 -1
- data/lib/shiplog/version.rb +1 -1
- data/lib/shiplog.rb +73 -0
- data/lib/tasks/shiplog.rake +29 -0
- metadata +39 -1
|
@@ -97,7 +97,12 @@ Shiplog.configure do |config|
|
|
|
97
97
|
|
|
98
98
|
# Public paths, read when routes are drawn.
|
|
99
99
|
# config.roadmap_path = "roadmap"
|
|
100
|
-
# config.changelog_path = "changelog"
|
|
100
|
+
# config.changelog_path = "changelog" # a-z, 0-9, - and _; "docs/news" nests; outer slashes are stripped
|
|
101
|
+
|
|
102
|
+
# The site's canonical origin (scheme, host, optional port; no path). The Atom feed's ids and links, og:url
|
|
103
|
+
# and the other absolute URLs use it instead of the request's host, which varies (apex vs www, a forged
|
|
104
|
+
# X-Forwarded-Host). Behind a proxy, also set config.assume_ssl = true (or send X-Forwarded-Proto) in Rails.
|
|
105
|
+
# config.canonical_url = "https://example.com"
|
|
101
106
|
|
|
102
107
|
# Pages added to the Roadmap / Changelog switcher. label and path are Strings, or callables evaluated in the
|
|
103
108
|
# view (route helpers, I18n). Render the same switcher on your own pages with `helper Shiplog::SwitcherHelper`
|
|
@@ -117,6 +122,45 @@ Shiplog.configure do |config|
|
|
|
117
122
|
# The public changelog's search box and ?q= filtering. Set to false to turn search off.
|
|
118
123
|
# config.changelog_search = true
|
|
119
124
|
|
|
125
|
+
# "What's new". Shiplog doesn't know your users, so you supply the "last seen" time. This is called with the
|
|
126
|
+
# controller on every changelog index or permalink view, BEFORE the page renders (a 304 revisit included), so a
|
|
127
|
+
# navbar badge in the same response is current. It may write and runs on every visit: keep it cheap, and write
|
|
128
|
+
# only when something is new. Show `Shiplog.unseen_count(since: user.changelog_seen_at)` (at most 99, counted on
|
|
129
|
+
# approved_at; `helper Shiplog::UnseenHelper` and `shiplog_unseen_count(since)` in views) next to your Changelog
|
|
130
|
+
# link. current_user is often private, hence controller.send (or read a Current attribute).
|
|
131
|
+
# config.on_changelog_seen = lambda do |controller|
|
|
132
|
+
# user = controller.send(:current_user)
|
|
133
|
+
# user.update_column(:changelog_seen_at, Time.current) if user && Shiplog.unseen_count(since: user.changelog_seen_at).positive?
|
|
134
|
+
# end
|
|
135
|
+
|
|
136
|
+
# The 0.3.0 migration gives entries approved so far their permalink slug. The public pages never write (a GET
|
|
137
|
+
# may hit a read replica), so an entry approved by older code during a deploy window has no permalink until
|
|
138
|
+
# `bin/rails shiplog:backfill_slugs` runs. Permalink pages set `content_for :shiplog_og` (Open Graph tags),
|
|
139
|
+
# `content_for :shiplog_description` and `content_for :shiplog_feed` (the Atom discovery link); Shiplog's own
|
|
140
|
+
# layout renders them, so emit them in your layout's <head> when you set `config.public_layout`.
|
|
141
|
+
|
|
142
|
+
# Product areas. The rewriter's reply may name an area (an existing one, or a new name of 1-3 words); Shiplog
|
|
143
|
+
# creates it while fewer than max_areas exist, so the AI cannot sprawl. The changelog offers an area filter
|
|
144
|
+
# (?area=slug) once two areas have published entries. Manage areas in the admin ("Areas"): rename, reorder,
|
|
145
|
+
# merge, delete, and "Detect areas" to backfill entries that have none (it reuses config.rewriter).
|
|
146
|
+
# Entries you set by hand on the edit page are never changed by the AI.
|
|
147
|
+
# config.max_areas = 8
|
|
148
|
+
|
|
149
|
+
# Screenshots in entries. They need Active Storage in your app (require "active_storage/engine", and its tables);
|
|
150
|
+
# without it they stay off and Shiplog logs that once at boot. When a draft is written, the images in the GitHub
|
|
151
|
+
# issue or pull request body are attached to the entry (GitHub image hosts only, 5 MB and PNG, JPEG, GIF or
|
|
152
|
+
# WebP at most; the token needs no extra permission). You can also upload images on the entry edit page.
|
|
153
|
+
# config.images = true
|
|
154
|
+
# config.max_images_per_entry = 4
|
|
155
|
+
|
|
156
|
+
# Translations of entries. Entries are translated into each of these locales with config.rewriter when they are
|
|
157
|
+
# approved, and again when the English title or body of an approved entry changes; the translation is published
|
|
158
|
+
# at once, with no second approval. One LLM call per entry, locale and change. Admins can edit a translation on
|
|
159
|
+
# the entry page (kept until the English text changes) or retranslate it. Pages render the translation when
|
|
160
|
+
# I18n.locale is one of these and it matches the current English text, and English otherwise. Your routes
|
|
161
|
+
# decide which URLs set the locale (e.g. a /es scope). The default locale is dropped from the list.
|
|
162
|
+
# config.locales = [] # e.g. [:es]
|
|
163
|
+
|
|
120
164
|
# Callables above cannot be part of the public pages' ETag. Bump this when the output of a callable you
|
|
121
165
|
# configured changes (a new label, another path) so cached pages are refreshed.
|
|
122
166
|
# config.cache_version = nil
|
data/lib/shiplog/classes.rb
CHANGED
|
@@ -49,7 +49,16 @@ module Shiplog
|
|
|
49
49
|
search_button: "shiplog-search__button",
|
|
50
50
|
search_summary: "shiplog-search__summary",
|
|
51
51
|
search_clear: "shiplog-search__clear",
|
|
52
|
-
search_empty: "shiplog-search__empty"
|
|
52
|
+
search_empty: "shiplog-search__empty",
|
|
53
|
+
permalink: "shiplog-permalink",
|
|
54
|
+
permalink_back: "shiplog-permalink__back",
|
|
55
|
+
permalink_nav: "shiplog-permalink__nav",
|
|
56
|
+
permalink_nav_link: "shiplog-permalink__nav-link",
|
|
57
|
+
areas: "shiplog-areas",
|
|
58
|
+
area_link: "shiplog-areas__link",
|
|
59
|
+
entry_area: "shiplog-entry__area",
|
|
60
|
+
entry_images: "shiplog-entry__images",
|
|
61
|
+
entry_image: "shiplog-entry__image"
|
|
53
62
|
}.freeze
|
|
54
63
|
|
|
55
64
|
KEYS = BEM.keys.freeze
|
|
@@ -5,7 +5,7 @@ module Shiplog
|
|
|
5
5
|
:rewriter, :product_name, :audience, :tone, :prompt_builder,
|
|
6
6
|
:public_layout, :admin_layout,
|
|
7
7
|
:stylesheet, :admin_flash, :authorize_admin, :current_admin,
|
|
8
|
-
:roadmap_path
|
|
8
|
+
:roadmap_path
|
|
9
9
|
|
|
10
10
|
ADMIN_CHROMES = %i[standalone hosted].freeze
|
|
11
11
|
# Segments of only dots ("." and "..") are path traversal, not names.
|
|
@@ -16,16 +16,40 @@ module Shiplog
|
|
|
16
16
|
# Path of the lockfile read in merged pull requests to detect gem upgrades (see config.repositories `gem:`).
|
|
17
17
|
attr_accessor :lockfile_path
|
|
18
18
|
|
|
19
|
+
# The public changelog's path: segments of a-z, 0-9, dashes and underscores, joined by slashes ("changelog",
|
|
20
|
+
# "docs/whats_new"); surrounding slashes are stripped. Permalinks live below it and the Atom feed is
|
|
21
|
+
# "<path>.atom". Read when routes are drawn.
|
|
22
|
+
attr_reader :changelog_path
|
|
23
|
+
CHANGELOG_PATH = %r{\A[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*\z}
|
|
24
|
+
|
|
25
|
+
# The site's canonical origin, e.g. "https://trackberry.com" (nil by default). When set, the Atom feed's ids
|
|
26
|
+
# and links, entry links, og:url and the other absolute URLs use it instead of the request's host, which
|
|
27
|
+
# varies (apex and www, a forged X-Forwarded-Host). An origin only: scheme, host and optional port. Any path
|
|
28
|
+
# prefix of the engine's mount comes from the routes, so it needs no place here.
|
|
29
|
+
attr_reader :canonical_url
|
|
30
|
+
|
|
19
31
|
attr_reader :admin_chrome, :classes, :public_parent_controller, :admin_parent_controller
|
|
20
32
|
|
|
21
33
|
# Product updates navigation and roadmap options.
|
|
22
34
|
attr_accessor :feedback_path
|
|
23
35
|
# Whether the public changelog has a search box and honours ?q= (default true).
|
|
24
36
|
attr_accessor :changelog_search
|
|
37
|
+
# Called with the controller on each changelog index or permalink view (200 or 304 answers), e.g. to stamp
|
|
38
|
+
# the visitor's "last seen" time. It runs BEFORE the page renders (so a badge in the same response is
|
|
39
|
+
# current), on every visit, and may write: keep it cheap, e.g. write only when the stored time is older than
|
|
40
|
+
# the newest entry. Nil by default. Not part of the ETag: it doesn't change the markup.
|
|
41
|
+
attr_reader :on_changelog_seen
|
|
25
42
|
# Bump it when the output of a callable you configured changes (extra_pages, repository_labels, feedback_path):
|
|
26
43
|
# callables cannot be part of the ETag, so this is how cached public pages learn about it.
|
|
27
44
|
attr_accessor :cache_version
|
|
28
45
|
attr_reader :extra_pages, :repository_labels, :roadmap_shipped_count
|
|
46
|
+
# How many product areas the AI may create (default 8). Past it, entries can only join existing areas.
|
|
47
|
+
# Not part of render_digest: the markup depends on the areas themselves, which the ETag carries.
|
|
48
|
+
attr_reader :max_areas
|
|
49
|
+
# Screenshots in entries. `images` (default true) is effective only when Active Storage is loaded in the host
|
|
50
|
+
# (see Shiplog.images?). `max_images_per_entry` (default 4) caps the images fetched from GitHub per entry.
|
|
51
|
+
attr_reader :images, :max_images_per_entry
|
|
52
|
+
LOCALE_FORMAT = /\A[A-Za-z][A-Za-z0-9_-]*\z/
|
|
29
53
|
|
|
30
54
|
EXTRA_PAGE_KEYS = %i[label path].freeze
|
|
31
55
|
|
|
@@ -43,10 +67,10 @@ module Shiplog
|
|
|
43
67
|
stable = ->(value) { value.respond_to?(:call) ? "callable" : value }
|
|
44
68
|
data = [
|
|
45
69
|
classes.sort_by { |key, _| key.to_s }.map { |key, value| [ key.to_s, Array(value).map(&:to_s) ] },
|
|
46
|
-
stylesheet, roadmap_shipped_count, cache_version,
|
|
70
|
+
stylesheet, roadmap_shipped_count, cache_version, product_name, canonical_url,
|
|
47
71
|
extra_pages.map { |page| [ stable.(page[:label]), stable.(page[:path]) ] },
|
|
48
72
|
repository_labels.sort_by { |name, _| name.to_s }.map { |name, label| [ name, stable.(label) ] },
|
|
49
|
-
stable.(feedback_path), changelog_search
|
|
73
|
+
stable.(feedback_path), changelog_search, images, max_images_per_entry, locales
|
|
50
74
|
]
|
|
51
75
|
Digest::SHA256.hexdigest(JSON.generate(data))
|
|
52
76
|
end
|
|
@@ -92,6 +116,51 @@ module Shiplog
|
|
|
92
116
|
@roadmap_shipped_count = value
|
|
93
117
|
end
|
|
94
118
|
|
|
119
|
+
def max_areas=(value)
|
|
120
|
+
raise ArgumentError, "max_areas must be a non-negative Integer, got #{value.inspect}" unless value.is_a?(Integer) && value >= 0
|
|
121
|
+
@max_areas = value
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def images=(value)
|
|
125
|
+
raise ArgumentError, "images must be true or false, got #{value.inspect}" unless [ true, false ].include?(value)
|
|
126
|
+
@images = value
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def max_images_per_entry=(value)
|
|
130
|
+
raise ArgumentError, "max_images_per_entry must be a non-negative Integer, got #{value.inspect}" unless value.is_a?(Integer) && value >= 0
|
|
131
|
+
@max_images_per_entry = value
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Locales entries are translated into when approved, e.g. [:es] (default none), as symbols and without the
|
|
135
|
+
# default locale (I18n.default_locale, read each time so an initializer that sets it later still counts): it
|
|
136
|
+
# is the language entries are written in. See Shiplog::Translator.
|
|
137
|
+
def locales = @locales - [ I18n.default_locale.to_sym ]
|
|
138
|
+
|
|
139
|
+
# Sets the locales to translate into: an Array of locale Symbols or Strings (see #locales).
|
|
140
|
+
def locales=(value)
|
|
141
|
+
unless value.is_a?(Array) && value.all? { |item| (item.is_a?(Symbol) || item.is_a?(String)) && LOCALE_FORMAT.match?(item.to_s) }
|
|
142
|
+
raise ArgumentError, "locales must be an Array of locale Symbols or Strings like [:es, \"pt-BR\"], got #{value.inspect}"
|
|
143
|
+
end
|
|
144
|
+
@locales = value.map(&:to_sym).uniq.freeze
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def changelog_path=(value)
|
|
148
|
+
path = value.gsub(%r{\A/+|/+\z}, "") if value.is_a?(String)
|
|
149
|
+
unless path && CHANGELOG_PATH.match?(path)
|
|
150
|
+
raise ArgumentError, "changelog_path must be a path of letters, digits, dashes and underscores, with / between segments (like \"changelog\"), got #{value.inspect}"
|
|
151
|
+
end
|
|
152
|
+
@changelog_path = path
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def canonical_url=(value)
|
|
156
|
+
@canonical_url = normalize_canonical_url(value)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def on_changelog_seen=(value)
|
|
160
|
+
raise ArgumentError, "on_changelog_seen must be nil or a callable, got #{value.inspect}" unless value.nil? || value.respond_to?(:call)
|
|
161
|
+
@on_changelog_seen = value
|
|
162
|
+
end
|
|
163
|
+
|
|
95
164
|
# :standalone (default) wraps the admin in Shiplog's own page chrome; :hosted renders it inside the
|
|
96
165
|
# layout of config.admin_layout (compact sub-nav, no page chrome of its own).
|
|
97
166
|
def admin_chrome=(value)
|
|
@@ -158,6 +227,10 @@ module Shiplog
|
|
|
158
227
|
@repository_labels = {}.freeze
|
|
159
228
|
@roadmap_shipped_count = 4
|
|
160
229
|
@changelog_search = true
|
|
230
|
+
@max_areas = 8
|
|
231
|
+
@images = true
|
|
232
|
+
@max_images_per_entry = 4
|
|
233
|
+
@locales = [].freeze
|
|
161
234
|
end
|
|
162
235
|
|
|
163
236
|
def resolved_github_token
|
|
@@ -196,6 +269,22 @@ module Shiplog
|
|
|
196
269
|
|
|
197
270
|
private
|
|
198
271
|
|
|
272
|
+
def normalize_canonical_url(value)
|
|
273
|
+
return if value.nil? || (value.is_a?(String) && value.strip.empty?)
|
|
274
|
+
raise ArgumentError, "canonical_url must be a String like \"https://example.com\", got #{value.inspect}" unless value.is_a?(String)
|
|
275
|
+
|
|
276
|
+
uri = begin
|
|
277
|
+
URI.parse(value.strip)
|
|
278
|
+
rescue URI::InvalidURIError
|
|
279
|
+
nil
|
|
280
|
+
end
|
|
281
|
+
valid = uri.is_a?(URI::HTTP) && uri.host.present? && [ "", "/" ].include?(uri.path) && uri.query.nil? && uri.fragment.nil? && uri.userinfo.nil?
|
|
282
|
+
raise ArgumentError, "canonical_url must be an http(s) origin without path, query or credentials (\"https://example.com\"), got #{value.inspect}" unless valid
|
|
283
|
+
|
|
284
|
+
port = ":#{uri.port}" unless uri.port == uri.default_port
|
|
285
|
+
"#{uri.scheme}://#{uri.host.downcase}#{port}"
|
|
286
|
+
end
|
|
287
|
+
|
|
199
288
|
def normalize_repositories(value)
|
|
200
289
|
raise ArgumentError, "config.repositories must be an Array, got #{value.inspect}" unless value.is_a?(Array)
|
|
201
290
|
|
data/lib/shiplog/engine.rb
CHANGED
|
@@ -133,7 +133,19 @@ module Shiplog
|
|
|
133
133
|
}
|
|
134
134
|
GRAPHQL
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
# The rendered HTML of one issue or pull request body (`$id` is its node id). Kept out of the sync documents so
|
|
137
|
+
# their payloads don't grow: it is only asked for when a draft is written, to find screenshots. On private
|
|
138
|
+
# repositories its <img src> URLs are short-lived signed URLs.
|
|
139
|
+
BODY_HTML = <<~GRAPHQL.freeze
|
|
140
|
+
query($id: ID!) {
|
|
141
|
+
node(id: $id) {
|
|
142
|
+
... on Issue { bodyHTML }
|
|
143
|
+
... on PullRequest { bodyHTML }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
GRAPHQL
|
|
147
|
+
|
|
148
|
+
ALL = { ISSUES:, PULL_REQUESTS:, ISSUE:, PULL_REQUEST:, COMMITS:, BLOB_TEXT:, COMMIT_PULL_REQUESTS:, BODY_HTML: }.freeze
|
|
137
149
|
end
|
|
138
150
|
end
|
|
139
151
|
end
|
data/lib/shiplog/version.rb
CHANGED
data/lib/shiplog.rb
CHANGED
|
@@ -12,5 +12,78 @@ module Shiplog
|
|
|
12
12
|
def config = (@config ||= Configuration.new)
|
|
13
13
|
def configure = yield(config)
|
|
14
14
|
def reset_config! = (@config = nil)
|
|
15
|
+
|
|
16
|
+
# The number of approved features and changes approved after `since`, or all of them while it is nil, capped
|
|
17
|
+
# at 99. Counted on approved_at (when the entry was approved), not released_on (the list's order). The host supplies the timestamp (Shiplog doesn't know its users): see config.on_changelog_seen.
|
|
18
|
+
def unseen_count(since: nil) = Entry.unseen_count(since:)
|
|
19
|
+
|
|
20
|
+
ACTIVE_STORAGE_TABLES = %w[active_storage_attachments active_storage_blobs].freeze
|
|
21
|
+
|
|
22
|
+
# Whether the host loaded Active Storage (its engine and the model macros). The declaration on Entry needs
|
|
23
|
+
# only this, so it never touches the database.
|
|
24
|
+
def active_storage_loaded? = !!(defined?(ActiveStorage::Engine) && ActiveRecord::Base.respond_to?(:has_many_attached))
|
|
25
|
+
|
|
26
|
+
# Whether screenshots can work: Active Storage is loaded and its tables exist (a host that loads
|
|
27
|
+
# `rails/all` but never ran active_storage:install would otherwise fail on every query). Without either,
|
|
28
|
+
# every image feature stays off instead of failing.
|
|
29
|
+
def active_storage? = active_storage_loaded? && active_storage_tables?
|
|
30
|
+
|
|
31
|
+
# Whether the Active Storage tables exist. Checked lazily, on first use, then remembered for the life of the
|
|
32
|
+
# process, so public pages never look at the schema again. When the database can't be reached (at boot, say)
|
|
33
|
+
# the answer is "not yet known": false for now, and asked again next time. The reason is logged once.
|
|
34
|
+
def active_storage_tables?
|
|
35
|
+
return @active_storage_tables unless @active_storage_tables.nil?
|
|
36
|
+
|
|
37
|
+
present = ActiveRecord::Base.with_connection { |connection| ACTIVE_STORAGE_TABLES.all? { |table| connection.data_source_exists?(table) } }
|
|
38
|
+
log_images_off("Active Storage's tables are missing (#{ACTIVE_STORAGE_TABLES.join(", ")}). Run bin/rails active_storage:install and db:migrate to enable them.") unless present || !config.images
|
|
39
|
+
@active_storage_tables = present
|
|
40
|
+
rescue ActiveRecord::ActiveRecordError
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Forgets what was learned about the schema and what was logged (tests, and hosts that create the tables at runtime).
|
|
45
|
+
def reset_images_checks!
|
|
46
|
+
@active_storage_tables = nil
|
|
47
|
+
@images_off_logged = nil
|
|
48
|
+
@image_variants = nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Logged once at boot (see Shiplog::Engine): screenshots wanted but Active Storage missing. A missing
|
|
52
|
+
# table is logged by the check itself, the first time it runs.
|
|
53
|
+
def log_images_status
|
|
54
|
+
return unless config.images
|
|
55
|
+
|
|
56
|
+
if !active_storage_loaded? then log_images_off("Active Storage is not loaded. Require active_storage/engine to enable them.")
|
|
57
|
+
else active_storage_tables?
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Whether the admin can show thumbnails as variants: the image_processing gem is loaded and so is a
|
|
62
|
+
# processor for it (ruby-vips, which must load, or mini_magick). Otherwise the originals are shown.
|
|
63
|
+
def image_variants?
|
|
64
|
+
return @image_variants unless @image_variants.nil?
|
|
65
|
+
|
|
66
|
+
@image_variants = Gem.loaded_specs.key?("image_processing") && (vips_loads? || Gem.loaded_specs.key?("mini_magick"))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Whether screenshots are in use: config.images and Active Storage.
|
|
70
|
+
def images? = config.images && active_storage?
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def vips_loads?
|
|
75
|
+
return true if defined?(::Vips)
|
|
76
|
+
|
|
77
|
+
Gem.loaded_specs.key?("ruby-vips") && require("vips")
|
|
78
|
+
rescue LoadError
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def log_images_off(reason)
|
|
83
|
+
@images_off_logged ||= Set.new
|
|
84
|
+
return unless @images_off_logged.add?(reason)
|
|
85
|
+
|
|
86
|
+
Rails.logger&.info("[shiplog] Screenshots are off: #{reason}")
|
|
87
|
+
end
|
|
15
88
|
end
|
|
16
89
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Shiplog::Engine and Shiplog::Admin::Engine share the gem's root, so Rails loads this file once for each: the
|
|
2
|
+
# guard keeps every task from running twice.
|
|
3
|
+
unless Rake::Task.task_defined?("shiplog:backfill_slugs")
|
|
4
|
+
namespace :shiplog do
|
|
5
|
+
desc "Give approved changelog entries that have no permalink slug one (entries approved by code older than 0.3.0 after the migration ran)"
|
|
6
|
+
task backfill_slugs: :environment do
|
|
7
|
+
count = Shiplog::Entry.backfill_slugs
|
|
8
|
+
puts "Shiplog: gave #{count} #{count == 1 ? "entry" : "entries"} a slug."
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "Translate approved entries and area names that have no current translation into config.locales (entries approved before 0.3.0 get none). BATCH_SIZE=100"
|
|
12
|
+
task translate_approved: :environment do
|
|
13
|
+
if Shiplog.config.locales.empty?
|
|
14
|
+
puts "Shiplog: config.locales is empty, nothing to translate."
|
|
15
|
+
elsif Shiplog.config.rewriter.nil?
|
|
16
|
+
puts "Shiplog: config.rewriter is not set, so nothing can be translated."
|
|
17
|
+
else
|
|
18
|
+
result = Shiplog::Entry.enqueue_missing_translations(batch_size: ENV.fetch("BATCH_SIZE", 100).to_i.clamp(1, 10_000))
|
|
19
|
+
approved = result[:approved]
|
|
20
|
+
puts "Shiplog: #{approved} approved #{approved == 1 ? "entry" : "entries"}."
|
|
21
|
+
result[:enqueued].each do |locale, count|
|
|
22
|
+
puts "Shiplog: #{locale}: #{count} enqueued, #{approved - count} current."
|
|
23
|
+
end
|
|
24
|
+
areas = Shiplog::Area.enqueue_missing_translations
|
|
25
|
+
puts "Shiplog: #{areas} #{areas == 1 ? "area" : "areas"} enqueued for name translation."
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shiplog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Trackberry
|
|
@@ -40,14 +40,25 @@ files:
|
|
|
40
40
|
- MIT-LICENSE
|
|
41
41
|
- README.md
|
|
42
42
|
- app/controllers/shiplog/admin/application_controller.rb
|
|
43
|
+
- app/controllers/shiplog/admin/area_detections_controller.rb
|
|
44
|
+
- app/controllers/shiplog/admin/area_translations_controller.rb
|
|
45
|
+
- app/controllers/shiplog/admin/areas/merges_controller.rb
|
|
46
|
+
- app/controllers/shiplog/admin/areas/moves_controller.rb
|
|
47
|
+
- app/controllers/shiplog/admin/areas_controller.rb
|
|
43
48
|
- app/controllers/shiplog/admin/boards_controller.rb
|
|
44
49
|
- app/controllers/shiplog/admin/entries/approvals_controller.rb
|
|
45
50
|
- app/controllers/shiplog/admin/entries/discards_controller.rb
|
|
51
|
+
- app/controllers/shiplog/admin/entries/images/moves_controller.rb
|
|
52
|
+
- app/controllers/shiplog/admin/entries/images_controller.rb
|
|
46
53
|
- app/controllers/shiplog/admin/entries/rewrites_controller.rb
|
|
54
|
+
- app/controllers/shiplog/admin/entries/translations/retranslations_controller.rb
|
|
55
|
+
- app/controllers/shiplog/admin/entries/translations_controller.rb
|
|
47
56
|
- app/controllers/shiplog/admin/entries_controller.rb
|
|
48
57
|
- app/controllers/shiplog/admin/inbox_filters.rb
|
|
49
58
|
- app/controllers/shiplog/admin/syncs_controller.rb
|
|
50
59
|
- app/controllers/shiplog/application_controller.rb
|
|
60
|
+
- app/controllers/shiplog/changelog_entries_controller.rb
|
|
61
|
+
- app/controllers/shiplog/changelog_feeds_controller.rb
|
|
51
62
|
- app/controllers/shiplog/changelogs_controller.rb
|
|
52
63
|
- app/controllers/shiplog/frontends_controller.rb
|
|
53
64
|
- app/controllers/shiplog/public_controller.rb
|
|
@@ -62,20 +73,34 @@ files:
|
|
|
62
73
|
- app/helpers/shiplog/callable_helper.rb
|
|
63
74
|
- app/helpers/shiplog/frontend_helper.rb
|
|
64
75
|
- app/helpers/shiplog/host_routes_helper.rb
|
|
76
|
+
- app/helpers/shiplog/permalink_helper.rb
|
|
65
77
|
- app/helpers/shiplog/roadmap_helper.rb
|
|
66
78
|
- app/helpers/shiplog/styling_helper.rb
|
|
67
79
|
- app/helpers/shiplog/switcher_helper.rb
|
|
80
|
+
- app/helpers/shiplog/unseen_helper.rb
|
|
68
81
|
- app/jobs/shiplog/application_job.rb
|
|
82
|
+
- app/jobs/shiplog/detect_areas_job.rb
|
|
69
83
|
- app/jobs/shiplog/refresh_job.rb
|
|
70
84
|
- app/jobs/shiplog/rewrite_entry_job.rb
|
|
71
85
|
- app/jobs/shiplog/scan_changes_job.rb
|
|
72
86
|
- app/jobs/shiplog/sync_job.rb
|
|
87
|
+
- app/jobs/shiplog/translate_area_job.rb
|
|
88
|
+
- app/jobs/shiplog/translate_entry_job.rb
|
|
73
89
|
- app/models/shiplog/application_record.rb
|
|
90
|
+
- app/models/shiplog/area.rb
|
|
91
|
+
- app/models/shiplog/area_detector.rb
|
|
92
|
+
- app/models/shiplog/area_translator.rb
|
|
74
93
|
- app/models/shiplog/board_arrangement.rb
|
|
75
94
|
- app/models/shiplog/change_candidates.rb
|
|
76
95
|
- app/models/shiplog/commit.rb
|
|
77
96
|
- app/models/shiplog/drafter.rb
|
|
78
97
|
- app/models/shiplog/entry.rb
|
|
98
|
+
- app/models/shiplog/entry/images.rb
|
|
99
|
+
- app/models/shiplog/entry_translation.rb
|
|
100
|
+
- app/models/shiplog/image_fetcher.rb
|
|
101
|
+
- app/models/shiplog/image_fetcher/downloader.rb
|
|
102
|
+
- app/models/shiplog/image_fetcher/sources.rb
|
|
103
|
+
- app/models/shiplog/image_type.rb
|
|
79
104
|
- app/models/shiplog/issue.rb
|
|
80
105
|
- app/models/shiplog/issue_form.rb
|
|
81
106
|
- app/models/shiplog/issue_link.rb
|
|
@@ -86,12 +111,15 @@ files:
|
|
|
86
111
|
- app/models/shiplog/rewriter/prompt.rb
|
|
87
112
|
- app/models/shiplog/roadmap.rb
|
|
88
113
|
- app/models/shiplog/roadmap/column_resolver.rb
|
|
114
|
+
- app/models/shiplog/setting.rb
|
|
89
115
|
- app/models/shiplog/sync/commit_scanner.rb
|
|
90
116
|
- app/models/shiplog/sync/issue_importer.rb
|
|
91
117
|
- app/models/shiplog/sync/node_attributes.rb
|
|
92
118
|
- app/models/shiplog/sync/pull_request_importer.rb
|
|
93
119
|
- app/models/shiplog/sync/repository_sync.rb
|
|
94
120
|
- app/models/shiplog/sync/upgrade_detector.rb
|
|
121
|
+
- app/models/shiplog/translator.rb
|
|
122
|
+
- app/models/shiplog/translator/prompt.rb
|
|
95
123
|
- app/models/shiplog/webhook_delivery.rb
|
|
96
124
|
- app/models/shiplog/webhook_signature.rb
|
|
97
125
|
- app/views/layouts/shiplog/admin.html.erb
|
|
@@ -105,14 +133,21 @@ files:
|
|
|
105
133
|
- app/views/shiplog/admin/_custom_head.html.erb
|
|
106
134
|
- app/views/shiplog/admin/_nav.html.erb
|
|
107
135
|
- app/views/shiplog/admin/_page.html.erb
|
|
136
|
+
- app/views/shiplog/admin/areas/index.html.erb
|
|
108
137
|
- app/views/shiplog/admin/boards/_card.html.erb
|
|
109
138
|
- app/views/shiplog/admin/boards/show.html.erb
|
|
110
139
|
- app/views/shiplog/admin/entries/_badges.html.erb
|
|
111
140
|
- app/views/shiplog/admin/entries/_entry_row.html.erb
|
|
141
|
+
- app/views/shiplog/admin/entries/_images.html.erb
|
|
142
|
+
- app/views/shiplog/admin/entries/_translations.html.erb
|
|
112
143
|
- app/views/shiplog/admin/entries/edit.html.erb
|
|
113
144
|
- app/views/shiplog/admin/entries/index.html.erb
|
|
114
145
|
- app/views/shiplog/admin/syncs/show.html.erb
|
|
146
|
+
- app/views/shiplog/changelog_entries/show.html.erb
|
|
147
|
+
- app/views/shiplog/changelog_feeds/show.atom.builder
|
|
148
|
+
- app/views/shiplog/changelogs/_areas.html.erb
|
|
115
149
|
- app/views/shiplog/changelogs/_entry.html.erb
|
|
150
|
+
- app/views/shiplog/changelogs/_entry_content.html.erb
|
|
116
151
|
- app/views/shiplog/changelogs/_month.html.erb
|
|
117
152
|
- app/views/shiplog/changelogs/_search.html.erb
|
|
118
153
|
- app/views/shiplog/changelogs/show.html.erb
|
|
@@ -122,9 +157,11 @@ files:
|
|
|
122
157
|
- app/views/shiplog/roadmaps/show.html.erb
|
|
123
158
|
- config/admin_routes.rb
|
|
124
159
|
- config/locales/en.yml
|
|
160
|
+
- config/locales/es.yml
|
|
125
161
|
- config/routes.rb
|
|
126
162
|
- db/migrate/20261026000001_create_shiplog_tables.rb
|
|
127
163
|
- db/migrate/20261027000001_create_shiplog_pull_request_upgrades.rb
|
|
164
|
+
- db/migrate/20261027000002_shiplog_0_3_0.rb
|
|
128
165
|
- lib/generators/shiplog/install/install_generator.rb
|
|
129
166
|
- lib/generators/shiplog/install/templates/initializer.rb.tt
|
|
130
167
|
- lib/generators/shiplog/install/templates/shiplog-feature.yml
|
|
@@ -137,6 +174,7 @@ files:
|
|
|
137
174
|
- lib/shiplog/github/errors.rb
|
|
138
175
|
- lib/shiplog/github/queries.rb
|
|
139
176
|
- lib/shiplog/version.rb
|
|
177
|
+
- lib/tasks/shiplog.rake
|
|
140
178
|
homepage: https://github.com/Trackberry/shiplog
|
|
141
179
|
licenses:
|
|
142
180
|
- MIT
|