middleman-hashicorp 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +12 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/Makefile +21 -0
- data/README.md +183 -0
- data/Rakefile +11 -0
- data/assets/fonts/glyphicons-halflings-regular.eot +0 -0
- data/assets/fonts/glyphicons-halflings-regular.svg +229 -0
- data/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/assets/fonts/glyphicons-halflings-regular.woff +0 -0
- data/assets/images/fastly_logo.png +0 -0
- data/assets/images/icons/icon_centos.png +0 -0
- data/assets/images/icons/icon_darwin.png +0 -0
- data/assets/images/icons/icon_debian.png +0 -0
- data/assets/images/icons/icon_freebsd.png +0 -0
- data/assets/images/icons/icon_hashios.png +0 -0
- data/assets/images/icons/icon_linux.png +0 -0
- data/assets/images/icons/icon_macosx.png +0 -0
- data/assets/images/icons/icon_netbsd.png +0 -0
- data/assets/images/icons/icon_openbsd.png +0 -0
- data/assets/images/icons/icon_rpm.png +0 -0
- data/assets/images/icons/icon_solaris.png +0 -0
- data/assets/images/icons/icon_windows.png +0 -0
- data/assets/javascripts/bootstrap.js +2114 -0
- data/assets/javascripts/ie-compat.js +1 -0
- data/assets/javascripts/ie-compat/html5shiv-printshiv.js +520 -0
- data/assets/javascripts/ie-compat/html5shiv.js +322 -0
- data/assets/javascripts/ie-compat/respond.js +353 -0
- data/assets/javascripts/jquery.js +9190 -0
- data/docker/Dockerfile +22 -0
- data/lib/middleman-hashicorp.rb +6 -0
- data/lib/middleman-hashicorp/bintray.rb +98 -0
- data/lib/middleman-hashicorp/extension.rb +237 -0
- data/lib/middleman-hashicorp/redcarpet.rb +134 -0
- data/lib/middleman-hashicorp/releases.rb +32 -0
- data/lib/middleman-hashicorp/rouge.rb +16 -0
- data/lib/middleman-hashicorp/version.rb +5 -0
- data/lib/middleman_extension.rb +1 -0
- data/middleman-hashicorp.gemspec +48 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/unit/helper_spec.rb +71 -0
- data/spec/unit/markdown_spec.rb +206 -0
- data/spec/unit/releases_spec.rb +34 -0
- metadata +330 -0
data/docker/Dockerfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
FROM ruby:2.3-slim
|
2
|
+
MAINTAINER Seth Vargo <seth@hashicorp.com>
|
3
|
+
|
4
|
+
# Required packages
|
5
|
+
RUN \
|
6
|
+
apt-get -qq update && \
|
7
|
+
apt-get install -yqq build-essential git python-setuptools
|
8
|
+
|
9
|
+
# Install s3cmd
|
10
|
+
RUN \
|
11
|
+
curl -sLo /s3cmd-1.6.1.tar.gz https://github.com/s3tools/s3cmd/releases/download/v1.6.1/s3cmd-1.6.1.tar.gz && \
|
12
|
+
tar -xzvf /s3cmd-1.6.1.tar.gz && \
|
13
|
+
cd /s3cmd-1.6.1 && \
|
14
|
+
python setup.py install && \
|
15
|
+
rm -rf /s3cmd-1.6.1*
|
16
|
+
|
17
|
+
# Install the bundle
|
18
|
+
RUN \
|
19
|
+
gem install middleman-hashicorp --version "${VERSION}"
|
20
|
+
|
21
|
+
CMD (bundle check || bundle install) && \
|
22
|
+
bundle exec middleman build --clean
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
|
3
|
+
class Middleman::HashiCorp::BintrayAPI
|
4
|
+
attr_reader :filter
|
5
|
+
attr_reader :prefixed
|
6
|
+
|
7
|
+
#
|
8
|
+
# @param [String] :repo (e.g. mitchellh/packer)
|
9
|
+
# @param [String] :user (e.g. mitchellh/packer)
|
10
|
+
# @param [String] :key (e.g. abcd1234)
|
11
|
+
# @param [Proc] :filter
|
12
|
+
# @param [Boolean] :prefixed
|
13
|
+
#
|
14
|
+
def initialize(repo: nil, user: nil, key: nil, filter: nil, prefixed: true)
|
15
|
+
set_or_raise(:repo, repo)
|
16
|
+
set_or_raise(:user, user)
|
17
|
+
set_or_raise(:key, key)
|
18
|
+
|
19
|
+
@filter = filter || Proc.new {}
|
20
|
+
@prefixed = prefixed
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Get the structured downloads for the given version and required OSes.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# api.downloads_for_version("1.0.0") #=>
|
28
|
+
# {
|
29
|
+
# "os" => {
|
30
|
+
# "arch" => "http://download.url",
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
# @return [Hash]
|
35
|
+
#
|
36
|
+
def downloads_for_version(version)
|
37
|
+
url = "http://dl.bintray.com/#{repo}/"
|
38
|
+
options = { http_basic_authentication: [user, key] }
|
39
|
+
|
40
|
+
if prefixed
|
41
|
+
project = repo.split("/", 2).last
|
42
|
+
regex = /(#{Regexp.escape(project)}_#{Regexp.escape(version)}_.+?)('|")/
|
43
|
+
else
|
44
|
+
regex = /(#{Regexp.escape(version)}_.+?)('|")/
|
45
|
+
end
|
46
|
+
|
47
|
+
result = {}
|
48
|
+
|
49
|
+
open(url, options) do |file|
|
50
|
+
file.readlines.each do |line|
|
51
|
+
if line.chomp! =~ regex
|
52
|
+
filename = $1
|
53
|
+
|
54
|
+
# Hardcoded filter
|
55
|
+
next if filename.include?("SHA256SUMS")
|
56
|
+
|
57
|
+
os = filename.strip.split("_")[-2].strip
|
58
|
+
|
59
|
+
# Custom filter
|
60
|
+
next if filter.call(os, filename)
|
61
|
+
|
62
|
+
arch = line.split("_").last.split(".", 2).first
|
63
|
+
|
64
|
+
result[os] ||= {}
|
65
|
+
result[os][arch] = "https://dl.bintray.com/#{repo}/#{filename}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
result
|
71
|
+
rescue OpenURI::HTTPError
|
72
|
+
# Ignore HTTP errors and just have no versions
|
73
|
+
return {}
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
#
|
79
|
+
# Magical method that checks if the value is truly present (i.e. not an
|
80
|
+
# empty string), raising an exception if it is not. If the value is present,
|
81
|
+
# the named instance variable and attr_reader are defined.
|
82
|
+
#
|
83
|
+
# @raise [RuntimeError]
|
84
|
+
# @return [true]
|
85
|
+
#
|
86
|
+
def set_or_raise(key, value)
|
87
|
+
value = value.to_s.strip
|
88
|
+
|
89
|
+
if value.empty?
|
90
|
+
raise "Bintray #{key} was not given!"
|
91
|
+
end
|
92
|
+
|
93
|
+
instance_variable_set(:"@#{key}", value)
|
94
|
+
self.class.send(:attr_reader, :"#{key}")
|
95
|
+
|
96
|
+
true
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
module Middleman
|
2
|
+
module HashiCorp
|
3
|
+
require_relative "bintray"
|
4
|
+
require_relative "redcarpet"
|
5
|
+
require_relative "releases"
|
6
|
+
require_relative "rouge"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Middleman::HashiCorpExtension < ::Middleman::Extension
|
11
|
+
option :bintray_enabled, false, "Whether Bintray is enabeld"
|
12
|
+
option :bintray_repo, nil, "The Bintray repo name (e.g. mitchellh/packer)"
|
13
|
+
option :bintray_user, nil, "The Bintray http basic auth user (e.g. mitchellh)"
|
14
|
+
option :bintray_key, nil, "The Bintray http basic auth key (e.g. abcd1234)"
|
15
|
+
option :bintray_exclude_proc, nil, "A filter to apply for packages"
|
16
|
+
option :bintray_prefixed, true, "Whether packages are prefixed with the project name"
|
17
|
+
|
18
|
+
option :name, nil, "The name of the package (e.g. 'consul')"
|
19
|
+
option :version, nil, "The version of the package (e.g. 0.1.0)"
|
20
|
+
option :minify_javascript, true, "Whether to minimize JS or not"
|
21
|
+
option :github_slug, nil, "The project's GitHub namespace/project_name duo (e.g. hashicorp/serf)"
|
22
|
+
option :website_root, "website", "The project's middleman directory relative to the Git root"
|
23
|
+
option :releases_enabled, true, "Whether to fetch releases"
|
24
|
+
|
25
|
+
def initialize(app, options_hash = {}, &block)
|
26
|
+
super
|
27
|
+
|
28
|
+
# Grab a reference to self so we can access it deep inside blocks
|
29
|
+
_self = self
|
30
|
+
|
31
|
+
# Use syntax highlighting on fenced code blocks
|
32
|
+
# This is super hacky, but middleman does not let you activate an
|
33
|
+
# extension on "app" outside of the "configure" block.
|
34
|
+
require "middleman-syntax"
|
35
|
+
syntax = Proc.new { activate :syntax }
|
36
|
+
app.configure(:development, &syntax)
|
37
|
+
app.configure(:build, &syntax)
|
38
|
+
|
39
|
+
# Organize assets like Rails
|
40
|
+
app.set :css_dir, "assets/stylesheets"
|
41
|
+
app.set :js_dir, "assets/javascripts"
|
42
|
+
app.set :images_dir, "assets/images"
|
43
|
+
app.set :fonts_dir, "assets/fonts"
|
44
|
+
|
45
|
+
# Make custom assets available
|
46
|
+
assets = Proc.new { sprockets.import_asset "ie-compat.js" }
|
47
|
+
app.configure(:development, &assets)
|
48
|
+
app.configure(:build, &assets)
|
49
|
+
|
50
|
+
# Override the default Markdown settings to use our customer renderer
|
51
|
+
# and the options we want!
|
52
|
+
app.set :markdown_engine, :redcarpet
|
53
|
+
app.set :markdown, Middleman::HashiCorp::RedcarpetHTML::REDCARPET_OPTIONS.merge(
|
54
|
+
renderer: Middleman::HashiCorp::RedcarpetHTML
|
55
|
+
)
|
56
|
+
|
57
|
+
# Set the latest version
|
58
|
+
app.set :latest_version, options.version
|
59
|
+
|
60
|
+
# Do the releases dance
|
61
|
+
app.set :product_versions, _self.product_versions
|
62
|
+
|
63
|
+
app.set :github_slug, options.github_slug
|
64
|
+
app.set :website_root, options.website_root
|
65
|
+
|
66
|
+
# Configure the development-specific environment
|
67
|
+
app.configure :development do
|
68
|
+
# Reload the browser automatically whenever files change
|
69
|
+
require "middleman-livereload"
|
70
|
+
activate :livereload
|
71
|
+
end
|
72
|
+
|
73
|
+
# Configure the build-specific environment
|
74
|
+
minify_javascript = options.minify_javascript
|
75
|
+
app.configure :build do
|
76
|
+
# Minify CSS on build
|
77
|
+
activate :minify_css
|
78
|
+
|
79
|
+
if minify_javascript
|
80
|
+
# Minify Javascript on build
|
81
|
+
activate :minify_javascript
|
82
|
+
end
|
83
|
+
|
84
|
+
# Minify HTML
|
85
|
+
require "middleman-minify-html"
|
86
|
+
activate :minify_html do |html|
|
87
|
+
html.remove_quotes = false
|
88
|
+
html.remove_script_attributes = false
|
89
|
+
html.remove_multi_spaces = false
|
90
|
+
html.remove_http_protocol = false
|
91
|
+
html.remove_https_protocol = false
|
92
|
+
end
|
93
|
+
|
94
|
+
# Enable cache buster
|
95
|
+
activate :asset_hash
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
helpers do
|
100
|
+
#
|
101
|
+
# Output an image that corresponds to the given operating system using the
|
102
|
+
# vendored image icons.
|
103
|
+
#
|
104
|
+
# @return [String] (html)
|
105
|
+
#
|
106
|
+
def system_icon(name)
|
107
|
+
image_tag("icons/icon_#{name.to_s.downcase}.png")
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# The formatted operating system name.
|
112
|
+
#
|
113
|
+
# @return [String]
|
114
|
+
#
|
115
|
+
def pretty_os(os)
|
116
|
+
case os
|
117
|
+
when /darwin/
|
118
|
+
"Mac OS X"
|
119
|
+
when /freebsd/
|
120
|
+
"FreeBSD"
|
121
|
+
when /openbsd/
|
122
|
+
"OpenBSD"
|
123
|
+
when /netbsd/
|
124
|
+
"NetBSD"
|
125
|
+
when /linux/
|
126
|
+
"Linux"
|
127
|
+
when /windows/
|
128
|
+
"Windows"
|
129
|
+
else
|
130
|
+
os.capitalize
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# The formatted architecture name.
|
136
|
+
#
|
137
|
+
# @return [String]
|
138
|
+
#
|
139
|
+
def pretty_arch(arch)
|
140
|
+
case arch
|
141
|
+
when /all/
|
142
|
+
"Universal (32 and 64-bit)"
|
143
|
+
when /686/, /386/
|
144
|
+
"32-bit"
|
145
|
+
when /86_64/, /amd64/
|
146
|
+
"64-bit"
|
147
|
+
else
|
148
|
+
parts = arch.split("_")
|
149
|
+
|
150
|
+
if parts.empty?
|
151
|
+
raise "Could not determine pretty arch `#{arch}'!"
|
152
|
+
end
|
153
|
+
|
154
|
+
parts.last.capitalize
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Calculate the architecture for the given filename (from Bintray).
|
160
|
+
#
|
161
|
+
# @return [String]
|
162
|
+
#
|
163
|
+
def arch_for_filename(path)
|
164
|
+
file = File.basename(path, File.extname(path))
|
165
|
+
|
166
|
+
case file
|
167
|
+
when /686/, /386/
|
168
|
+
"32-bit"
|
169
|
+
when /86_64/, /amd64/
|
170
|
+
"64-bit"
|
171
|
+
else
|
172
|
+
parts = file.split("_")
|
173
|
+
|
174
|
+
if parts.empty?
|
175
|
+
raise "Could not determine arch for filename `#{file}'!"
|
176
|
+
end
|
177
|
+
|
178
|
+
parts.last.capitalize
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
#
|
183
|
+
# Return the GitHub URL associated with the project
|
184
|
+
# @return [String] the project's URL on GitHub
|
185
|
+
# @return [false] if github_slug hasn't been set
|
186
|
+
#
|
187
|
+
def github_url(specificity = :repo)
|
188
|
+
return false if github_slug.nil?
|
189
|
+
base_url = "https://github.com/#{github_slug}"
|
190
|
+
if specificity == :repo
|
191
|
+
base_url
|
192
|
+
elsif specificity == :current_page
|
193
|
+
base_url + "/blob/master/" + path_in_repository(current_page)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Return a resource's path relative to its source repo's root directory.
|
199
|
+
# @param page [Middleman::Sitemap::Resource] a sitemap resource object
|
200
|
+
# @return [String] a resource's path relative to its source repo's root
|
201
|
+
# directory
|
202
|
+
#
|
203
|
+
def path_in_repository(resource)
|
204
|
+
relative_path = resource.path.match(/.*\//).to_s
|
205
|
+
file = resource.source_file.split("/").last
|
206
|
+
website_root + "/source/" + relative_path + file
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
#
|
211
|
+
# Query the Bintray API to get the real product download versions.
|
212
|
+
#
|
213
|
+
# @return [Hash]
|
214
|
+
#
|
215
|
+
def product_versions
|
216
|
+
if !options.bintray_enabled && !options.releases_enabled
|
217
|
+
return {
|
218
|
+
"HashiOS" => {
|
219
|
+
"amd64" => "/0.1.0_hashios_amd64.zip",
|
220
|
+
"i386" => "/0.1.0_hashios_i386.zip",
|
221
|
+
}
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
if options.bintray_repo
|
226
|
+
Middleman::HashiCorp::BintrayAPI.new(
|
227
|
+
repo: options.bintray_repo,
|
228
|
+
user: options.bintray_user,
|
229
|
+
key: options.bintray_key,
|
230
|
+
filter: options.bintray_exclude_proc,
|
231
|
+
prefixed: options.bintray_prefixed,
|
232
|
+
).downloads_for_version(options.version)
|
233
|
+
else
|
234
|
+
Middleman::HashiCorp::Releases.fetch(options.name, options.version)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require "middleman-core"
|
2
|
+
require "middleman-core/renderers/redcarpet"
|
3
|
+
require "active_support/core_ext/module/attribute_accessors"
|
4
|
+
|
5
|
+
# Our custom Markdown parser - extends middleman's customer parser so we pick up
|
6
|
+
# all the magic.
|
7
|
+
class Middleman::HashiCorp::RedcarpetHTML < ::Middleman::Renderers::MiddlemanRedcarpetHTML
|
8
|
+
# Custom RedCarpet options.
|
9
|
+
REDCARPET_OPTIONS = {
|
10
|
+
autolink: true,
|
11
|
+
fenced_code_blocks: true,
|
12
|
+
tables: true,
|
13
|
+
no_intra_emphasis: true,
|
14
|
+
with_toc_data: true,
|
15
|
+
xhtml: true,
|
16
|
+
strikethrough: true,
|
17
|
+
superscript: true,
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def initialize(options = {})
|
21
|
+
super(options.merge(REDCARPET_OPTIONS))
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Override list_item to automatically add links for documentation
|
26
|
+
#
|
27
|
+
# @param [String] text
|
28
|
+
# @param [String] list_type
|
29
|
+
#
|
30
|
+
def list_item(text, list_type)
|
31
|
+
md = text.match(/(<code>(.+?)<\/code>)/)
|
32
|
+
linked = !text.match(/^(<p>)?<a(.+?)>(.+?)<\/a>\s*?[-:]?/).nil?
|
33
|
+
|
34
|
+
if !md.nil? && !linked
|
35
|
+
container, name = md.captures
|
36
|
+
anchor = anchor_for(name)
|
37
|
+
|
38
|
+
replace = %|<a name="#{anchor}" /><a href="##{anchor}">#{container}</a>|
|
39
|
+
text.sub!(container, replace)
|
40
|
+
end
|
41
|
+
|
42
|
+
"<li>#{text}</li>\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Override block_html to support parsing nested markdown blocks.
|
47
|
+
#
|
48
|
+
# @param [String] raw
|
49
|
+
#
|
50
|
+
def block_html(raw)
|
51
|
+
raw = unindent(raw)
|
52
|
+
|
53
|
+
if md = raw.match(/\<(.+?)\>(.*)\<(\/.+?)\>/m)
|
54
|
+
open_tag, content, close_tag = md.captures
|
55
|
+
"<#{open_tag}>\n#{recursive_render(unindent(content))}<#{close_tag}>"
|
56
|
+
else
|
57
|
+
raw
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Override paragraph to support custom alerts.
|
63
|
+
#
|
64
|
+
# @param [String] text
|
65
|
+
# @return [String]
|
66
|
+
#
|
67
|
+
def paragraph(text)
|
68
|
+
add_alerts("<p>#{text.strip}</p>\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
#
|
74
|
+
# Remove any special characters from the anchor name.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# anchor_for("this") #=> "this"
|
78
|
+
# anchor_for("this is cool") #=> "this_is_cool"
|
79
|
+
# anchor_for("this__is__cool!") #=> "this__is__cool_"
|
80
|
+
#
|
81
|
+
#
|
82
|
+
# @param [String] text
|
83
|
+
# @return [String]
|
84
|
+
#
|
85
|
+
def anchor_for(text)
|
86
|
+
text.gsub(/[^[:word:]]/, "_").squeeze("_")
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# This is jank, but Redcarpet does not provide a way to access the
|
91
|
+
# renderer from inside Redcarpet::Markdown. Since we know who we are, we
|
92
|
+
# can cheat a bit.
|
93
|
+
#
|
94
|
+
# @param [String] markdown
|
95
|
+
# @return [String]
|
96
|
+
#
|
97
|
+
def recursive_render(markdown)
|
98
|
+
Redcarpet::Markdown.new(self.class, REDCARPET_OPTIONS).render(markdown)
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Add alert text to the given markdown.
|
103
|
+
#
|
104
|
+
# @param [String] text
|
105
|
+
# @return [String]
|
106
|
+
#
|
107
|
+
def add_alerts(text)
|
108
|
+
map = {
|
109
|
+
"=>" => "success",
|
110
|
+
"->" => "info",
|
111
|
+
"~>" => "warning",
|
112
|
+
"!>" => "danger",
|
113
|
+
}
|
114
|
+
|
115
|
+
regexp = map.map { |k, _| Regexp.escape(k) }.join("|")
|
116
|
+
|
117
|
+
if md = text.match(/^<p>(#{regexp})/)
|
118
|
+
key = md.captures[0]
|
119
|
+
klass = map[key]
|
120
|
+
text.gsub!(/#{Regexp.escape(key)}\s+?/, "")
|
121
|
+
|
122
|
+
return <<-EOH.gsub(/^ {8}/, "")
|
123
|
+
<div class="alert alert-#{klass}" role="alert">
|
124
|
+
#{text}</div>
|
125
|
+
EOH
|
126
|
+
else
|
127
|
+
return text
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def unindent(string)
|
132
|
+
string.gsub(/^#{string.scan(/^[[:blank:]]+/).min_by { |l| l.length }}/, "")
|
133
|
+
end
|
134
|
+
end
|