middleman-hashicorp 0.2.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +21 -0
  6. data/Makefile +21 -0
  7. data/README.md +183 -0
  8. data/Rakefile +11 -0
  9. data/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  10. data/assets/fonts/glyphicons-halflings-regular.svg +229 -0
  11. data/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  12. data/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  13. data/assets/images/fastly_logo.png +0 -0
  14. data/assets/images/icons/icon_centos.png +0 -0
  15. data/assets/images/icons/icon_darwin.png +0 -0
  16. data/assets/images/icons/icon_debian.png +0 -0
  17. data/assets/images/icons/icon_freebsd.png +0 -0
  18. data/assets/images/icons/icon_hashios.png +0 -0
  19. data/assets/images/icons/icon_linux.png +0 -0
  20. data/assets/images/icons/icon_macosx.png +0 -0
  21. data/assets/images/icons/icon_netbsd.png +0 -0
  22. data/assets/images/icons/icon_openbsd.png +0 -0
  23. data/assets/images/icons/icon_rpm.png +0 -0
  24. data/assets/images/icons/icon_solaris.png +0 -0
  25. data/assets/images/icons/icon_windows.png +0 -0
  26. data/assets/javascripts/bootstrap.js +2114 -0
  27. data/assets/javascripts/ie-compat.js +1 -0
  28. data/assets/javascripts/ie-compat/html5shiv-printshiv.js +520 -0
  29. data/assets/javascripts/ie-compat/html5shiv.js +322 -0
  30. data/assets/javascripts/ie-compat/respond.js +353 -0
  31. data/assets/javascripts/jquery.js +9190 -0
  32. data/docker/Dockerfile +22 -0
  33. data/lib/middleman-hashicorp.rb +6 -0
  34. data/lib/middleman-hashicorp/bintray.rb +98 -0
  35. data/lib/middleman-hashicorp/extension.rb +237 -0
  36. data/lib/middleman-hashicorp/redcarpet.rb +134 -0
  37. data/lib/middleman-hashicorp/releases.rb +32 -0
  38. data/lib/middleman-hashicorp/rouge.rb +16 -0
  39. data/lib/middleman-hashicorp/version.rb +5 -0
  40. data/lib/middleman_extension.rb +1 -0
  41. data/middleman-hashicorp.gemspec +48 -0
  42. data/spec/spec_helper.rb +34 -0
  43. data/spec/unit/helper_spec.rb +71 -0
  44. data/spec/unit/markdown_spec.rb +206 -0
  45. data/spec/unit/releases_spec.rb +34 -0
  46. metadata +330 -0
@@ -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,6 @@
1
+ require "middleman-core"
2
+
3
+ ::Middleman::Extensions.register(:hashicorp) do
4
+ require "middleman-hashicorp/extension"
5
+ ::Middleman::HashiCorpExtension
6
+ end
@@ -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
+ "=&gt;" => "success",
110
+ "-&gt;" => "info",
111
+ "~&gt;" => "warning",
112
+ "!&gt;" => "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