jekyll-remote-theme 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/jekyll-remote-theme.rb +30 -0
- data/lib/jekyll-remote-theme/downloader.rb +90 -0
- data/lib/jekyll-remote-theme/executor.rb +42 -0
- data/lib/jekyll-remote-theme/mock_gemspec.rb +25 -0
- data/lib/jekyll-remote-theme/munger.rb +67 -0
- data/lib/jekyll-remote-theme/theme.rb +67 -0
- data/lib/jekyll-remote-theme/version.rb +7 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8229e0bedfef52dba41d17eff8eb8e4bba23972c
|
4
|
+
data.tar.gz: 3f0cb3209952df98c3d7db8fac598590f83e6fcc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 75fef7047c3d3f2d5a6727e53a6079ca45f45d6e3340f769d95d4f4874c89df97735e905d6f9b59eca401aae8057b5ac06859bbd0a884818bcf933a115bdcf63
|
7
|
+
data.tar.gz: 68995b0baa3a99fc41682e74ac5a6c4cdb953a0b814461e03cac599dad36ff26f4c5d107994ac06ebd62cbff4ae76621aa114a18b890025b67a560d25b9f9b57
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll"
|
4
|
+
require "fileutils"
|
5
|
+
require "tempfile"
|
6
|
+
require "addressable"
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
module Jekyll
|
11
|
+
module RemoteTheme
|
12
|
+
autoload :Downloader, "jekyll-remote-theme/downloader"
|
13
|
+
autoload :Executor, "jekyll-remote-theme/executor"
|
14
|
+
autoload :MockGemspec, "jekyll-remote-theme/mock_gemspec"
|
15
|
+
autoload :Munger, "jekyll-remote-theme/munger"
|
16
|
+
autoload :Theme, "jekyll-remote-theme/theme"
|
17
|
+
autoload :VERSION, "jekyll-remote-theme/version"
|
18
|
+
|
19
|
+
CONFIG_KEY = "remote_theme".freeze
|
20
|
+
LOG_KEY = "Remote Theme:".freeze
|
21
|
+
|
22
|
+
def self.init(site)
|
23
|
+
Munger.new(site).munge!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Jekyll::Hooks.register :site, :after_reset do |site|
|
29
|
+
Jekyll::RemoteTheme.init(site)
|
30
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module RemoteTheme
|
5
|
+
class Downloader
|
6
|
+
include RemoteTheme::Executor
|
7
|
+
|
8
|
+
HOST = "https://codeload.github.com".freeze
|
9
|
+
PROJECT_URL = "https://github.com/benbalter/jekyll-remote-theme".freeze
|
10
|
+
USER_AGENT = "Jekyll Remote Theme/#{VERSION} (+#{PROJECT_URL})".freeze
|
11
|
+
TEMP_PREFIX = "jekyll-remote-theme-".freeze
|
12
|
+
|
13
|
+
attr_reader :theme
|
14
|
+
private :theme
|
15
|
+
|
16
|
+
def initialize(theme)
|
17
|
+
@theme = theme
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
if downloaded?
|
22
|
+
Jekyll.logger.debug LOG_KEY, "Using existing #{theme.name_with_owner}"
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
download
|
27
|
+
unzip
|
28
|
+
set_theme_root
|
29
|
+
|
30
|
+
@downloaded = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def downloaded?
|
34
|
+
@downloaded ||= theme_dir_exists? && !theme_dir_empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def temp_dir
|
38
|
+
@temp_dir ||= File.realpath Dir.mktmpdir(TEMP_PREFIX)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def zip_file
|
44
|
+
@zip_file ||= Tempfile.new([TEMP_PREFIX, ".zip"])
|
45
|
+
end
|
46
|
+
|
47
|
+
def download
|
48
|
+
Jekyll.logger.debug LOG_KEY, "Downloading #{zip_url} to #{zip_file.path}"
|
49
|
+
cmd = [
|
50
|
+
*timeout_command, "curl", "--url", zip_url, "--output", zip_file.path,
|
51
|
+
"--user-agent", USER_AGENT, "--fail", "--silent", "--show-error",
|
52
|
+
]
|
53
|
+
run_command(*cmd)
|
54
|
+
end
|
55
|
+
|
56
|
+
def unzip
|
57
|
+
Jekyll.logger.debug LOG_KEY, "Unzipping #{zip_file.path} to #{temp_dir}"
|
58
|
+
cmd = [*timeout_command, "unzip", zip_file.path, "-d", temp_dir]
|
59
|
+
run_command(*cmd)
|
60
|
+
zip_file.unlink
|
61
|
+
end
|
62
|
+
|
63
|
+
# Codeload generated zip files contain a top level folder in the form of
|
64
|
+
# THEME_NAME-GIT_REF/. While requests for Git repos are case incensitive,
|
65
|
+
# the zip subfolder will respect the case in the repository's name, thus
|
66
|
+
# making it impossible to predict the true path to the theme. In case we're
|
67
|
+
# on a case-sensitive file system, set the theme's root to the true theme
|
68
|
+
# directory, after we've extracted the zip and can determine its actual path.
|
69
|
+
def set_theme_root
|
70
|
+
theme.root = Dir["#{temp_dir}/*"].first
|
71
|
+
Jekyll.logger.debug LOG_KEY, "Setting theme root to #{theme.root}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Full URL to codeload zip download endpoint for the given theme
|
75
|
+
def zip_url
|
76
|
+
Addressable::URI.join(
|
77
|
+
HOST, "#{theme.owner}/", "#{theme.name}/", "zip/", theme.git_ref
|
78
|
+
).normalize
|
79
|
+
end
|
80
|
+
|
81
|
+
def theme_dir_exists?
|
82
|
+
theme.root && Dir.exist?(theme.root)
|
83
|
+
end
|
84
|
+
|
85
|
+
def theme_dir_empty?
|
86
|
+
Dir["#{theme.root}/*"].empty?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module RemoteTheme
|
7
|
+
module Executor
|
8
|
+
class ExecutionError < StandardError; end
|
9
|
+
|
10
|
+
TIMEOUT = 3600
|
11
|
+
TIMEOUT_COMMANDS = %w(timeout gtimeout).freeze
|
12
|
+
|
13
|
+
def run_command(*cmds)
|
14
|
+
stdout, stderr, status = Open3.capture3(*cmds)
|
15
|
+
|
16
|
+
if status.exitstatus != 0
|
17
|
+
Jekyll.logger.error LOG_KEY, "Error running command #{cmds.join(" ")}"
|
18
|
+
Jekyll.logger.debug LOG_KEY, stdout
|
19
|
+
raise ExecutionError, stderr
|
20
|
+
end
|
21
|
+
|
22
|
+
stdout
|
23
|
+
end
|
24
|
+
|
25
|
+
def timeout_command
|
26
|
+
@timeout_command ||= begin
|
27
|
+
cmd = TIMEOUT_COMMANDS.find { |exe| executable_exists?(exe) }
|
28
|
+
cmd ? [cmd, TIMEOUT.to_s].freeze : [].freeze
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def executable_exists?(executable)
|
35
|
+
ENV["PATH"].split(File::PATH_SEPARATOR).any? do |dir|
|
36
|
+
exe = File.join(dir, executable)
|
37
|
+
File.file?(exe) && File.executable?(exe)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module RemoteTheme
|
5
|
+
# Jekyll::Theme expects the theme's gemspec to tell it things like
|
6
|
+
# the path to the theme and runtime dependencies. MockGemspec serves as a
|
7
|
+
# stand in, since remote themes don't have Gemspecs
|
8
|
+
class MockGemspec
|
9
|
+
extend Forwardable
|
10
|
+
def_delegator :theme, :root, :full_gem_path
|
11
|
+
|
12
|
+
def initialize(theme)
|
13
|
+
@theme = theme
|
14
|
+
end
|
15
|
+
|
16
|
+
def runtime_dependencies
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :theme
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module RemoteTheme
|
5
|
+
class Munger
|
6
|
+
extend Forwardable
|
7
|
+
def_delegator :site, :config
|
8
|
+
attr_reader :site
|
9
|
+
|
10
|
+
def initialize(site)
|
11
|
+
@site = site
|
12
|
+
end
|
13
|
+
|
14
|
+
def munge!
|
15
|
+
return unless raw_theme
|
16
|
+
|
17
|
+
unless theme.valid?
|
18
|
+
Jekyll.logger.error LOG_KEY, "#{raw_theme.inspect} is not a valid remote theme"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
Jekyll.logger.info LOG_KEY, "Using theme #{theme.name_with_owner}"
|
23
|
+
return if munged?
|
24
|
+
|
25
|
+
downloader.run
|
26
|
+
configure_theme
|
27
|
+
enqueue_theme_cleanup
|
28
|
+
|
29
|
+
theme
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def munged?
|
35
|
+
site.theme && site.theme.is_a?(Jekyll::RemoteTheme::Theme)
|
36
|
+
end
|
37
|
+
|
38
|
+
def theme
|
39
|
+
@theme ||= Theme.new(raw_theme)
|
40
|
+
end
|
41
|
+
|
42
|
+
def raw_theme
|
43
|
+
config[CONFIG_KEY]
|
44
|
+
end
|
45
|
+
|
46
|
+
def downloader
|
47
|
+
@downloader ||= Downloader.new(theme)
|
48
|
+
end
|
49
|
+
|
50
|
+
def configure_theme
|
51
|
+
return unless theme
|
52
|
+
site.config["theme"] = theme.name
|
53
|
+
site.theme = theme
|
54
|
+
site.theme.configure_sass
|
55
|
+
site.send(:configure_include_paths)
|
56
|
+
end
|
57
|
+
|
58
|
+
def enqueue_theme_cleanup
|
59
|
+
at_exit do
|
60
|
+
return unless munged? && downloader.downloaded?
|
61
|
+
Jekyll.logger.debug LOG_KEY, "Cleaning up #{downloader.temp_dir}"
|
62
|
+
FileUtils.rm_rf downloader.temp_dir
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module RemoteTheme
|
5
|
+
class Theme < Jekyll::Theme
|
6
|
+
OWNER_REGEX = %r!(?<owner>[a-z0-9\-]+)!i
|
7
|
+
NAME_REGEX = %r!(?<name>[a-z0-9\-_]+)!i
|
8
|
+
REF_REGEX = %r!@(?<ref>[a-z0-9\._\-]+)!i # May be a branch, tag, or commit
|
9
|
+
THEME_REGEX = %r!\A#{OWNER_REGEX}/#{NAME_REGEX}(?:#{REF_REGEX})?\z!i
|
10
|
+
|
11
|
+
# Initializes a new Jekyll::RemoteTheme::Theme
|
12
|
+
#
|
13
|
+
# raw_theme can be in the form of:
|
14
|
+
#
|
15
|
+
# 1. owner/theme-name - a GitHub owner + theme-name string
|
16
|
+
# 2. owner/theme-name@git_ref - a GitHub owner + theme-name + Git ref string
|
17
|
+
def initialize(raw_theme)
|
18
|
+
@raw_theme = raw_theme.to_s.downcase.strip
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
theme_parts[:name]
|
23
|
+
end
|
24
|
+
|
25
|
+
def owner
|
26
|
+
theme_parts[:owner]
|
27
|
+
end
|
28
|
+
|
29
|
+
def name_with_owner
|
30
|
+
[owner, name].join("/")
|
31
|
+
end
|
32
|
+
alias_method :nwo, :name_with_owner
|
33
|
+
|
34
|
+
def valid?
|
35
|
+
theme_parts && name && owner
|
36
|
+
end
|
37
|
+
|
38
|
+
def git_ref
|
39
|
+
theme_parts[:ref] || "master"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Override Jekyll::Theme's native #root which calls gemspec.full_gem_path
|
43
|
+
def root
|
44
|
+
defined?(@root) ? @root : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def root=(path)
|
48
|
+
@root = File.realpath(path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"#<Jekyll::RemoteTheme::Theme owner=\"#{owner}\" name=\"#{name}\"" \
|
53
|
+
" ref=\"#{git_ref}\" root=\"#{root}\">"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def theme_parts
|
59
|
+
@theme_parts ||= @raw_theme.match(THEME_REGEX)
|
60
|
+
end
|
61
|
+
|
62
|
+
def gemspec
|
63
|
+
@gemspec ||= MockGemspec.new(self)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-remote-theme
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Balter
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jekyll
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jekyll-theme-primer
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.11'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.4'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- ben.balter@github.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/jekyll-remote-theme.rb
|
91
|
+
- lib/jekyll-remote-theme/downloader.rb
|
92
|
+
- lib/jekyll-remote-theme/executor.rb
|
93
|
+
- lib/jekyll-remote-theme/mock_gemspec.rb
|
94
|
+
- lib/jekyll-remote-theme/munger.rb
|
95
|
+
- lib/jekyll-remote-theme/theme.rb
|
96
|
+
- lib/jekyll-remote-theme/version.rb
|
97
|
+
homepage: https://github.com/benbalter/jekyll-remote-theme
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.6.11
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Jekyll plugin for building Jekyll sites with any GitHub-hosted theme
|
121
|
+
test_files: []
|