jekyll_flexible_include 2.0.14 → 2.0.18
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/.rubocop.yml +76 -8
- data/CHANGELOG.md +40 -15
- data/README.md +199 -53
- data/Rakefile +3 -5
- data/jekyll_flexible_include_plugin.gemspec +20 -29
- data/lib/flexible_include/version.rb +1 -3
- data/lib/flexible_include.rb +161 -108
- data/lib/git_util.rb +41 -0
- data/spec/flexible_include_spec.rb +18 -0
- data/spec/spec_helper.rb +4 -6
- metadata +9 -23
- data/lib/jekyll_tag_helper.rb +0 -90
- data/spec/glob_spec.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8f07914cb71b0dc833f7735175ef18ccde8357e7fcf14014d954cae6e40c5bc
|
4
|
+
data.tar.gz: e077293b1be82f05dedfd02e6326c6c64d4a87895fc4d6f90cc7d532d50df857
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a476f8c116c18590acd67e5be96fb1d10889394f3c278cec7bb43d211be7a8f8dcacc158953c4e9bea3de29b6d32668e8d5500ae24a8a0bd405d091219ac6ff
|
7
|
+
data.tar.gz: 5bd8341a6eb53a35a520791a32d79ec587169650f56d36c3d4b4aa965f6247867037253d83c677cbdae913d79b19d6a18a901a63013cc49fbc526358583bb238
|
data/.rubocop.yml
CHANGED
@@ -1,16 +1,36 @@
|
|
1
|
-
require:
|
2
|
-
|
3
|
-
|
1
|
+
require:
|
2
|
+
# - rubocop-jekyll
|
3
|
+
- rubocop-md
|
4
|
+
- rubocop-performance
|
5
|
+
- rubocop-rake
|
6
|
+
- rubocop-rspec
|
7
|
+
|
8
|
+
# inherit_gem:
|
9
|
+
# rubocop-jekyll: .rubocop.yml
|
4
10
|
|
5
11
|
AllCops:
|
6
12
|
Exclude:
|
7
|
-
|
8
|
-
|
13
|
+
- demo/_site/**/*
|
14
|
+
- exe/**/*
|
15
|
+
- vendor/**/*
|
16
|
+
- Gemfile*
|
9
17
|
NewCops: enable
|
10
18
|
TargetRubyVersion: 2.6
|
11
19
|
|
12
|
-
|
13
|
-
|
20
|
+
Gemspec/DeprecatedAttributeAssignment:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Gemspec/RequireMFA:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Layout/InitialIndentation:
|
27
|
+
Exclude:
|
28
|
+
- README.md
|
29
|
+
|
30
|
+
Layout/HashAlignment:
|
31
|
+
EnforcedColonStyle: table
|
32
|
+
Exclude:
|
33
|
+
- jekyll_flexible_include_plugin.gemspec
|
14
34
|
|
15
35
|
Layout/LineLength:
|
16
36
|
Max: 150
|
@@ -18,20 +38,68 @@ Layout/LineLength:
|
|
18
38
|
Layout/MultilineMethodCallIndentation:
|
19
39
|
Enabled: false
|
20
40
|
|
41
|
+
Lint/RedundantCopDisableDirective:
|
42
|
+
Exclude:
|
43
|
+
- jekyll_flexible_include_plugin.gemspec
|
44
|
+
|
45
|
+
Metrics/AbcSize:
|
46
|
+
Max: 45
|
47
|
+
|
21
48
|
Metrics/BlockLength:
|
22
|
-
|
49
|
+
Exclude:
|
50
|
+
- jekyll_flexible_include_plugin.gemspec
|
51
|
+
- spec/**/*
|
52
|
+
|
53
|
+
Metrics/ClassLength:
|
54
|
+
Exclude:
|
55
|
+
- spec/**/*
|
56
|
+
|
57
|
+
Metrics/CyclomaticComplexity:
|
58
|
+
Max: 20
|
59
|
+
|
60
|
+
Metrics/MethodLength:
|
61
|
+
Max: 40
|
62
|
+
|
63
|
+
Metrics/PerceivedComplexity:
|
64
|
+
Max: 15
|
65
|
+
|
66
|
+
Naming/FileName:
|
67
|
+
Exclude:
|
68
|
+
- Rakefile
|
69
|
+
|
70
|
+
RSpec/ExampleLength:
|
71
|
+
Max: 20
|
72
|
+
|
73
|
+
RSpec/MultipleExpectations:
|
74
|
+
Max: 15
|
23
75
|
|
24
76
|
Style/CommandLiteral:
|
25
77
|
Enabled: false
|
26
78
|
|
79
|
+
Style/CommentedKeyword:
|
80
|
+
Enabled: false
|
81
|
+
|
82
|
+
Style/Documentation:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
Style/FrozenStringLiteralComment:
|
86
|
+
Enabled: false
|
87
|
+
|
27
88
|
Style/PercentLiteralDelimiters:
|
28
89
|
Enabled: false
|
29
90
|
|
30
91
|
Style/RegexpLiteral:
|
31
92
|
Enabled: false
|
32
93
|
|
94
|
+
Style/StringConcatenation:
|
95
|
+
Exclude:
|
96
|
+
- spec/**/*
|
97
|
+
|
33
98
|
Style/StringLiterals:
|
34
99
|
Enabled: false
|
35
100
|
|
36
101
|
Style/StringLiteralsInInterpolation:
|
37
102
|
Enabled: false
|
103
|
+
|
104
|
+
Style/TrailingCommaInHashLiteral:
|
105
|
+
EnforcedStyleForMultiline: comma
|
data/CHANGELOG.md
CHANGED
@@ -1,18 +1,42 @@
|
|
1
|
+
## 2.0.18 / 2023-03-24
|
2
|
+
* The following are now parsed property:
|
3
|
+
`die_on_file_error`, `die_on_path_denied`, `die_on_run_error`, `die_on_path_denied`, and `die_on_other_error`.
|
4
|
+
|
5
|
+
## 2.0.17 / 2023-03-22
|
6
|
+
* Added `repo` and `git_ref` parameters, so files can be retrieved from git repositories at a given commit or tag.
|
7
|
+
|
8
|
+
## 2.0.16 / 2023-02-19
|
9
|
+
* Replaced hard-coded CSS in `denied` method with `flexible_error` class in
|
10
|
+
`demo/assets/css/style.css`.
|
11
|
+
* Added `-e` and `-x` options to `demo/_bin/debug`.
|
12
|
+
* Added configuration section `flexible_include` with supported parameters
|
13
|
+
`die_on_file_error`, `die_on_path_denied`, `die_on_run_error`,
|
14
|
+
`die_on_path_denied` and `die_on_other_error`.
|
15
|
+
* Fixed `undefined method 'path'` that occurred when `FLEXIBLE_INCLUDE_PATHS` was specified.
|
16
|
+
|
17
|
+
## 2.0.15 / 2023-02-18
|
18
|
+
* Replaced dependency `key-value-parser` with `jekyll_plugin_support`.
|
19
|
+
* Added `demo` website.
|
20
|
+
* Improved the documentation.
|
21
|
+
* Updated Rubocop configuration.
|
22
|
+
* Added `strip` option.
|
23
|
+
|
1
24
|
## 2.0.14 / 2022-09-27
|
2
|
-
* Added `key-value-parser` as a dependency
|
25
|
+
* Added `key-value-parser` as a dependency.
|
3
26
|
|
4
27
|
## 2.0.13 / 2022-04-24
|
5
|
-
* Added `highlight` regex option, for highlighting
|
6
|
-
* Added `number` option, for numbered lines
|
28
|
+
* Added `highlight` regex option, for highlighting.
|
29
|
+
* Added `number` option, for numbered lines.
|
7
30
|
|
8
31
|
## 2.0.12 / 2022-04-22
|
9
|
-
* Exits with an error message if an environment variable included in the value
|
32
|
+
* Exits with an error message if an environment variable included in the value
|
33
|
+
of `FLEXIBLE_INCLUDE_PATHS` is undefined.
|
10
34
|
|
11
35
|
## 2.0.11 / 2022-04-15
|
12
|
-
* Added & => & to the escaped characters
|
36
|
+
* Added & => & to the escaped characters.
|
13
37
|
|
14
38
|
## 2.0.10 / 2022-04-15
|
15
|
-
* Fixed nil pointer
|
39
|
+
* Fixed nil pointer.
|
16
40
|
|
17
41
|
## 2.0.9 / 2022-04-15
|
18
42
|
* Changed how path matching was implemented.
|
@@ -40,10 +64,11 @@
|
|
40
64
|
* Updated to `jekyll_plugin_logger` v2.1.0
|
41
65
|
|
42
66
|
## 2.0.0 / 2022-03-11
|
43
|
-
* Made into a Ruby gem and published on RubyGems.org as
|
44
|
-
|
45
|
-
*
|
46
|
-
*
|
67
|
+
* Made into a Ruby gem and published on RubyGems.org as
|
68
|
+
[jekyll_flexible_include](https://rubygems.org/gems/jekyll_flexible_include).
|
69
|
+
* `bin/attach` script added for debugging.
|
70
|
+
* Rubocop standards added.
|
71
|
+
* Proper versioning and CHANGELOG.md added.
|
47
72
|
|
48
73
|
## 1.1.1 / 2021-05-01
|
49
74
|
* Handles spaces in filenames properly.
|
@@ -52,14 +77,14 @@
|
|
52
77
|
* Added `do_not_escape` optional parameter.
|
53
78
|
|
54
79
|
## 1.0.0 / 2020-11-28
|
55
|
-
* Mike Slinn took over the project
|
56
|
-
* Now supports relative includes
|
80
|
+
* Mike Slinn took over the project.
|
81
|
+
* Now supports relative includes.
|
57
82
|
|
58
83
|
## 2020-11-28
|
59
|
-
*
|
84
|
+
* Mike Slinn renamed `include_absolute` to `flexible_include`.
|
60
85
|
|
61
86
|
## 2020-08-23
|
62
|
-
* Now supports absolute paths
|
87
|
+
* Now supports absolute paths.
|
63
88
|
|
64
89
|
## 2022-03-11
|
65
|
-
* Project began
|
90
|
+
* Project began.
|
data/README.md
CHANGED
@@ -2,52 +2,115 @@ Jekyll `flexible_include` Plugin
|
|
2
2
|
[](https://badge.fury.io/rb/jekyll_flexible_include)
|
3
3
|
===========
|
4
4
|
|
5
|
-
`Flexible_include` is a Jekyll plugin that includes the contents of a file
|
5
|
+
`Flexible_include` is a Jekyll plugin that includes the contents of a file
|
6
|
+
or the result of a process into a generated page.
|
7
|
+
`Flexible_include` is useful because Jekyll's built-in `include` tag only
|
8
|
+
supports the including of files residing within the `_includes/` subfolder of a Jekyll project,
|
9
|
+
and because `flexible_include` offers additional ways of including content.
|
6
10
|
|
7
|
-
Originally called `include_absolute`,
|
11
|
+
Originally called `include_absolute`,
|
12
|
+
this plugin has been renamed to `flexible_include` because it no longer just
|
13
|
+
includes absolute file names.
|
8
14
|
|
9
15
|
This plugin is available as a [Ruby gem](https://rubygems.org/gems/jekyll_flexible_include).
|
10
16
|
More information is available on my website about [my Jekyll plugins](https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html).
|
11
17
|
|
12
18
|
This plugin supports 4 types of includes:
|
13
19
|
|
20
|
+
|
14
21
|
### Include Types
|
15
22
|
|
16
23
|
1. Absolute filenames (recognized by filename paths that start with `/`).
|
17
|
-
|
24
|
+
|
25
|
+
2. Filenames relative to the top-level directory of the Jekyll website (relative paths **do not** start with `.` or `/`).
|
26
|
+
|
18
27
|
3. Filenames relative to the user home directory (recognized by filename paths starting with `~/`).
|
28
|
+
|
19
29
|
4. Executable filenames on the `PATH` (recognized by filename paths that begin with `!`).
|
20
30
|
|
21
31
|
|
22
|
-
In addition, filenames that require environment expansion because they contain a
|
23
|
-
expanded according to the environment variables
|
32
|
+
In addition, filenames that require environment expansion because they contain a
|
33
|
+
<code>$</code> character are expanded according to the environment variables
|
34
|
+
defined when <code>jekyll build</code> executes.
|
24
35
|
|
25
36
|
|
26
|
-
|
27
|
-
|
37
|
+
A file from a git repository can also be included.
|
38
|
+
Files can be retrieved from at a given commit or tag.
|
39
|
+
Two new options are provided for this purpose:
|
40
|
+
- `repo` - directory where git repo resides; environment variables are expanded; defaults to current directory.
|
41
|
+
- `git_ref` - Git ref of commit or tag to be examined for the file; defaults to `HEAD`.
|
42
|
+
|
43
|
+
|
44
|
+
### Configuration
|
45
|
+
Configuration parameters can be added to a section in `_config.yml` called `flexible_include`, like this:
|
46
|
+
|
47
|
+
```yaml
|
48
|
+
flexible_include:
|
49
|
+
die_on_file_error: true
|
50
|
+
die_on_path_denied: true
|
51
|
+
die_on_run_error: true
|
52
|
+
die_on_other_error: true
|
28
53
|
```
|
54
|
+
|
55
|
+
The default values for all of these parameters is `false`,
|
56
|
+
except for `die_on_other_error`, which defaults to `true`.
|
57
|
+
|
58
|
+
- If `die_on_file_error` is enabled, then an attempt to include a file that fails will cause Jekyll to die with an error message.
|
59
|
+
|
60
|
+
- If `die_on_path_denied` is enabled (see [Restricting Directory Access](#restricting-directory-access)), then an attempt to include a file that should be blocked will cause Jekyll to die with an error message.
|
61
|
+
|
62
|
+
- If `die_on_run_error` is enabled, then an attempt to run a process that fails will cause Jekyll to die with an error message.
|
63
|
+
|
64
|
+
- If `die_on_other_error` is enabled, then any other exception will cause Jekyll to die with an error message.
|
65
|
+
|
66
|
+
|
67
|
+
### Syntax
|
68
|
+
The following are all equivalent, however, the first two are recommended:
|
69
|
+
```html
|
70
|
+
{% flexible_include file="path" [ OPTIONS ] %}
|
71
|
+
{% flexible_include file='path' [ OPTIONS ] %}
|
29
72
|
{% flexible_include path [ OPTIONS ] %}
|
30
73
|
{% flexible_include 'path' [ OPTIONS ] %}
|
31
74
|
{% flexible_include "path" [ OPTIONS ] %}
|
32
75
|
```
|
33
76
|
|
34
|
-
|
35
|
-
|
36
|
-
|
77
|
+
Note that the [square brackets] merely indicate optional parameters
|
78
|
+
and are not intended to be written literally.
|
79
|
+
|
37
80
|
|
38
81
|
### Options
|
39
|
-
* `do_not_escape`
|
40
|
-
|
41
|
-
|
82
|
+
* `do_not_escape` keyword option caused the content to be included without HTML escaping it.
|
83
|
+
By default, the included file will escape characters <code><</code>,
|
84
|
+
<code>{</code> and <code>}</code> unless the <code>do_not_escape</code> keyword option is specified.
|
85
|
+
|
86
|
+
* `highlight='regex pattern here'` wraps content matching the regex pattern within a
|
87
|
+
`<span class='bg_yellow'></span>` tag.
|
88
|
+
Note that the pattern can simply consist of the exact text that you want to highlight.
|
89
|
+
|
90
|
+
* `pre` is a keyword option that causes the included file to be wrapped inside a
|
91
|
+
<pre></pre> tag; no label is generated.
|
92
|
+
The <pre></pre> tag has an `data-lt-active="false"` attribute, so
|
93
|
+
[LanguageTool](https://forum.languagetool.org/t/avoid-spell-check-on-certain-html-inputs-manually/3944)
|
94
|
+
will not attempt to check the spelling or grammar of the contents.
|
42
95
|
|
43
96
|
The following options imply `pre`:
|
44
|
-
* `dark` applies the `dark` class to the generated <pre></pre> tag.
|
97
|
+
* `dark` keyword option applies the `dark` class to the generated <pre></pre> tag.
|
45
98
|
You can define the `dark` and `darkLabel` classes as desired.
|
46
99
|
[This CSS is a good starting point.](https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#pre_css)
|
47
|
-
|
48
|
-
* `
|
49
|
-
|
50
|
-
|
100
|
+
|
101
|
+
* `download` keyword option uses the name of the file as a label,
|
102
|
+
and displays it above the <pre></pre> tag.
|
103
|
+
Clicking the label causes the file to be downloaded.
|
104
|
+
|
105
|
+
* `copyButton` keyword option draws an icon at the top right of the <pre></pre>
|
106
|
+
tag that causes the included contents to be copied to the clipboard.
|
107
|
+
|
108
|
+
* `label` keyword option specifies that an automatically generated label be placed above the contents.
|
109
|
+
There is no need to specify this option if `download` or `copy_button` options are provided.
|
110
|
+
|
111
|
+
* `label="blah blah"` specifies a label for the contents; this value overrides the default label.
|
112
|
+
The value can be enclosed in single or double quotes.
|
113
|
+
|
51
114
|
|
52
115
|
### Restricting Directory Access
|
53
116
|
By default, `flexible_include` can read from all directories according to the permissions of the user account that launched the `jekyll` process.
|
@@ -57,70 +120,97 @@ Defining an environment variable called `FLEXIBLE_INCLUDE_PATHS` prior to launch
|
|
57
120
|
This environment variable consists of a colon-delimited set of
|
58
121
|
[file and directory glob patterns](https://docs.ruby-lang.org/en/2.7.0/Dir.html#method-c-glob).
|
59
122
|
For example, the following restricts access to only the files within:
|
123
|
+
|
60
124
|
1. The `~/my_dir` directory tree of the account of the user that launched Jekyll.
|
125
|
+
|
61
126
|
2. The directory tree rooted at `/var/files`.
|
127
|
+
|
62
128
|
3. The directory tree rooted at the expanded value of the `$work` environment variable.
|
129
|
+
|
63
130
|
```shell
|
64
131
|
export FLEXIBLE_INCLUDE_PATHS='~/.*:$sites/.*:$work/.*'
|
65
132
|
```
|
133
|
+
|
66
134
|
Note that the above matches dot (hidden) files as well as regular files.
|
67
135
|
To just match visible files:
|
136
|
+
|
68
137
|
```shell
|
69
138
|
export FLEXIBLE_INCLUDE_PATHS='~/my_dir/**/*:/var/files/**/*:$work/**/*'
|
70
139
|
```
|
71
140
|
|
72
141
|
#### Note
|
73
|
-
The specified directories are traversed when the plugin starts,
|
142
|
+
The specified directories are traversed when the plugin starts,
|
143
|
+
and the filenames are stored in memory.
|
144
|
+
Directories with lots of files might take a noticable amount of
|
145
|
+
time to enumerate the files.
|
74
146
|
|
75
147
|
|
76
148
|
### Restricting Arbitrary Processes
|
77
|
-
By default, `flexible_include` can execute any command.
|
149
|
+
By default, `flexible_include` can execute any command.
|
150
|
+
You can disable that by setting the environment variable `DISABLE_FLEXIBLE_INCLUDE`
|
151
|
+
to any non-empty value.
|
78
152
|
```shell
|
79
153
|
export DISABLE_FLEXIBLE_INCLUDE=true
|
80
154
|
```
|
81
155
|
|
82
|
-
If a potential command execution is intercepted,
|
156
|
+
If a potential command execution is intercepted,
|
157
|
+
a big red message will appear on the generated web page that says
|
158
|
+
`Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.`,
|
159
|
+
and a red error message will be logged on the console that says something like:
|
160
|
+
`ERROR FlexibleInclude: _posts/2020/2020-10-03-jekyll-plugins.html - Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.`
|
83
161
|
|
84
162
|
|
85
163
|
## Installation
|
164
|
+
1. Add the following to `Gemfile`, inside the `jekyll_plugins` group:
|
165
|
+
```ruby
|
166
|
+
group :jekyll_plugins do
|
167
|
+
gem 'jekyll_flexible_include', '~> 2.0.15'
|
168
|
+
end
|
169
|
+
```
|
86
170
|
|
87
|
-
Add the following to `
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
171
|
+
2. Add the following to `_config.yml`.
|
172
|
+
This is necessary because the name of the plugin
|
173
|
+
does not match the name of the entry point file:
|
174
|
+
```yaml
|
175
|
+
plugins:
|
176
|
+
- flexible_include
|
177
|
+
```
|
93
178
|
|
94
|
-
|
95
|
-
|
96
|
-
plugins:
|
97
|
-
- flexible_include
|
98
|
-
```
|
179
|
+
3. Copy `demo/assets/images/clippy.svg` to a directory that resolves to
|
180
|
+
`assets/images/` in your Jekyll website.
|
99
181
|
|
100
|
-
|
182
|
+
4. Copy the CSS from `demo/assets/css/styles.css` between the comments to your Jekyll project's CSS file:
|
183
|
+
```css
|
184
|
+
blah blah blah
|
101
185
|
|
102
|
-
|
186
|
+
/* START OF CSS TO COPY */
|
187
|
+
Copy this stuff
|
188
|
+
/* END OF CSS TO COPY */
|
103
189
|
|
104
|
-
|
190
|
+
blah blah blah
|
191
|
+
```
|
105
192
|
|
106
|
-
|
193
|
+
5. Install the `jekyll_flexible_include` Ruby gem as usual:
|
194
|
+
```shell
|
195
|
+
$ bundle install
|
196
|
+
```
|
107
197
|
|
108
198
|
|
109
199
|
## Examples
|
110
200
|
|
111
|
-
1. Include files, escaping any HTML markup so it appears as written; all four types of includes are shown.
|
201
|
+
1. Include files, escaping any HTML markup, so it appears as written; all four types of includes are shown.
|
112
202
|
```
|
113
203
|
{% flexible_include '../../folder/outside/jekyll/site/foo.html' %}
|
114
204
|
{% flexible_include 'folder/within/jekyll/site/bar.js' %}
|
115
205
|
{% flexible_include '/etc/passwd' %}
|
116
206
|
{% flexible_include '~/.ssh/config' %}
|
117
|
-
{% flexible_include '!jekyll' %}
|
207
|
+
{% flexible_include '!jekyll help' %}
|
118
208
|
{% flexible_include '$HOME/.bash_aliases' %}
|
119
209
|
```
|
120
210
|
|
121
211
|
2. Include a JSON file (without escaping characters).
|
122
212
|
```
|
123
|
-
{% flexible_include '~/folder/under/home/directory/foo.html'
|
213
|
+
{% flexible_include do_not_escape file='~/folder/under/home/directory/foo.html' %}
|
124
214
|
```
|
125
215
|
|
126
216
|
## Additional Information
|
@@ -132,37 +222,43 @@ More information is available on
|
|
132
222
|
GitHub Pages only allows [these plugins](https://pages.github.com/versions/).
|
133
223
|
That means `flexible_include` will not work on GitHub Pages.
|
134
224
|
Following is a workaround.
|
225
|
+
|
135
226
|
1. Let's assume your git repository that you want to publish as GitHub Pages is called `mysite`.
|
136
227
|
This repository cannot be the source of your GitHub Pages because you are using the `flexible_include` plugin.
|
228
|
+
|
137
229
|
2. Make a new git repository to hold the generated website. Let's call this git repository `generated_site`.
|
230
|
+
|
138
231
|
3. Generate `mysite` locally as usual.
|
232
|
+
|
139
233
|
4. Copy the generated HTML in the `mysite/_site/` directory to `generated_site`.
|
234
|
+
|
140
235
|
5. Run `git commit` on `generated_site`.
|
236
|
+
|
141
237
|
6. Tell GitHub that you want the `generated_site` repository to hold your GitHub pages.
|
238
|
+
|
142
239
|
7. A moment later, your website will now be visible as GitHub Pages, with the included content, just as you saw it locally.
|
143
240
|
|
144
241
|
|
145
242
|
## Known Issues
|
146
243
|
If the plugin does not work:
|
147
|
-
1. Ensure `_config.yml` doesn't have `safe: true`. That prevents all plugins from working.
|
148
|
-
2. If you have version older than v2.x.x, delete the file `_plugins/flexible_include.rb` or you will have version conflicts.
|
149
244
|
|
245
|
+
1. Ensure `_config.yml` doesn't have `safe: true` set.
|
246
|
+
That prevents all plugins from working.
|
150
247
|
|
151
|
-
|
248
|
+
2. If you have version older than v2.x.x,
|
249
|
+
delete the file `_plugins/flexible_include.rb`,
|
250
|
+
or you will have version conflicts.
|
152
251
|
|
153
|
-
|
252
|
+
|
253
|
+
## Development
|
254
|
+
After checking out the repo, run `bin/setup` to install dependencies as binstubs in the `exe` directory.
|
154
255
|
|
155
256
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
156
257
|
|
157
258
|
### Build and Install Locally
|
158
259
|
To build and install this gem onto your local machine, run:
|
159
260
|
```shell
|
160
|
-
$ rake install
|
161
|
-
```
|
162
|
-
|
163
|
-
The following also does the same thing:
|
164
|
-
```shell
|
165
|
-
$ bundle exec rake install
|
261
|
+
$ rake install
|
166
262
|
```
|
167
263
|
|
168
264
|
Examine the newly built gem:
|
@@ -182,11 +278,59 @@ jekyll_flexible_include (2.0.4)
|
|
182
278
|
```
|
183
279
|
|
184
280
|
|
281
|
+
## Demo Website
|
282
|
+
A test/demo website is provided in the `demo` directory.
|
283
|
+
You can run it under a debugger, or let it run free.
|
284
|
+
|
285
|
+
The `demo/_bin/debug` script can set various parameters for the demo.
|
286
|
+
View the help information with the `-h` option:
|
287
|
+
```shell
|
288
|
+
$ demo/_bin/debug -h
|
289
|
+
|
290
|
+
debug - Run the demo Jekyll website.
|
291
|
+
|
292
|
+
By default the demo Jekyll website runs without restriction under ruby-debug-ide and debase.
|
293
|
+
View it at http://localhost:4444
|
294
|
+
|
295
|
+
Options:
|
296
|
+
-e Restrict the allowable directories to read from to the following regexes:
|
297
|
+
jekyll_flexible_include_plugin/.*
|
298
|
+
/dev/.*
|
299
|
+
/proc/.*
|
300
|
+
/run/.*
|
301
|
+
|
302
|
+
-h Show this error message
|
303
|
+
|
304
|
+
-r Run freely, without a debugger
|
305
|
+
|
306
|
+
-x Disable the ability to execute arbitrary commands
|
307
|
+
```
|
308
|
+
|
309
|
+
|
310
|
+
### Debugging the Demo
|
311
|
+
To run under a debugger, for example Visual Studio Code:
|
312
|
+
1. Set breakpoints.
|
313
|
+
|
314
|
+
2. Initiate a debug session from the command line:
|
315
|
+
```shell
|
316
|
+
$ demo/bin/debug
|
317
|
+
```
|
318
|
+
|
319
|
+
3. Once the `Fast Debugger` signon appears, launch the Visual Studio Code launch configuration called `Attach rdebug-ide`.
|
320
|
+
|
321
|
+
4. View the generated website at [`http://localhost:4444`](http://localhost:4444).
|
322
|
+
|
323
|
+
|
185
324
|
### Build and Push to RubyGems
|
186
325
|
To release a new version,
|
326
|
+
|
187
327
|
1. Update the version number in `version.rb`.
|
188
|
-
|
189
|
-
|
328
|
+
|
329
|
+
2. Add a comment to the top of `CHANGELOG.md`.
|
330
|
+
|
331
|
+
3. Commit all changes to git; if you don't the next step will fail.
|
332
|
+
|
333
|
+
4. Run the following:
|
190
334
|
```shell
|
191
335
|
$ bundle exec rake release
|
192
336
|
```
|
@@ -195,12 +339,14 @@ To release a new version,
|
|
195
339
|
|
196
340
|
|
197
341
|
## Contributing
|
198
|
-
|
199
342
|
1. Fork the project
|
343
|
+
|
200
344
|
2. Create a descriptively named feature branch
|
345
|
+
|
201
346
|
3. Add your feature
|
347
|
+
|
202
348
|
4. Submit a pull request
|
203
349
|
|
204
|
-
## License
|
205
350
|
|
351
|
+
## License
|
206
352
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
1
|
+
require_relative 'lib/flexible_include/version'
|
2
2
|
|
3
|
-
|
3
|
+
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
4
|
+
github = 'https://github.com/mslinn/jekyll_flexible_include_plugin'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
spec.authors = ["Mike Slinn", "Tan Nhu", "Maarten Brakkee"]
|
9
|
-
spec.bindir = "exe"
|
6
|
+
spec.authors = ['Mike Slinn', 'Tan Nhu', 'Maarten Brakkee']
|
7
|
+
spec.bindir = 'exe'
|
10
8
|
spec.description = <<~END_OF_DESC
|
11
9
|
Jekyll's built-in include tag only supports including files within the _includes folder.
|
12
10
|
This plugin supports 4 types of includes: absolute filenames,
|
@@ -14,36 +12,29 @@ Gem::Specification.new do |spec|
|
|
14
12
|
filenames relative to the user home directory,
|
15
13
|
and executable filenames on the PATH.
|
16
14
|
END_OF_DESC
|
17
|
-
spec.email = [
|
18
|
-
spec.files = Dir[
|
19
|
-
spec.homepage =
|
20
|
-
spec.license =
|
15
|
+
spec.email = ['mslinn@mslinn.com']
|
16
|
+
spec.files = Dir['.rubocop.yml', 'LICENSE.*', 'Rakefile', '{lib,spec}/**/*', '*.gemspec', '*.md']
|
17
|
+
spec.homepage = 'https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#flexibleInclude'
|
18
|
+
spec.license = 'MIT'
|
21
19
|
spec.metadata = {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
20
|
+
'allowed_push_host' => 'https://rubygems.org',
|
21
|
+
'bug_tracker_uri' => "#{github}/issues",
|
22
|
+
'changelog_uri' => "#{github}/CHANGELOG.md",
|
23
|
+
'homepage_uri' => spec.homepage,
|
24
|
+
'source_code_uri' => github,
|
27
25
|
}
|
28
|
-
spec.name =
|
26
|
+
spec.name = 'jekyll_flexible_include'
|
29
27
|
spec.post_install_message = <<~END_MESSAGE
|
30
28
|
|
31
29
|
Thanks for installing #{spec.name}!
|
32
30
|
|
33
31
|
END_MESSAGE
|
34
|
-
spec.require_paths = [
|
35
|
-
spec.required_ruby_version =
|
36
|
-
spec.summary =
|
32
|
+
spec.require_paths = ['lib']
|
33
|
+
spec.required_ruby_version = '>= 2.6.0'
|
34
|
+
spec.summary = 'Jekyll plugin supports various ways to include content into the generated site.'
|
37
35
|
spec.test_files = spec.files.grep(%r!^(test|spec|features)/!)
|
38
36
|
spec.version = JekyllFlexibleIncludePluginVersion::VERSION
|
39
37
|
|
40
|
-
spec.add_dependency
|
41
|
-
spec.add_dependency
|
42
|
-
spec.add_dependency "key-value-parser"
|
43
|
-
|
44
|
-
# spec.add_development_dependency "debase"
|
45
|
-
# spec.add_development_dependency "rubocop-jekyll"
|
46
|
-
# spec.add_development_dependency "rubocop-rake"
|
47
|
-
# spec.add_development_dependency "rubocop-rspec"
|
48
|
-
# spec.add_development_dependency "ruby-debug-ide"
|
38
|
+
spec.add_dependency 'jekyll_plugin_support', '~> 0.5.1'
|
39
|
+
spec.add_dependency 'rugged'
|
49
40
|
end
|
data/lib/flexible_include.rb
CHANGED
@@ -1,170 +1,224 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
|
5
|
-
require "jekyll_plugin_logger"
|
6
|
-
require "securerandom"
|
7
|
-
require_relative "flexible_include/version"
|
8
|
-
require_relative "jekyll_tag_helper"
|
1
|
+
require 'benchmark'
|
2
|
+
require 'jekyll_plugin_support'
|
3
|
+
require 'securerandom'
|
4
|
+
require_relative 'flexible_include/version'
|
9
5
|
|
10
6
|
module JekyllFlexibleIncludeName
|
11
|
-
PLUGIN_NAME =
|
12
|
-
end
|
13
|
-
|
14
|
-
class FlexibleError < StandardError
|
7
|
+
PLUGIN_NAME = 'flexible_include'.freeze
|
15
8
|
end
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@read_regexes = nil
|
10
|
+
module FlexibleClassMethods
|
11
|
+
def access_allowed(path)
|
12
|
+
return true unless @read_regexes
|
21
13
|
|
22
|
-
|
23
|
-
JekyllTagHelper.expand_env(path, die_if_undefined: true)
|
24
|
-
.gsub("~", Dir.home)
|
14
|
+
@read_regexes.find { |regex| regex.match(normalize_path(path)) }
|
25
15
|
end
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
unless @read_regexes
|
33
|
-
flexible_include_paths = ENV['FLEXIBLE_INCLUDE_PATHS']
|
34
|
-
read_paths = normalize_path(flexible_include_paths) if flexible_include_paths
|
35
|
-
if read_paths
|
36
|
-
@read_regexes = read_paths.split(":").map do |path|
|
37
|
-
abs_path = path.start_with?('/') ? path : (Pathname.new(Dir.pwd) + path).to_s
|
38
|
-
Regexp.new(abs_path)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
17
|
+
def self.escape_html(string)
|
18
|
+
string.gsub("&", "&")
|
19
|
+
.gsub("{", "{")
|
20
|
+
.gsub("}", "}")
|
21
|
+
.gsub("<", "<")
|
42
22
|
end
|
43
23
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
@read_regexes.find { |regex| regex.match(normalize_path(path)) }
|
24
|
+
def normalize_path(path)
|
25
|
+
JekyllPluginHelper.expand_env(path, die_if_undefined: true)
|
26
|
+
.gsub('~', Dir.home)
|
48
27
|
end
|
49
28
|
|
50
|
-
def
|
29
|
+
def number_content(content)
|
51
30
|
lines = content.split("\n")
|
52
31
|
digits = lines.length.to_s.length
|
53
32
|
i = 0
|
54
33
|
numbered_content = lines.map do |line|
|
55
34
|
i += 1
|
56
|
-
number = i.to_s.rjust(digits,
|
35
|
+
number = i.to_s.rjust(digits, ' ')
|
57
36
|
"<span class='unselectable numbered_line'> #{number}: </span>#{line}"
|
58
37
|
end
|
59
|
-
result = numbered_content.join
|
60
|
-
result += "\n" unless result.end_with?
|
38
|
+
result = numbered_content.join "\n"
|
39
|
+
result += "\n" unless result.end_with? "\n"
|
61
40
|
result
|
62
41
|
end
|
63
42
|
|
64
|
-
#
|
65
|
-
# @
|
66
|
-
|
67
|
-
|
68
|
-
# a boolean parameter that determines if error messages should display the line number the error occurred.
|
69
|
-
# This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
|
70
|
-
# See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
|
71
|
-
def initialize(tag_name, markup, _parse_context)
|
72
|
-
super
|
73
|
-
@logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
74
|
-
@helper = JekyllTagHelper.new(tag_name, markup, @logger)
|
43
|
+
# If FLEXIBLE_INCLUDE_PATHS='~/lib/.*:.*:$WORK/.*'
|
44
|
+
# Then @read_regexes will be set to regexes of ['/home/my_user_id/lib/.*', '/pwd/.*', '/work/envar/path/.*']
|
45
|
+
def security_check
|
46
|
+
@execution_denied = ENV.fetch('DISABLE_FLEXIBLE_INCLUDE', nil)
|
75
47
|
|
76
|
-
|
77
|
-
end
|
48
|
+
return if @read_regexes
|
78
49
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
@do_not_escape = @helper.parameter_specified? "do_not_escape"
|
83
|
-
@download = @helper.parameter_specified? "download"
|
84
|
-
@dark = " dark" if @helper.parameter_specified?("dark")
|
85
|
-
@highlight_pattern = @helper.parameter_specified? "highlight"
|
86
|
-
@label = @helper.parameter_specified? "label"
|
87
|
-
@number_lines = @helper.parameter_specified? "number"
|
88
|
-
@label_specified = @label
|
89
|
-
@copy_button = @helper.parameter_specified? "copyButton"
|
90
|
-
@pre = @copy_button || @dark || @download || @label_specified || @number_lines || @helper.parameter_specified?("pre") # Download or label implies pre
|
50
|
+
flexible_include_paths = ENV.fetch('FLEXIBLE_INCLUDE_PATHS', nil)
|
51
|
+
read_paths = normalize_path(flexible_include_paths) if flexible_include_paths
|
52
|
+
return unless read_paths
|
91
53
|
|
92
|
-
|
93
|
-
|
94
|
-
|
54
|
+
@read_regexes = read_paths.split(':').map do |path|
|
55
|
+
abs_path = path.start_with?('/') ? path : (Pathname.new(Dir.pwd) + path).to_s
|
56
|
+
Regexp.new(abs_path)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
95
60
|
|
96
|
-
|
97
|
-
@label ||= @helper.params[1..].join(" ")
|
61
|
+
FlexibleIncludeError = Class.new(Liquid::Error)
|
98
62
|
|
99
|
-
|
63
|
+
class FlexibleInclude < JekyllSupport::JekyllTag # rubocop: disable Metrics/ClassLength
|
64
|
+
include JekyllFlexibleIncludePluginVersion
|
100
65
|
|
101
|
-
|
66
|
+
def render_impl # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
67
|
+
setup
|
68
|
+
path = JekyllPluginHelper.expand_env(@filename)
|
102
69
|
case path
|
103
70
|
when /\A\// # Absolute path
|
104
|
-
return denied("Access to
|
71
|
+
return denied("Access to <code>#{path}</code> denied by <code>FLEXIBLE_INCLUDE_PATHS</code> value.") unless self.class.access_allowed(path)
|
105
72
|
|
106
|
-
@logger.debug { "Absolute path=#{path}, filename=#{filename}" }
|
73
|
+
@logger.debug { "Absolute path=#{path}, @filename=#{@filename}" }
|
107
74
|
when /\A~/ # Relative path to user's home directory
|
108
|
-
return denied("Access to
|
75
|
+
return denied("Access to <code>#{path}</code> denied by <code>FLEXIBLE_INCLUDE_PATHS</code> value.") unless self.class.access_allowed(path)
|
109
76
|
|
110
|
-
@logger.debug { "User home start filename=#{filename}, path=#{path}" }
|
111
|
-
filename.
|
112
|
-
path = File.join(
|
113
|
-
@logger.debug { "User home end filename=#{filename}, path=#{path}" }
|
77
|
+
@logger.debug { "User home start @filename=#{@filename}, path=#{path}" }
|
78
|
+
@filename = @filename.delete_prefix '~/'
|
79
|
+
path = File.join(Dir.home, @filename)
|
80
|
+
@logger.debug { "User home end @filename=#{@filename}, path=#{path}" }
|
114
81
|
when /\A!/ # Run command and return response
|
115
|
-
return denied(
|
82
|
+
return denied('Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.') if @execution_denied
|
116
83
|
|
117
|
-
filename =
|
118
|
-
filename.
|
119
|
-
contents = run(filename)
|
84
|
+
@filename = JekyllPluginHelper.remove_quotes(@helper.argv.first) if @helper.argv.first
|
85
|
+
@filename = @filename.delete_prefix '!'
|
86
|
+
contents = run(@filename)
|
120
87
|
else # Relative path
|
121
|
-
|
122
|
-
|
123
|
-
path = File.join(source, filename) # Fully qualified path of include file from relative path
|
88
|
+
source = File.expand_path(@site.config['source']) # website root directory
|
89
|
+
path = File.join(source, @filename) # Fully qualified path of include file from relative path
|
124
90
|
@relative = true
|
125
|
-
@logger.debug { "Relative end filename=#{filename}, path=#{path}" }
|
91
|
+
@logger.debug { "Relative end @filename=#{@filename}, path=#{path}" }
|
126
92
|
end
|
127
93
|
render_completion(path, contents)
|
128
|
-
|
129
|
-
|
94
|
+
rescue Errno::EACCES => e
|
95
|
+
msg = format_error_message e.message
|
96
|
+
@logger.error msg
|
97
|
+
raise FlexibleIncludeError, msg, [] if @die_on_file_error
|
98
|
+
|
99
|
+
"<span class='flexible_error'>FlexibleIncludeError: #{msg}</span>"
|
100
|
+
rescue Errno::ENOENT => e
|
101
|
+
msg = format_error_message e.message
|
102
|
+
@logger.error msg
|
103
|
+
raise FlexibleIncludeError, msg, [] if @die_on_path_denied
|
104
|
+
|
105
|
+
"<span class='flexible_error'>FlexibleIncludeError: #{msg}</span>"
|
106
|
+
rescue FlexibleIncludeError => e
|
107
|
+
@logger.error e.message
|
108
|
+
raise e
|
109
|
+
rescue StandardError => e
|
110
|
+
msg = format_error_message e.message
|
111
|
+
@logger.error msg
|
112
|
+
raise FlexibleIncludeError, msg, [] if @die_on_other_error
|
113
|
+
|
114
|
+
"<span class='flexible_error'>FlexibleIncludeError: #{msg}</span>"
|
130
115
|
end
|
131
116
|
|
132
117
|
private
|
133
118
|
|
119
|
+
class << self
|
120
|
+
include FlexibleClassMethods
|
121
|
+
end
|
122
|
+
|
134
123
|
def denied(msg)
|
135
|
-
|
136
|
-
"
|
124
|
+
msg_no_html = remove_html_tags(msg)
|
125
|
+
@logger.error("#{@page['path']} - #{msg_no_html}")
|
126
|
+
raise FlexibleIncludeError, "#{@page['path']} - #{msg_no_html.red}", [] if @die_on_path_denied
|
127
|
+
|
128
|
+
"<p class='flexible_error'>#{msg}</p>"
|
129
|
+
end
|
130
|
+
|
131
|
+
def format_error_message(message)
|
132
|
+
"#{message} on line #{@line_number} (after front matter) of #{@page['path']}}"
|
137
133
|
end
|
138
134
|
|
139
135
|
def highlight(content, pattern)
|
140
|
-
content.gsub(Regexp
|
136
|
+
content.gsub(Regexp.new(pattern), "<span class='bg_yellow'>\\0</span>")
|
141
137
|
end
|
142
138
|
|
143
|
-
def
|
144
|
-
|
139
|
+
def parse_args
|
140
|
+
@copy_button = @helper.parameter_specified? 'copyButton'
|
141
|
+
@dark = ' dark' if @helper.parameter_specified? 'dark'
|
142
|
+
@do_not_escape = @helper.parameter_specified? 'do_not_escape'
|
143
|
+
@download = @helper.parameter_specified? 'download'
|
144
|
+
@highlight_pattern = @helper.parameter_specified? 'highlight'
|
145
|
+
@label = @helper.parameter_specified? 'label'
|
146
|
+
@label_specified = @label
|
147
|
+
@number_lines = @helper.parameter_specified? 'number'
|
148
|
+
@strip = @helper.parameter_specified? 'strip'
|
149
|
+
|
150
|
+
# Download, dark, label or number implies pre
|
151
|
+
@pre = @helper.parameter_specified?('pre') || @copy_button || @dark || @download || @label_specified || @number_lines
|
152
|
+
|
153
|
+
@filename = @helper.parameter_specified? 'file'
|
154
|
+
@filename ||= @helper.params.first # Do this after all options have been checked for
|
155
|
+
@label ||= @filename
|
156
|
+
|
157
|
+
# If a label was specified, use it, otherwise concatenate any dangling parameters and use that as the label
|
158
|
+
@label ||= @helper.params[1..].join(' ')
|
159
|
+
|
160
|
+
@logger.debug("@filename=#{@filename}")
|
145
161
|
end
|
146
162
|
|
163
|
+
# Not used, delete
|
147
164
|
def realpath_prefixed_with?(path, dir)
|
148
165
|
File.exist?(path) && File.realpath(path).start_with?(dir)
|
149
|
-
rescue StandardError
|
150
|
-
|
166
|
+
rescue StandardError => _e
|
167
|
+
raise FlexibleIncludeError, remove_html_tags(e.message).red, [] if @die_on_file_error
|
168
|
+
end
|
169
|
+
|
170
|
+
def remove_html_tags(string)
|
171
|
+
string.gsub(/<[^>]*>/, '')
|
151
172
|
end
|
152
173
|
|
153
174
|
def render_completion(path, contents)
|
154
|
-
contents ||=
|
155
|
-
|
175
|
+
contents ||= File.read(path)
|
176
|
+
contents.strip! if @strip
|
177
|
+
contents2 = @do_not_escape ? contents : FlexibleClassMethods.escape_html(contents)
|
156
178
|
contents2 = highlight(contents2, @highlight_pattern) if @highlight_pattern
|
157
179
|
contents2 = FlexibleInclude.number_content(contents2) if @number_lines
|
158
180
|
@pre ? wrap_in_pre(path, contents2) : contents2
|
159
181
|
end
|
160
182
|
|
161
183
|
def run(cmd)
|
162
|
-
|
184
|
+
if cmd.empty?
|
185
|
+
msg = format_error_message 'FlexibleIncludeError: Empty command string'
|
186
|
+
@do_not_escape = true
|
187
|
+
return "<span class='flexible_error'>#{msg}</span>" unless @die_on_other_error
|
188
|
+
|
189
|
+
raise FlexibleIncludeError, msg, []
|
190
|
+
end
|
191
|
+
|
192
|
+
@logger.debug { "Executing #{cmd}" }
|
163
193
|
%x[#{cmd}].chomp
|
194
|
+
rescue FlexibleIncludeError => e
|
195
|
+
raise e
|
196
|
+
rescue StandardError => e
|
197
|
+
msg = format_error_message "#{e.class}: #{e.message.strip}"
|
198
|
+
@logger.error msg
|
199
|
+
@do_not_escape = true
|
200
|
+
return "<span class='flexible_error'>#{msg}</span>" unless @die_on_run_error
|
201
|
+
|
202
|
+
e.set_backtrace []
|
203
|
+
raise e
|
204
|
+
end
|
205
|
+
|
206
|
+
def setup
|
207
|
+
self.class.security_check
|
208
|
+
|
209
|
+
config = @config[JekyllFlexibleIncludeName::PLUGIN_NAME]
|
210
|
+
if config
|
211
|
+
@die_on_file_error = config['die_on_file_error'] == true
|
212
|
+
@die_on_other_error = config['die_on_other_error'] == true
|
213
|
+
@die_on_path_denied = config['die_on_path_denied'] == true
|
214
|
+
@die_on_run_error = config['die_on_run_error'] == true
|
215
|
+
end
|
216
|
+
|
217
|
+
parse_args
|
164
218
|
end
|
165
219
|
|
166
|
-
PREFIX = "<button class='copyBtn' data-clipboard-target="
|
167
|
-
SUFFIX = "title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>"
|
220
|
+
PREFIX = "<button class='copyBtn' data-clipboard-target=".freeze
|
221
|
+
SUFFIX = "title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>".freeze
|
168
222
|
|
169
223
|
def wrap_in_pre(path, content)
|
170
224
|
basename = File.basename(path)
|
@@ -178,14 +232,13 @@ class FlexibleInclude < Liquid::Tag
|
|
178
232
|
@label_specified ? @label : basename
|
179
233
|
end
|
180
234
|
pre_id = "id#{SecureRandom.hex 6}"
|
181
|
-
copy_button = @copy_button ? "#{PREFIX}'##{pre_id}'#{SUFFIX}" :
|
182
|
-
dark_label =
|
235
|
+
copy_button = @copy_button ? "#{PREFIX}'##{pre_id}'#{SUFFIX}" : ''
|
236
|
+
dark_label = ' darkLabel' if @dark
|
183
237
|
<<~END_PRE
|
184
238
|
<div class="codeLabel#{dark_label}">#{label_or_href}</div>
|
185
239
|
<pre data-lt-active="false" class="maxOneScreenHigh copyContainer#{@dark}" id="#{pre_id}">#{copy_button}#{content}</pre>
|
186
240
|
END_PRE
|
187
241
|
end
|
188
|
-
end
|
189
242
|
|
190
|
-
|
191
|
-
|
243
|
+
JekyllPluginHelper.register(self, 'flexible_include')
|
244
|
+
end
|
data/lib/git_util.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rugged'
|
2
|
+
|
3
|
+
class GitFileReader
|
4
|
+
def initialize(repo_dir = '.')
|
5
|
+
@repo = Rugged::Repository.new repo_dir
|
6
|
+
end
|
7
|
+
|
8
|
+
def blob_at(ref, path)
|
9
|
+
commit = @repo.rev_parse ref # Rugged::Commit
|
10
|
+
@repo.blob_at(commit.oid, path) # Rugged::Blob
|
11
|
+
end
|
12
|
+
|
13
|
+
def commit_for_ref(ref)
|
14
|
+
reference = @repo.ref ref # Rugged::Reference
|
15
|
+
abort "Error: #{ref} is an invalid ref" if reference.nil?
|
16
|
+
|
17
|
+
reference_direct = reference.type == :symbolic ? reference.target : reference
|
18
|
+
@commit = reference_direct.target # Rugged::Commit
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return content of desired file
|
23
|
+
def contents(filename)
|
24
|
+
abort('Error: @commit is undefined; invoke commit_for_ref before invoking contents') if @commit.nil?
|
25
|
+
|
26
|
+
tree = @commit.tree # Rugged::Tree
|
27
|
+
entry = tree.get_entry filename # hash
|
28
|
+
abort("Error: #{filename} is not present in commit #{commit.oid}") if entry.nil?
|
29
|
+
|
30
|
+
sha = entry[:oid] # String
|
31
|
+
object = @repo.read sha # Rugged::ObdObject; this is a blob
|
32
|
+
object.data # String
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if $PROGRAM_NAME == __FILE__
|
37
|
+
puts GitFileReader.new('.').blob_at('HEAD~2', 'README.md').content
|
38
|
+
# puts GitFileReader.new('.').commit_for_ref('HEAD^').contents('README.md')
|
39
|
+
# puts GitFileReader.new('.').commit_for_ref('HEAD').contents('README.md')
|
40
|
+
# puts GitFileReader.new('.').commit_for_ref('refs/heads/master').contents('README.md')
|
41
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative '../lib/flexible_include'
|
2
|
+
|
3
|
+
RSpec.describe(FlexibleInclude) do
|
4
|
+
it 'controls access to files' do
|
5
|
+
ENV['FLEXIBLE_INCLUDE_PATHS'] = '~/.*:spec/.*'
|
6
|
+
|
7
|
+
described_class.send(:new, 'my_tag', '', Liquid::ParseContext.new)
|
8
|
+
described_class.security_check
|
9
|
+
expect(described_class.access_allowed(__FILE__)).to be_truthy
|
10
|
+
|
11
|
+
expect(described_class.access_allowed('~/.mem_settings.yaml')).to be_truthy
|
12
|
+
|
13
|
+
home_file = JekyllTagHelper.expand_env('$HOME/.mem_settings.yaml')
|
14
|
+
expect(described_class.access_allowed(home_file)).to be_truthy
|
15
|
+
|
16
|
+
expect(described_class.access_allowed('/asdf')).to be_falsey
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
|
-
|
1
|
+
require 'jekyll'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require_relative "../lib/flexible_include"
|
3
|
+
require_relative '../lib/flexible_include'
|
6
4
|
|
7
5
|
Jekyll.logger.log_level = :info
|
8
6
|
|
9
7
|
RSpec.configure do |config|
|
10
8
|
config.filter_run :focus
|
11
|
-
config.order =
|
9
|
+
config.order = 'random'
|
12
10
|
config.run_all_when_everything_filtered = true
|
13
11
|
|
14
12
|
# See https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures
|
15
|
-
config.example_status_persistence_file_path =
|
13
|
+
config.example_status_persistence_file_path = 'spec/status_persistence.txt'
|
16
14
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll_flexible_include
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Slinn
|
@@ -10,38 +10,24 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2023-03-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
18
|
-
requirements:
|
19
|
-
- - ">="
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 3.5.0
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
version: 3.5.0
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: jekyll_plugin_logger
|
16
|
+
name: jekyll_plugin_support
|
31
17
|
requirement: !ruby/object:Gem::Requirement
|
32
18
|
requirements:
|
33
19
|
- - "~>"
|
34
20
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
21
|
+
version: 0.5.1
|
36
22
|
type: :runtime
|
37
23
|
prerelease: false
|
38
24
|
version_requirements: !ruby/object:Gem::Requirement
|
39
25
|
requirements:
|
40
26
|
- - "~>"
|
41
27
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
28
|
+
version: 0.5.1
|
43
29
|
- !ruby/object:Gem::Dependency
|
44
|
-
name:
|
30
|
+
name: rugged
|
45
31
|
requirement: !ruby/object:Gem::Requirement
|
46
32
|
requirements:
|
47
33
|
- - ">="
|
@@ -73,8 +59,8 @@ files:
|
|
73
59
|
- jekyll_flexible_include_plugin.gemspec
|
74
60
|
- lib/flexible_include.rb
|
75
61
|
- lib/flexible_include/version.rb
|
76
|
-
- lib/
|
77
|
-
- spec/
|
62
|
+
- lib/git_util.rb
|
63
|
+
- spec/flexible_include_spec.rb
|
78
64
|
- spec/spec_helper.rb
|
79
65
|
- spec/status_persistence.txt
|
80
66
|
homepage: https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#flexibleInclude
|
@@ -110,7 +96,7 @@ specification_version: 4
|
|
110
96
|
summary: Jekyll plugin supports various ways to include content into the generated
|
111
97
|
site.
|
112
98
|
test_files:
|
113
|
-
- spec/
|
99
|
+
- spec/flexible_include_spec.rb
|
114
100
|
- spec/spec_helper.rb
|
115
101
|
- spec/status_persistence.txt
|
116
102
|
...
|
data/lib/jekyll_tag_helper.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "shellwords"
|
4
|
-
require 'key_value_parser'
|
5
|
-
|
6
|
-
class JekyllTagHelper
|
7
|
-
attr_reader :argv, :liquid_context, :logger, :params, :tag_name
|
8
|
-
|
9
|
-
def self.escape_html(string)
|
10
|
-
string.gsub("&", "&")
|
11
|
-
.gsub("{", "{")
|
12
|
-
.gsub("}", "}")
|
13
|
-
.gsub("<", "<")
|
14
|
-
end
|
15
|
-
|
16
|
-
# Expand a environment variable reference
|
17
|
-
def self.expand_env(str, die_if_undefined=false)
|
18
|
-
str.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
|
19
|
-
envar = Regexp.last_match(1)
|
20
|
-
raise FlexibleError, "flexible_include error: #{envar} is undefined".red, [] if !ENV.key?(envar) && die_if_undefined # Suppress stack trace
|
21
|
-
ENV[envar]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# strip leading and trailing quotes if present
|
26
|
-
def self.remove_quotes(string)
|
27
|
-
string.strip.gsub(/\A'|\A"|'\Z|"\Z/, '').strip if string
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(tag_name, markup, logger)
|
31
|
-
@tag_name = tag_name
|
32
|
-
@argv = Shellwords.split(markup)
|
33
|
-
@keys_values = KeyValueParser.new.parse(@argv) # Hash[Symbol, String|Boolean]
|
34
|
-
@logger = logger
|
35
|
-
@logger.debug { "@keys_values='#{@keys_values}'" }
|
36
|
-
end
|
37
|
-
|
38
|
-
def delete_parameter(name)
|
39
|
-
@params.delete(name)
|
40
|
-
@argv.delete_if { |x| x.start_with? name }
|
41
|
-
@keys_values.delete(name.to_sym)
|
42
|
-
end
|
43
|
-
|
44
|
-
# @return if parameter was specified, returns value and removes it from the available tokens
|
45
|
-
def parameter_specified?(name)
|
46
|
-
value = @keys_values[name.to_sym]
|
47
|
-
delete_parameter(name)
|
48
|
-
value
|
49
|
-
end
|
50
|
-
|
51
|
-
PREDEFINED_SCOPE_KEYS = [:include, :page].freeze
|
52
|
-
|
53
|
-
# Finds variables defined in an invoking include, or maybe somewhere else
|
54
|
-
# @return variable value or nil
|
55
|
-
def dereference_include_variable(name)
|
56
|
-
@liquid_context.scopes.each do |scope|
|
57
|
-
next if PREDEFINED_SCOPE_KEYS.include? scope.keys.first
|
58
|
-
|
59
|
-
value = scope[name]
|
60
|
-
return value if value
|
61
|
-
end
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
|
65
|
-
# @return value of variable, or the empty string
|
66
|
-
def dereference_variable(name)
|
67
|
-
value = @liquid_context[name] # Finds variables named like 'include.my_variable', found in @liquid_context.scopes.first
|
68
|
-
value ||= @page[name] if @page # Finds variables named like 'page.my_variable'
|
69
|
-
value ||= dereference_include_variable(name)
|
70
|
-
value ||= ""
|
71
|
-
value
|
72
|
-
end
|
73
|
-
|
74
|
-
# Sets @params by replacing any Liquid variable names with their values
|
75
|
-
def liquid_context=(context)
|
76
|
-
@liquid_context = context
|
77
|
-
@params = @keys_values.map { |k, _v| lookup_variable(k) }
|
78
|
-
end
|
79
|
-
|
80
|
-
def lookup_variable(symbol)
|
81
|
-
string = symbol.to_s
|
82
|
-
return string unless string.start_with?("{{") && string.end_with?("}}")
|
83
|
-
|
84
|
-
dereference_variable(string.delete_prefix("{{").delete_suffix("}}"))
|
85
|
-
end
|
86
|
-
|
87
|
-
def page
|
88
|
-
@liquid_context.registers[:page]
|
89
|
-
end
|
90
|
-
end
|
data/spec/glob_spec.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../lib/flexible_include"
|
4
|
-
|
5
|
-
RSpec.describe(FlexibleInclude) do
|
6
|
-
it "controls access to files" do
|
7
|
-
ENV['FLEXIBLE_INCLUDE_PATHS'] = '~/.*:spec/.*'
|
8
|
-
|
9
|
-
FlexibleInclude.send(:new, 'my_tag', "", Liquid::ParseContext.new)
|
10
|
-
FlexibleInclude.security_check
|
11
|
-
expect(FlexibleInclude.access_allowed(__FILE__)).to be_truthy
|
12
|
-
|
13
|
-
expect(FlexibleInclude.access_allowed("~/.mem_settings.yaml")).to be_truthy
|
14
|
-
|
15
|
-
home_file = JekyllTagHelper.expand_env("$HOME/.mem_settings.yaml")
|
16
|
-
expect(FlexibleInclude.access_allowed(home_file)).to be_truthy
|
17
|
-
|
18
|
-
expect(FlexibleInclude.access_allowed('/asdf')).to be_falsey
|
19
|
-
end
|
20
|
-
end
|