trmnl_preview 0.20.0 → 0.21.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 +10 -0
- data/README.md +29 -2
- data/lib/trmnlp/cli.rb +22 -0
- data/lib/trmnlp/lint/checks/no_unknown_filters.rb +46 -0
- data/lib/trmnlp/lint/diagnostic.rb +2 -2
- data/lib/trmnlp/lint.rb +3 -1
- data/lib/trmnlp/paths.rb +3 -0
- data/lib/trmnlp/testing/rspec.rb +3 -3
- data/lib/trmnlp/testing/screen.rb +3 -2
- data/lib/trmnlp/update_check.rb +94 -0
- data/lib/trmnlp/version.rb +1 -1
- data/templates/init/bin/trmnlp +70 -21
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1b736ce74a0385dda5b989fe604d0256d0b1522b1955855aa0eac945ec3dd0b
|
|
4
|
+
data.tar.gz: f4b4321d72bd9129a025f07d21707f0584e965a5bfe072db3ecc95c2fbf04921
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3997799add0b42fd60ffd8f6c7d9d3c99696152fe32316e8925cfe1a24eed73f410a7efc86a432a46697d1fbd7fa0fa44c7d8c63727c48c0ff48778035e993f5
|
|
7
|
+
data.tar.gz: 12cf46d3ae00fda1f1e82eb80c5ed2bbe2598208ad84de9ecdc878afcb96b51f27e4ed62630195cdb1a0fc60a8d5b6cbc1d6be2f1ef4833aefdc0c370cd1634d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
|
|
2
2
|
# Changelog
|
|
3
3
|
|
|
4
|
+
## 0.21.0
|
|
5
|
+
|
|
6
|
+
- The `bin/trmnlp` script that `trmnlp init` writes pulls a newer `trmnl/trmnlp` image once a day, so a Docker user stays on the latest release. Before, it ran whatever image was pulled first. The README shows how to install the script as `trmnlp` with Docker alone.
|
|
7
|
+
- The `bin/trmnlp` script that `trmnlp init` writes runs every command in Docker as the gem would. It asked for a terminal it did not have on CI and in scripts (`the input device is not a TTY`), and published port 4567 for every command, so `lint` and `test` failed while `serve` ran and `serve --port` could not be reached. On Linux it runs as you, so snapshots and `_build` are not root's. A plugin's OAuth tokens and saved data are kept between runs, and `CI` and `TRMNL_API_KEY` reach trmnlp. Saved as `trmnlp` on the `PATH`, it no longer starts itself without end. An existing plugin gets the new script from `trmnlp init <its folder>`, answering `y` for `bin/trmnlp` only.
|
|
8
|
+
- The README asks for Ruby >= 4.0 to install the gem, as the gem does. It said 3.4.
|
|
9
|
+
- `trmnlp lint` reports a filter that neither Liquid nor trmnl-liquid defines (`no_unknown_filters`), such as a typo or a LiquidJS filter like `push`. TRMNL outputs the value unfiltered, and `serve` and `build` drew it the same way, so nothing showed the mistake.
|
|
10
|
+
- The Docker image includes geckodriver, so it draws screens on arm64 Linux such as a Raspberry Pi. Selenium downloaded it on every run that draws a screen (`test`, `build`, `serve`), which took about half a second and failed with no network, and on arm64 without x86 emulation it could not download one at all (`Unable to obtain geckodriver`).
|
|
11
|
+
- Every command checks RubyGems for a newer stable release, once a day, and suggests a Gem or Bundler update command on stderr; `trmnlp version` always asks. `--quiet` or `TRMNLP_NO_UPDATE_NOTIFIER=1` skips it, and network failures leave output and exit status unchanged.
|
|
12
|
+
- `have_no_overflow(except: '.forecast')` and `screen.overflowing(except:)` leave out a box that hides content on purpose, and everything inside it.
|
|
13
|
+
|
|
4
14
|
## 0.20.0
|
|
5
15
|
|
|
6
16
|
- `trmnlp lint` skips the rules listed under `ignored_lint_rules` in `.trmnlp.yml`, in text and JSON output alike. An unknown rule ID is an error that lists the known ones.
|
data/README.md
CHANGED
|
@@ -100,6 +100,17 @@ trmnlp push # upload
|
|
|
100
100
|
|
|
101
101
|
`trmnlp lint` exits non-zero when it finds issues, so you can gate CI on it. Run `trmnlp help` for all flags.
|
|
102
102
|
|
|
103
|
+
Every command also checks RubyGems for a newer stable `trmnl_preview` release
|
|
104
|
+
and, when one is available, suggests `gem update trmnl_preview`, or
|
|
105
|
+
`bundle update trmnl_preview` when run through Bundler. An exact Gemfile pin must
|
|
106
|
+
be changed before Bundler can update it. Notices go to stderr, keeping lint JSON
|
|
107
|
+
output and exit status unchanged. The answer is cached for a day, so most runs
|
|
108
|
+
make no request; `trmnlp version` always asks RubyGems. `--quiet` or
|
|
109
|
+
`TRMNLP_NO_UPDATE_NOTIFIER=1` skips the check. Connection and read timeouts are
|
|
110
|
+
two seconds each; current versions and an unavailable registry stay silent. The
|
|
111
|
+
check never installs an update or changes your Gemfile or lockfile. Inside Docker there is no
|
|
112
|
+
check: the `bin/trmnlp` script pulls a newer image once a day instead.
|
|
113
|
+
|
|
103
114
|
Lint findings include a stable snake_case rule ID, severity and source locations.
|
|
104
115
|
Locations use project-relative paths and one-based line/column numbers, followed
|
|
105
116
|
by a source excerpt (up to 240 characters). Aggregate checks show their contributing
|
|
@@ -123,6 +134,8 @@ Liquid in declaration values is counted without rendering the plugin, every bran
|
|
|
123
134
|
Filters from `custom_filters` in `.trmnlp.yml` exist only in trmnlp. TRMNL does not load them
|
|
124
135
|
and outputs the value unfiltered, so `no_custom_filters` reports each place the markup uses one.
|
|
125
136
|
Filters that trmnl-liquid also provides are not reported.
|
|
137
|
+
A filter that neither Liquid nor trmnl-liquid defines, such as a typo or one from another
|
|
138
|
+
Liquid (Jekyll, LiquidJS), is also output unfiltered on TRMNL, so `no_unknown_filters` reports it.
|
|
126
139
|
|
|
127
140
|
To accept a rule's findings, list its rule ID under `ignored_lint_rules` in `.trmnlp.yml`.
|
|
128
141
|
`trmnlp lint` then skips that check in both formats, so the CLI and CI agree. An unknown
|
|
@@ -214,13 +227,13 @@ The `bin/trmnlp` script is provided as a convenience. It will use the local Ruby
|
|
|
214
227
|
|
|
215
228
|
You can modify the `bin/trmnlp` script to set up environment variables (plugin secrets, etc.) before running the server.
|
|
216
229
|
|
|
217
|
-
**Gem or Docker?** Install the gem if you already have Ruby >=
|
|
230
|
+
**Gem or Docker?** Install the gem if you already have Ruby >= 4.0 — it has the fastest startup. Use Docker for zero local setup.
|
|
218
231
|
|
|
219
232
|
### Installing via RubyGems
|
|
220
233
|
|
|
221
234
|
Prerequisites:
|
|
222
235
|
|
|
223
|
-
- Ruby >=
|
|
236
|
+
- Ruby >= 4.0
|
|
224
237
|
- For PNG rendering (optional):
|
|
225
238
|
- Firefox
|
|
226
239
|
- ImageMagick
|
|
@@ -232,6 +245,20 @@ trmnlp serve
|
|
|
232
245
|
|
|
233
246
|
### Installing via Docker
|
|
234
247
|
|
|
248
|
+
To type `trmnlp` as with the gem, copy the script from the image onto your `PATH`:
|
|
249
|
+
|
|
250
|
+
```sh
|
|
251
|
+
mkdir -p ~/.local/bin
|
|
252
|
+
docker run --rm --entrypoint cat trmnl/trmnlp /app/templates/init/bin/trmnlp > ~/.local/bin/trmnlp
|
|
253
|
+
chmod +x ~/.local/bin/trmnlp
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
It runs each command in the image and pulls a newer image once a day. To stay on one release,
|
|
257
|
+
set `IMAGE` in the script to a tag such as `trmnl/trmnlp:v0.20.0`. `trmnlp init` puts the same
|
|
258
|
+
script in each plugin as `bin/trmnlp`.
|
|
259
|
+
|
|
260
|
+
Or run the image yourself:
|
|
261
|
+
|
|
235
262
|
```sh
|
|
236
263
|
docker run \
|
|
237
264
|
--pull always \
|
data/lib/trmnlp/cli.rb
CHANGED
|
@@ -4,6 +4,8 @@ require 'thor'
|
|
|
4
4
|
|
|
5
5
|
require_relative '../trmnlp'
|
|
6
6
|
require_relative '../trmnlp/commands'
|
|
7
|
+
require_relative 'paths'
|
|
8
|
+
require_relative 'update_check'
|
|
7
9
|
|
|
8
10
|
module TRMNLP
|
|
9
11
|
class CLI < Thor
|
|
@@ -22,6 +24,23 @@ module TRMNLP
|
|
|
22
24
|
def self.in_container? = File.exist?('/.dockerenv') || File.exist?('/run/.containerenv')
|
|
23
25
|
def self.default_bind = in_container? ? '0.0.0.0' : '127.0.0.1'
|
|
24
26
|
|
|
27
|
+
no_commands do
|
|
28
|
+
# Thor routes every command through here, so the update notice covers
|
|
29
|
+
# them all without each command having to remember it. `version` runs
|
|
30
|
+
# its own fresh check after printing, and help has nothing to update.
|
|
31
|
+
def invoke_command(command, *)
|
|
32
|
+
check_for_update unless %w[help version].include?(command.name)
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def check_for_update(fresh: false)
|
|
37
|
+
# In a container `gem update` does not apply; bin/trmnlp pulls a newer image instead.
|
|
38
|
+
return if options[:quiet] || UpdateCheck.disabled? || self.class.in_container?
|
|
39
|
+
|
|
40
|
+
UpdateCheck.new(Paths.new(options[:dir]).update_check).call(fresh:)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
25
44
|
desc 'build', 'Generate static HTML files'
|
|
26
45
|
method_option :png, type: :boolean, default: false, desc: 'Also render a PNG per view'
|
|
27
46
|
method_option :width, type: :numeric, desc: 'PNG width in pixels (with --png)'
|
|
@@ -96,6 +115,9 @@ module TRMNLP
|
|
|
96
115
|
desc 'version', 'Show version'
|
|
97
116
|
def version
|
|
98
117
|
puts VERSION
|
|
118
|
+
# Keep the version first when stdout is a pipe and stderr is not.
|
|
119
|
+
$stdout.flush
|
|
120
|
+
check_for_update(fresh: true)
|
|
99
121
|
end
|
|
100
122
|
end
|
|
101
123
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'trmnl/liquid'
|
|
4
|
+
|
|
5
|
+
require_relative '../check'
|
|
6
|
+
|
|
7
|
+
module TRMNLP
|
|
8
|
+
module Lint
|
|
9
|
+
module Checks
|
|
10
|
+
# Reports markup that uses a filter neither Liquid nor trmnl-liquid defines, such
|
|
11
|
+
# as a typo or a filter from another Liquid (Jekyll, LiquidJS). TRMNL does not fail
|
|
12
|
+
# on one: it outputs the value unfiltered. custom_filters are no_custom_filters' to report.
|
|
13
|
+
class NoUnknownFilters < Check
|
|
14
|
+
def issues
|
|
15
|
+
unknown_filter_names.map do |name|
|
|
16
|
+
{ message: "Filter '#{name}' is not a Liquid or TRMNL filter. TRMNL outputs the value unfiltered; " \
|
|
17
|
+
'check the name, or use a built-in filter or the transform instead.' }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def unknown_filter_names
|
|
24
|
+
used_filter_names - environment.filter_method_names - source.local_only_filter_names
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def used_filter_names = source.markup_files.values.flat_map { filter_names(it) }.uniq
|
|
28
|
+
|
|
29
|
+
# Liquid's own parse, so a pipe in a string or a raw block is not taken for a filter.
|
|
30
|
+
def filter_names(markup)
|
|
31
|
+
names = []
|
|
32
|
+
template = ::Liquid::Template.parse(markup, environment:)
|
|
33
|
+
::Liquid::ParseTreeVisitor.for(template.root)
|
|
34
|
+
.add_callback_for(::Liquid::Variable) { names.concat(it.filters.map(&:first)) }
|
|
35
|
+
.visit
|
|
36
|
+
names
|
|
37
|
+
rescue ::Liquid::SyntaxError
|
|
38
|
+
[] # A template that does not parse fails to render, which says so.
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# trmnl-liquid's template tag keeps its body as a String, which ParseTreeVisitor cannot walk.
|
|
42
|
+
def environment = @environment ||= TRMNL::Liquid.new { it.register_tag 'template', ::Liquid::Block }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -44,7 +44,7 @@ module TRMNLP
|
|
|
44
44
|
when 'layouts_have_content' then empty_view_locations
|
|
45
45
|
when 'form_fields_valid' then form_field_locations
|
|
46
46
|
when 'custom_fields_used' then project_field_locations
|
|
47
|
-
when 'no_custom_filters' then
|
|
47
|
+
when 'no_custom_filters', 'no_unknown_filters' then filter_locations
|
|
48
48
|
when 'image_links_reachable' then source.locations(Regexp.union(check.unreachable_urls))
|
|
49
49
|
else []
|
|
50
50
|
end
|
|
@@ -55,7 +55,7 @@ module TRMNLP
|
|
|
55
55
|
css_class ? source.locations(Regexp.new(Regexp.escape(css_class))) : []
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def
|
|
58
|
+
def filter_locations
|
|
59
59
|
source.locations(Checks::NoCustomFilters.usage_pattern(finding[:message][/Filter '([^']+)'/, 1]))
|
|
60
60
|
end
|
|
61
61
|
|
data/lib/trmnlp/lint.rb
CHANGED
|
@@ -19,6 +19,7 @@ require_relative 'lint/checks/image_links_reachable'
|
|
|
19
19
|
require_relative 'lint/checks/custom_fields_used'
|
|
20
20
|
require_relative 'lint/checks/form_fields_valid'
|
|
21
21
|
require_relative 'lint/checks/no_custom_filters'
|
|
22
|
+
require_relative 'lint/checks/no_unknown_filters'
|
|
22
23
|
|
|
23
24
|
module TRMNLP
|
|
24
25
|
# Markup best-practice checks behind `trmnlp lint`.
|
|
@@ -41,7 +42,8 @@ module TRMNLP
|
|
|
41
42
|
Checks::ImageLinksReachable,
|
|
42
43
|
Checks::CustomFieldsUsed,
|
|
43
44
|
Checks::FormFieldsValid,
|
|
44
|
-
Checks::NoCustomFilters
|
|
45
|
+
Checks::NoCustomFilters,
|
|
46
|
+
Checks::NoUnknownFilters
|
|
45
47
|
].freeze
|
|
46
48
|
|
|
47
49
|
def self.rule_id(check_type) = check_type.name.split('::').last.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
data/lib/trmnlp/paths.rb
CHANGED
|
@@ -55,6 +55,9 @@ module TRMNLP
|
|
|
55
55
|
|
|
56
56
|
def transform_output = cache_dir.join('transform_output', "#{project_key}.json")
|
|
57
57
|
|
|
58
|
+
# One file for every project: the published gem version is not per project.
|
|
59
|
+
def update_check = cache_dir.join('update_check.json')
|
|
60
|
+
|
|
58
61
|
def render_template = Pathname.new(__dir__).join('..', '..', 'web', 'views', 'render_html.erb')
|
|
59
62
|
|
|
60
63
|
def src_files = src_dir.glob('*').select(&:file?)
|
data/lib/trmnlp/testing/rspec.rb
CHANGED
|
@@ -46,10 +46,10 @@ module TRMNLP
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
RSpec::Matchers.define :have_no_overflow do
|
|
50
|
-
match { |screen| screen.overflowing.empty? }
|
|
49
|
+
RSpec::Matchers.define :have_no_overflow do |except: nil|
|
|
50
|
+
match { |screen| screen.overflowing(except:).empty? }
|
|
51
51
|
failure_message do |screen|
|
|
52
|
-
"expected nothing to overflow on #{screen.inspect}, but: #{screen.overflowing.join(', ')}"
|
|
52
|
+
"expected nothing to overflow on #{screen.inspect}, but: #{screen.overflowing(except:).join(', ')}"
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -15,6 +15,7 @@ module TRMNLP
|
|
|
15
15
|
|
|
16
16
|
# Every element drawn past the screen's edge, and every box that cuts a child box or a glyph's ink.
|
|
17
17
|
OVERFLOW_SCRIPT = <<~JS
|
|
18
|
+
const except = arguments[0];
|
|
18
19
|
const screen = document.documentElement.getBoundingClientRect();
|
|
19
20
|
const describe = (el) => el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') +
|
|
20
21
|
(el.classList.length ? '.' + [...el.classList].join('.') : '');
|
|
@@ -78,7 +79,7 @@ module TRMNLP
|
|
|
78
79
|
};
|
|
79
80
|
return [...document.querySelectorAll('.view *')].filter((el) => {
|
|
80
81
|
const box = el.getBoundingClientRect();
|
|
81
|
-
if (!drawn(box) || insideTruncation(el)) return false;
|
|
82
|
+
if (!drawn(box) || insideTruncation(el) || (except && el.closest(except))) return false;
|
|
82
83
|
const outside = box.right > screen.right + 1 || box.bottom > screen.bottom + 1 || box.left < -1 || box.top < -1;
|
|
83
84
|
const style = getComputedStyle(el);
|
|
84
85
|
const overflows = el.scrollWidth > el.clientWidth + 1 || el.scrollHeight > el.clientHeight + 1;
|
|
@@ -123,7 +124,7 @@ module TRMNLP
|
|
|
123
124
|
|
|
124
125
|
def evaluate(expression) = @browser.on(self) { it.execute_script("return (#{expression});") }
|
|
125
126
|
|
|
126
|
-
def overflowing = @browser.on(self) { it.execute_script(OVERFLOW_SCRIPT) }
|
|
127
|
+
def overflowing(except: nil) = @browser.on(self) { it.execute_script(OVERFLOW_SCRIPT, except) }
|
|
127
128
|
|
|
128
129
|
# Script errors, unhandled rejections, console.error lines and files that failed to load.
|
|
129
130
|
def problems = evaluate('window.__trmnlpProblems || []')
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
require_relative 'reporter'
|
|
6
|
+
require_relative 'version'
|
|
7
|
+
|
|
8
|
+
module TRMNLP
|
|
9
|
+
# Tells the user when RubyGems has a newer trmnl_preview. The answer is
|
|
10
|
+
# cached for a day so routine commands do not wait on the network; a
|
|
11
|
+
# failed lookup is cached too, so an offline run stalls at most once a day.
|
|
12
|
+
class UpdateCheck
|
|
13
|
+
ENDPOINT = 'https://rubygems.org/api/v1/gems/trmnl_preview.json'
|
|
14
|
+
TIMEOUT = 2
|
|
15
|
+
CACHE_TTL = 24 * 60 * 60
|
|
16
|
+
DISABLE_ENV_KEY = 'TRMNLP_NO_UPDATE_NOTIFIER'
|
|
17
|
+
|
|
18
|
+
def self.disabled? = !ENV[DISABLE_ENV_KEY].to_s.empty?
|
|
19
|
+
|
|
20
|
+
def initialize(cache_path, reporter: Reporter.new(stream: $stderr))
|
|
21
|
+
@cache_path = Pathname.new(cache_path)
|
|
22
|
+
@reporter = reporter
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Reports a newer stable release. fresh: asks RubyGems now instead of
|
|
26
|
+
# trusting a cached answer from the last day.
|
|
27
|
+
def call(fresh: false)
|
|
28
|
+
latest = fresh ? refresh : cached
|
|
29
|
+
return unless latest && latest > Gem::Version.new(VERSION)
|
|
30
|
+
|
|
31
|
+
reporter.info("trmnl_preview #{latest} is available (installed: #{VERSION}).")
|
|
32
|
+
reporter.info(update_guidance)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
attr_reader :cache_path, :reporter
|
|
38
|
+
|
|
39
|
+
def cached
|
|
40
|
+
cache = read_cache
|
|
41
|
+
return version_from(cache['version']) if cache && Time.now.to_i - cache['checked_at'] < CACHE_TTL
|
|
42
|
+
|
|
43
|
+
refresh
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def refresh
|
|
47
|
+
latest = fetch_latest
|
|
48
|
+
write_cache(latest)
|
|
49
|
+
latest
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def read_cache
|
|
53
|
+
data = JSON.parse(cache_path.read)
|
|
54
|
+
data if data.is_a?(Hash) && data['checked_at'].is_a?(Integer)
|
|
55
|
+
rescue SystemCallError, JSON::ParserError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def write_cache(latest)
|
|
60
|
+
cache_path.dirname.mkpath
|
|
61
|
+
cache_path.write(JSON.generate(checked_at: Time.now.to_i, version: latest&.to_s))
|
|
62
|
+
rescue SystemCallError
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fetch_latest
|
|
67
|
+
response = Faraday.get(ENDPOINT) do |request|
|
|
68
|
+
request.options.open_timeout = TIMEOUT
|
|
69
|
+
request.options.timeout = TIMEOUT
|
|
70
|
+
end
|
|
71
|
+
parse_version(response.body) if response.success?
|
|
72
|
+
rescue Faraday::Error, JSON::ParserError, ArgumentError
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def parse_version(body)
|
|
77
|
+
data = JSON.parse(body)
|
|
78
|
+
version_from(data['version']) if data.is_a?(Hash)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def version_from(value)
|
|
82
|
+
return unless value.is_a?(String) && !value.empty? && Gem::Version.correct?(value)
|
|
83
|
+
|
|
84
|
+
version = Gem::Version.new(value)
|
|
85
|
+
version unless version.prerelease?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def update_guidance
|
|
89
|
+
return 'Update with: gem update trmnl_preview' if ENV['BUNDLE_GEMFILE'].to_s.empty?
|
|
90
|
+
|
|
91
|
+
'Update the trmnl_preview constraint in your Gemfile if needed, then run: bundle update trmnl_preview'
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/trmnlp/version.rb
CHANGED
data/templates/init/bin/trmnlp
CHANGED
|
@@ -4,10 +4,23 @@
|
|
|
4
4
|
# Use this opportunity to set up environment variables, install dependencies, etc.
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Saved on the PATH as `trmnlp`, this script is what `command -v trmnlp` finds: not a gem.
|
|
8
|
+
GEM="$(command -v trmnlp)"
|
|
9
|
+
if [ -n "$GEM" ] && ! [ "$GEM" -ef "$0" ]
|
|
8
10
|
then
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
exec "$GEM" "$@"
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if ! command -v docker &> /dev/null
|
|
15
|
+
then
|
|
16
|
+
echo "Install the trmnl_preview gem:
|
|
17
|
+
|
|
18
|
+
gem install trmnl_preview
|
|
19
|
+
|
|
20
|
+
Or install Docker:
|
|
21
|
+
|
|
22
|
+
https://docs.docker.com/get-docker/"
|
|
23
|
+
exit 1
|
|
11
24
|
fi
|
|
12
25
|
|
|
13
26
|
# Determine XDG config directory on host OS
|
|
@@ -17,28 +30,64 @@ else
|
|
|
17
30
|
CONFIG_DIR="$HOME/.config/trmnlp"
|
|
18
31
|
fi
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
# What trmnlp caches is kept as well: a plugin's OAuth tokens, its saved data and transform state.
|
|
34
|
+
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/trmnl"
|
|
35
|
+
|
|
36
|
+
mkdir -p "$CONFIG_DIR" "$CACHE_DIR"
|
|
37
|
+
|
|
38
|
+
# Add a tag to stay on one release: trmnl/trmnlp:v0.20.0
|
|
39
|
+
IMAGE=trmnl/trmnlp
|
|
40
|
+
|
|
41
|
+
# A newer release is pulled once a day. With no network, the image already here runs.
|
|
42
|
+
PULLED_AT="$CACHE_DIR/image_pulled"
|
|
43
|
+
if [ -z "$(find "$PULLED_AT" -mtime -1 2> /dev/null)" ]; then
|
|
44
|
+
docker pull --quiet "$IMAGE" > /dev/null 2>&1
|
|
45
|
+
touch "$PULLED_AT"
|
|
22
46
|
fi
|
|
23
47
|
|
|
24
|
-
|
|
48
|
+
# On Linux the container's root is the host's root, and the files trmnlp writes (snapshots, _build,
|
|
49
|
+
# a pulled plugin) would be root's. So trmnlp runs as you, with a home of its own to write in.
|
|
50
|
+
# Docker Desktop and rootless Docker hand the files to you already.
|
|
51
|
+
CONTAINER_HOME=/root
|
|
52
|
+
SECURITY="$(docker info --format '{{.SecurityOptions}}' 2> /dev/null)"
|
|
53
|
+
if [ "$(uname)" = Linux ] && [ "$(id -u)" != 0 ] && [ -n "$SECURITY" ] && [[ "$SECURITY" != *rootless* ]]
|
|
25
54
|
then
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
--
|
|
31
|
-
--
|
|
32
|
-
|
|
33
|
-
|
|
55
|
+
CONTAINER_HOME=/tmp/home
|
|
56
|
+
# .config and .cache are made for you as well: Docker would make them root's to mount the
|
|
57
|
+
# folders below into them, and Firefox hangs when it cannot write there.
|
|
58
|
+
ARGS=(
|
|
59
|
+
--user "$(id -u):$(id -g)"
|
|
60
|
+
--env HOME=/tmp/home
|
|
61
|
+
--tmpfs "/tmp/home:exec,uid=$(id -u),gid=$(id -g)"
|
|
62
|
+
--tmpfs "/tmp/home/.config:uid=$(id -u),gid=$(id -g)"
|
|
63
|
+
--tmpfs "/tmp/home/.cache:exec,uid=$(id -u),gid=$(id -g)"
|
|
64
|
+
)
|
|
34
65
|
fi
|
|
35
66
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Or install Docker:
|
|
67
|
+
# A terminal only when there is one: CI and scripts have none.
|
|
68
|
+
if [ -t 0 ] && [ -t 1 ]; then
|
|
69
|
+
ARGS+=(--tty)
|
|
70
|
+
fi
|
|
41
71
|
|
|
42
|
-
|
|
72
|
+
# Only serve listens, so every other command can run next to it.
|
|
73
|
+
if [ "$1" = serve ]; then
|
|
74
|
+
PORT=4567
|
|
75
|
+
for ARG in "$@"; do
|
|
76
|
+
case "$PREVIOUS" in -p | --port) PORT="$ARG" ;; esac
|
|
77
|
+
case "$ARG" in --port=*) PORT="${ARG#--port=}" ;; esac
|
|
78
|
+
PREVIOUS="$ARG"
|
|
79
|
+
done
|
|
80
|
+
ARGS+=(--publish "$PORT:$PORT")
|
|
81
|
+
fi
|
|
43
82
|
|
|
44
|
-
|
|
83
|
+
exec docker run \
|
|
84
|
+
--rm \
|
|
85
|
+
--init \
|
|
86
|
+
--interactive \
|
|
87
|
+
"${ARGS[@]}" \
|
|
88
|
+
--env CI \
|
|
89
|
+
--env TRMNL_API_KEY \
|
|
90
|
+
--volume "$CONFIG_DIR:$CONTAINER_HOME/.config/trmnlp" \
|
|
91
|
+
--volume "$CACHE_DIR:$CONTAINER_HOME/.cache/trmnl" \
|
|
92
|
+
--volume "$(pwd):/plugin" \
|
|
93
|
+
"$IMAGE" "$@"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trmnl_preview
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.21.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rockwell Schrock
|
|
@@ -433,6 +433,7 @@ files:
|
|
|
433
433
|
- lib/trmnlp/lint/checks/no_custom_filters.rb
|
|
434
434
|
- lib/trmnlp/lint/checks/no_opacity.rb
|
|
435
435
|
- lib/trmnlp/lint/checks/no_size_classes.rb
|
|
436
|
+
- lib/trmnlp/lint/checks/no_unknown_filters.rb
|
|
436
437
|
- lib/trmnlp/lint/checks/recipe_overview_length.rb
|
|
437
438
|
- lib/trmnlp/lint/checks/title_casing.rb
|
|
438
439
|
- lib/trmnlp/lint/checks/title_length.rb
|
|
@@ -483,6 +484,7 @@ files:
|
|
|
483
484
|
- lib/trmnlp/transform_client.rb
|
|
484
485
|
- lib/trmnlp/transform_pipeline.rb
|
|
485
486
|
- lib/trmnlp/transform_state.rb
|
|
487
|
+
- lib/trmnlp/update_check.rb
|
|
486
488
|
- lib/trmnlp/user_data_assembler.rb
|
|
487
489
|
- lib/trmnlp/version.rb
|
|
488
490
|
- lib/trmnlp/watcher.rb
|