middleman-hashicorp 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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