Flexible_include
is a Jekyll plugin that includes the contents of a file
or the result of a process into a generated page.
Flexible_include
is useful because Jekyll's built-in include
tag only
supports the including of files residing within the _includes/
subfolder of a Jekyll project,
and because flexible_include
offers additional ways of including content.
Originally called include_absolute
,
this plugin has been renamed to flexible_include
because it no longer just
includes absolute file names.
This plugin is available as a Ruby gem. More information is available on my website about my Jekyll plugins.
This plugin supports 4 types of includes:
Include Types
-
Absolute filenames (recognized by filename paths that start with
/
). -
Filenames relative to the top-level directory of the Jekyll website (relative paths do not start with
.
or/
). -
Filenames relative to the user home directory (recognized by filename paths starting with
~/
). -
Executable filenames on the
PATH
(recognized by filename paths that begin with!
).
In addition, filenames that require environment expansion because they contain a
$
character are expanded according to the environment variables
defined when jekyll build
executes.
A file from a git repository can also be included. Files can be retrieved from at a given commit or tag. Two new options are provided for this purpose:
-
repo
- directory where git repo resides; environment variables are expanded; defaults to current directory. -
git_ref
- Git ref of commit or tag to be examined for the file; defaults toHEAD
.
Configuration
Configuration parameters can be added to a section in _config.yml
called flexible_include
, like this:
flexible_include:
die_on_file_error: true
die_on_path_denied: true
die_on_run_error: true
die_on_other_error: true
The default values for all of these parameters is false
,
except for die_on_other_error
, which defaults to true
.
-
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. -
If
die_on_path_denied
is enabled (see Restricting Directory Access), then an attempt to include a file that should be blocked will cause Jekyll to die with an error message. -
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. -
If
die_on_other_error
is enabled, then any other exception will cause Jekyll to die with an error message.
Syntax
The following are all equivalent, however, the first two are recommended:
{% flexible_include file="path" [ OPTIONS ] %}
{% flexible_include file='path' [ OPTIONS ] %}
{% flexible_include path [ OPTIONS ] %}
{% flexible_include 'path' [ OPTIONS ] %}
{% flexible_include "path" [ OPTIONS ] %}
Note that the [square brackets] merely indicate optional parameters and are not intended to be written literally.
Options
-
attribution
seejekyll_plugin_support
-
do_not_escape
keyword option caused the content to be included without HTML escaping it. By default, the included file will escape characters<
,{
and}
unless thedo_not_escape
keyword option is specified. -
from='regex'
specifies that the beginning of the output should discarded until the matching string or regex is encountered. -
highlight='regex pattern here'
wraps content matching the regex pattern within a<span class='bg_yellow'></span>
tag. Note that the pattern can simply consist of the exact text that you want to highlight. -
pre
is a keyword option that causes the included file to be wrapped inside a <pre></pre> tag; no label is generated. The <pre></pre> tag has andata-lt-active="false"
attribute, so LanguageTool will not attempt to check the spelling or grammar of the contents. -
to='regex'
specifies that the output should discarded after the matching string or regex is encountered (includes the matched line). -
until='regex'
specifies that the output should discarded after the matching string or regex is encountered (excludes the matched line).
The following options imply pre
:
-
dark
keyword option applies thedark
class to the generated <pre></pre> tag. You can define thedark
anddarkLabel
classes as desired. This CSS is a good starting point. -
download
keyword option uses the name of the file as a label, and displays it above the <pre></pre> tag. Clicking the label causes the file to be downloaded. -
copyButton
keyword option draws an icon at the top right of the <pre></pre> tag that causes the included contents to be copied to the clipboard. -
label
keyword option specifies that an automatically generated label be placed above the contents. There is no need to specify this option ifdownload
orcopy_button
options are provided. -
label="blah blah"
specifies a label for the contents; this value overrides the default label. The value can be enclosed in single or double quotes.
Restricting Directory Access
By default, flexible_include
can read from all directories according to the permissions of the
user account that launched the jekyll
process.
For security-conscience environments, the accessible paths can be restricted.
Defining an environment variable called FLEXIBLE_INCLUDE_PATHS
prior to launching Jekyll will
restrict the paths that flexible_include
will be able to read from.
This environment variable consists of a colon-delimited set of
file and directory glob patterns.
For example, the following restricts access to only the files within:
-
The
~/my_dir
directory tree of the account of the user that launched Jekyll. -
The directory tree rooted at
/var/files
. -
The directory tree rooted at the expanded value of the
$work
environment variable.
export FLEXIBLE_INCLUDE_PATHS='~/.*:$sites/.*:$work/.*'
Note that the above matches dot (hidden) files as well as regular files. To just match visible files:
export FLEXIBLE_INCLUDE_PATHS='~/my_dir/**/*:/var/files/**/*:$work/**/*'
Note
The specified directories are traversed when the plugin starts, and the filenames are stored in memory. Directories with lots of files might take a noticable amount of time to enumerate the files.
Restricting Arbitrary Processes
By default, flexible_include
can execute any command.
You can disable that by setting the environment variable DISABLE_FLEXIBLE_INCLUDE
to any non-empty value.
export DISABLE_FLEXIBLE_INCLUDE=true
If a potential command execution is intercepted,
a big red message will appear on the generated web page that says
Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.
,
and a red error message will be logged on the console that says something like:
ERROR FlexibleInclude: _posts/2020/2020-10-03-jekyll-plugins.html - Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.
Installation
Gem
-
Add the following to
Gemfile
, inside thejekyll_plugins
group:group :jekyll_plugins do gem 'jekyll_flexible_include', '~> 2.0.15' end
-
Add the following to
_config.yml
. This is necessary because the name of the plugin does not match the name of the entry point file:plugins: - flexible_include
-
Copy
demo/assets/images/clippy.svg
to a directory that resolves toassets/images/
in your Jekyll website. -
Copy the CSS from
demo/assets/css/jekyll_flexible_include.css
to your Jekyll project's CSS file. -
Install the
jekyll_flexible_include
Ruby gem and mark it as a dependency of your project:$ bundle
CSS and Assets
Copy assets and CSS from the demo/ directory of the jekyll_pre GitHub project.
- Copy
demo/assets/images/clippy.svg
to a directory of the same name in your Jekyll project. - Copy
demo/assets/css/jekyll_plugin_support.css
to your Jekyll project assets directory. - Copy
demo/assets/css/shared_include_pre.css
to your Jekyll project assets directory. - Copy
demo/assets/css/jekyll_flexible_include.css
to your Jekyll project assets directory. - Incorporate the CSS stylesheets into the appropriate layout in your Jekyll project:
{% assign nowMillis = site.time | date: '%s' %}
<link rel="stylesheet" href="{{ '/assets/css/jekyll_plugin_support.css?v=' | append: nowMillis }}" type="text/css">
<link rel="stylesheet" href="{{ '/assets/css/shared_include_pre.css?v=' | append: nowMillis }}" type="text/css">
<link rel="stylesheet" href="{{ '/assets/css/jekyll_flexible_include.css?v=' | append: nowMillis }}" type="text/css">
JavaScript
Copy demo/assets/js/clipboard.min.js
from the jekyll_flexible_include_plugin
GitHub project
to your Jekyll project’s JavaScript directory.
Modify the Jekyll layout or selected pages to load the JavaScript. You can load it from your project, as shown below, or from a CDN.
<script defer src="/assets/js/clipboard.min.js"></script>
Examples
-
Include files, escaping any HTML markup, so it appears as written; all four types of includes are shown.
{% flexible_include '../../folder/outside/jekyll/site/foo.html' %} {% flexible_include 'folder/within/jekyll/site/bar.js' %} {% flexible_include '/etc/passwd' %} {% flexible_include '~/.ssh/config' %} {% flexible_include '!jekyll help' %} {% flexible_include '$HOME/.bash_aliases' %}
-
Include a JSON file (without escaping characters).
{% flexible_include do_not_escape file='~/folder/under/home/directory/foo.html' %}
Additional Information
More information is available on Mike Slinn’s website.
GitHub Pages
GitHub Pages only allows these plugins.
That means flexible_include
will not work on GitHub Pages.
Following is a workaround.
-
Let's assume your git repository that you want to publish as GitHub Pages is called
mysite
. This repository cannot be the source of your GitHub Pages because you are using theflexible_include
plugin. -
Make a new git repository to hold the generated website. Let's call this git repository
generated_site
. -
Generate
mysite
locally as usual. -
Copy the generated HTML in the
mysite/_site/
directory togenerated_site
. -
Run
git commit
ongenerated_site
. -
Tell GitHub that you want the
generated_site
repository to hold your GitHub pages. -
A moment later, your website will now be visible as GitHub Pages, with the included content, just as you saw it locally.
Known Issues
If the plugin does not work:
-
Ensure
_config.yml
doesn't havesafe: true
set. That prevents all plugins from working. -
If you have version older than v2.x.x, delete the file
_plugins/flexible_include.rb
, or you will have version conflicts.
Development
After checking out the repo, run bin/setup
to install dependencies as binstubs in the exe
directory.
You can also run bin/console
for an interactive prompt that will allow you to experiment.
Build and Install Locally
To build and install this gem onto your local machine, run:
$ bundle exec rake install
Examine the newly built gem:
$ gem info jekyll_flexible_include
*** LOCAL GEMS ***
jekyll_flexible_include (2.0.4)
Authors: Mike Slinn, Tan Nhu, Maarten Brakkee
Homepage: https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#flexibleInclude
License: MIT
Installed at (2.0.4): /home/mslinn/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0
Jekyll plugin supports various ways to include content into the
generated site.
Demo Website
A test/demo website is provided in the demo
directory.
You can run it under a debugger, or let it run free.
The demo/_bin/debug
script can set various parameters for the demo.
View the help information with the -h
option:
$ demo/_bin/debug -h
debug - Run the demo Jekyll website.
By default the demo Jekyll website runs without restriction under ruby-debug-ide and debase.
View it at http://localhost:4444
Options:
-e Restrict the allowable directories to read from to the following regexes:
jekyll_flexible_include_plugin/.*
/dev/.*
/proc/.*
/run/.*
-h Show this error message
-r Run freely, without a debugger
-x Disable the ability to execute arbitrary commands
Debugging the Demo
To run under a debugger, for example Visual Studio Code:
-
Set breakpoints.
-
Initiate a debug session from the command line:
$ demo/bin/debug
-
Once the
Fast Debugger
signon appears, launch the Visual Studio Code launch configuration calledAttach rdbg
. -
View the generated website at
http://localhost:4444
.
Build and Push to RubyGems
To release a new version,
-
Update the version number in
version.rb
. -
Add a comment to the top of
CHANGELOG.md
. -
Commit all changes to git; if you don't the next step will fail.
-
Run the following:
$ bundle exec rake release
The above creates a git tag for the version, commits the created tag, and pushes the new
.gem
file to RubyGems.org.
Contributing
- Fork the project
- Create a descriptively named feature branch
- Add your feature
- Submit a pull request
License
The gem is available as open source under the terms of the MIT License.