static-rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +63 -0
- data/.gitignore +8 -0
- data/.standard.yml +5 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +99 -0
- data/LICENSE.txt +21 -0
- data/README.md +162 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/generators/static_rails/initializer_generator.rb +12 -0
- data/lib/generators/templates/static.rb +55 -0
- data/lib/static-rails.rb +8 -0
- data/lib/static-rails/compile.rb +21 -0
- data/lib/static-rails/configuration.rb +39 -0
- data/lib/static-rails/error.rb +3 -0
- data/lib/static-rails/matches_request_to_static_site.rb +24 -0
- data/lib/static-rails/proxy_middleware.rb +51 -0
- data/lib/static-rails/rack_server_check.rb +36 -0
- data/lib/static-rails/railtie.rb +31 -0
- data/lib/static-rails/server.rb +66 -0
- data/lib/static-rails/server_store.rb +29 -0
- data/lib/static-rails/site.rb +33 -0
- data/lib/static-rails/static_middleware.rb +39 -0
- data/lib/static-rails/version.rb +3 -0
- data/lib/static-rails/waits_for_connection.rb +25 -0
- data/lib/tasks/static-rails.rake +20 -0
- data/static-rails.gemspec +25 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9a555d5b5ad757f03beff3a2b8e25b4abebdb3e9446d6089017c02b11450bc4a
|
4
|
+
data.tar.gz: 4b48dff937b530a8d407d312091b80649dbc0a4522457dc022eba8ebb8926af7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c789c382631f82888708250d75cbf7a541bb344a46706699903fe267e957ced8f20b5bf4ff24e27171eaf2c6fb3d2ef82e81cb7ce0044a5fc7abb99f92b960f8
|
7
|
+
data.tar.gz: 41dcfb55228d18933242c3c2aef24cbbcccd6cd0508ce10a97f2c0a3849db03aa81d099efd43f571fd57bd77220aabd6fc176ab8c7188f29ad9eafcb1b6642fd
|
@@ -0,0 +1,63 @@
|
|
1
|
+
version: 2
|
2
|
+
jobs:
|
3
|
+
build:
|
4
|
+
docker:
|
5
|
+
- image: circleci/ruby:2.7-node-browsers
|
6
|
+
environment:
|
7
|
+
RUBYOPT: "-W:no-deprecated -W:no-experimental"
|
8
|
+
steps:
|
9
|
+
- checkout
|
10
|
+
|
11
|
+
- run:
|
12
|
+
name: Add fake blog to the host
|
13
|
+
command: echo 127.0.0.1 blog.localhost | sudo tee -a /etc/hosts
|
14
|
+
|
15
|
+
# One of the apps needs hugo
|
16
|
+
- run: apt-get update && apt-get install -y --no-install-recommends hugo
|
17
|
+
|
18
|
+
# Bundle install dependencies
|
19
|
+
- type: cache-restore
|
20
|
+
key: v1-main-{{ checksum "Gemfile.lock" }}
|
21
|
+
|
22
|
+
- run: gem install bundler --version `tail -1 Gemfile.lock`
|
23
|
+
- run: bundle install --path vendor/bundle
|
24
|
+
|
25
|
+
- type: cache-save
|
26
|
+
key: v1-main-{{ checksum "Gemfile.lock" }}
|
27
|
+
paths:
|
28
|
+
- vendor/bundle
|
29
|
+
|
30
|
+
# Bundle install dependencies for example app
|
31
|
+
- type: cache-restore
|
32
|
+
key: v1-example-{{ checksum "example/Gemfile.lock" }}
|
33
|
+
|
34
|
+
- run: |
|
35
|
+
cd example
|
36
|
+
bundle install --path vendor/bundle
|
37
|
+
|
38
|
+
- type: cache-save
|
39
|
+
key: v1-example-{{ checksum "example/Gemfile.lock" }}
|
40
|
+
paths:
|
41
|
+
- example/vendor/bundle
|
42
|
+
|
43
|
+
# Yarn dependencies
|
44
|
+
- restore_cache:
|
45
|
+
keys:
|
46
|
+
- v2-yarn-{{ checksum "example/yarn.lock" }}
|
47
|
+
# fallback to using the latest cache if no exact match is found
|
48
|
+
- v2-yarn-
|
49
|
+
|
50
|
+
- run: |
|
51
|
+
cd example
|
52
|
+
yarn install
|
53
|
+
|
54
|
+
- save_cache:
|
55
|
+
paths:
|
56
|
+
- example/node_modules
|
57
|
+
- ~/.cache
|
58
|
+
key: v2-yarn-{{ checksum "example/yarn.lock" }}
|
59
|
+
|
60
|
+
- run: bin/rake standard:fix
|
61
|
+
- run: |
|
62
|
+
cd example
|
63
|
+
./script/test
|
data/.gitignore
ADDED
data/.standard.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
static-rails (0.0.1)
|
5
|
+
rack-proxy (~> 0.6)
|
6
|
+
railties (>= 5.0.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionpack (6.0.2.2)
|
12
|
+
actionview (= 6.0.2.2)
|
13
|
+
activesupport (= 6.0.2.2)
|
14
|
+
rack (~> 2.0, >= 2.0.8)
|
15
|
+
rack-test (>= 0.6.3)
|
16
|
+
rails-dom-testing (~> 2.0)
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
18
|
+
actionview (6.0.2.2)
|
19
|
+
activesupport (= 6.0.2.2)
|
20
|
+
builder (~> 3.1)
|
21
|
+
erubi (~> 1.4)
|
22
|
+
rails-dom-testing (~> 2.0)
|
23
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
24
|
+
activesupport (6.0.2.2)
|
25
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
26
|
+
i18n (>= 0.7, < 2)
|
27
|
+
minitest (~> 5.1)
|
28
|
+
tzinfo (~> 1.1)
|
29
|
+
zeitwerk (~> 2.2)
|
30
|
+
ast (2.4.0)
|
31
|
+
builder (3.2.4)
|
32
|
+
concurrent-ruby (1.1.6)
|
33
|
+
crass (1.0.6)
|
34
|
+
erubi (1.9.0)
|
35
|
+
i18n (1.8.2)
|
36
|
+
concurrent-ruby (~> 1.0)
|
37
|
+
jaro_winkler (1.5.4)
|
38
|
+
loofah (2.5.0)
|
39
|
+
crass (~> 1.0.2)
|
40
|
+
nokogiri (>= 1.5.9)
|
41
|
+
method_source (1.0.0)
|
42
|
+
mini_portile2 (2.4.0)
|
43
|
+
minitest (5.14.0)
|
44
|
+
nokogiri (1.10.9)
|
45
|
+
mini_portile2 (~> 2.4.0)
|
46
|
+
parallel (1.19.1)
|
47
|
+
parser (2.7.1.1)
|
48
|
+
ast (~> 2.4.0)
|
49
|
+
rack (2.2.2)
|
50
|
+
rack-proxy (0.6.5)
|
51
|
+
rack
|
52
|
+
rack-test (1.1.0)
|
53
|
+
rack (>= 1.0, < 3)
|
54
|
+
rails-dom-testing (2.0.3)
|
55
|
+
activesupport (>= 4.2.0)
|
56
|
+
nokogiri (>= 1.6)
|
57
|
+
rails-html-sanitizer (1.3.0)
|
58
|
+
loofah (~> 2.3)
|
59
|
+
railties (6.0.2.2)
|
60
|
+
actionpack (= 6.0.2.2)
|
61
|
+
activesupport (= 6.0.2.2)
|
62
|
+
method_source
|
63
|
+
rake (>= 0.8.7)
|
64
|
+
thor (>= 0.20.3, < 2.0)
|
65
|
+
rainbow (3.0.0)
|
66
|
+
rake (13.0.1)
|
67
|
+
rexml (3.2.4)
|
68
|
+
rubocop (0.80.1)
|
69
|
+
jaro_winkler (~> 1.5.1)
|
70
|
+
parallel (~> 1.10)
|
71
|
+
parser (>= 2.7.0.1)
|
72
|
+
rainbow (>= 2.2.2, < 4.0)
|
73
|
+
rexml
|
74
|
+
ruby-progressbar (~> 1.7)
|
75
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
76
|
+
rubocop-performance (1.5.2)
|
77
|
+
rubocop (>= 0.71.0)
|
78
|
+
ruby-progressbar (1.10.1)
|
79
|
+
standard (0.2.5)
|
80
|
+
rubocop (~> 0.80.1)
|
81
|
+
rubocop-performance (~> 1.5.2)
|
82
|
+
thor (1.0.1)
|
83
|
+
thread_safe (0.3.6)
|
84
|
+
tzinfo (1.2.7)
|
85
|
+
thread_safe (~> 0.1)
|
86
|
+
unicode-display_width (1.6.1)
|
87
|
+
zeitwerk (2.3.0)
|
88
|
+
|
89
|
+
PLATFORMS
|
90
|
+
ruby
|
91
|
+
|
92
|
+
DEPENDENCIES
|
93
|
+
minitest (~> 5.0)
|
94
|
+
rake (~> 13.0)
|
95
|
+
standard
|
96
|
+
static-rails!
|
97
|
+
|
98
|
+
BUNDLED WITH
|
99
|
+
2.1.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Test Double, LLC
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# static-rails
|
2
|
+
|
3
|
+
A gem to support integrating one or more static sites with your Rails
|
4
|
+
application.
|
5
|
+
|
6
|
+
## Install
|
7
|
+
|
8
|
+
Add this to the Gemfile of your Rails app:
|
9
|
+
|
10
|
+
```
|
11
|
+
gem "static-rails"
|
12
|
+
```
|
13
|
+
|
14
|
+
And run this to generate an initializer in which to configure your static sites:
|
15
|
+
|
16
|
+
```
|
17
|
+
$ rails g static_rails:initializer
|
18
|
+
```
|
19
|
+
|
20
|
+
This will create an documented configuration file in
|
21
|
+
`config/initializers/static.rb`
|
22
|
+
|
23
|
+
Once installed, any `config.sites` you specify will be handled by static-rails
|
24
|
+
in the following way, by default:
|
25
|
+
|
26
|
+
* In `development` and `test`, static-rails will start each site's
|
27
|
+
`serve_command` and proxy any requests to the Rails server that match the
|
28
|
+
`url_subdomain` and `url_root_path` to that server
|
29
|
+
|
30
|
+
* When running `rake assets:precompile` (typically performed during a deploy),
|
31
|
+
static-rails will run the `compile_command` for each configured site
|
32
|
+
|
33
|
+
* In `production`, static-rails will use `Rack::Static` to map any requests to
|
34
|
+
the Rails server that match the site's `url_subdomain` and `url_root_path`
|
35
|
+
settings (note that this is the opposite default for Rails'
|
36
|
+
`config.public_file_server.enabled` setting, which is disabled by default)
|
37
|
+
|
38
|
+
For examples on how to configure your static site, read on!
|
39
|
+
|
40
|
+
## Configuring your static site generators
|
41
|
+
|
42
|
+
### Using Jekyll
|
43
|
+
|
44
|
+
If you have a Jekyll app that you plan on serving from a non-root path (say,
|
45
|
+
`/docs`), then you'll need to set the `baseurl` in `_config.yml`:
|
46
|
+
|
47
|
+
```yml
|
48
|
+
baseurl: "/docs"
|
49
|
+
```
|
50
|
+
|
51
|
+
(Note that this means running the Jekyll application directly using `bundle exec
|
52
|
+
jekyll serve` will also start serving at `http://127.0.0.1:4000/docs/`)
|
53
|
+
|
54
|
+
### Using Hugo
|
55
|
+
|
56
|
+
If you are mounting your [Hugo](https://gohugo.io) app to anything but the root
|
57
|
+
path, you'll need to specify that path in the `baseURL` of your root
|
58
|
+
`config.toml` file, like so:
|
59
|
+
|
60
|
+
```toml
|
61
|
+
baseURL = "http://blog.example.com/docs"
|
62
|
+
```
|
63
|
+
|
64
|
+
Additionally, getting Hugo to play nicely when being proxied by your Rails
|
65
|
+
server in development and test can be a little tricky, because most themes will
|
66
|
+
render fully-qualified URLs into markup when running the `hugo server` command.
|
67
|
+
That means if you're forwarding `http://blog.localhost:3000/docs` to a Hugo
|
68
|
+
server running on `http://localhost:1313`, it's very likely the static files
|
69
|
+
(e.g. all the links on the page) will have references to
|
70
|
+
`http://localhost:1313`, which may result in accidentally navigating away from
|
71
|
+
your Rails development server unexpectedly.
|
72
|
+
|
73
|
+
To mitigate this, there are a few things you can do:
|
74
|
+
|
75
|
+
* Favor `.RelPermalink` in your templates over `.Permalink` where possible.
|
76
|
+
* In place of referring to `{{.Site.BaseURL}}` in your templates, generate a
|
77
|
+
base path with `{{ "/" | relURL }}` (given the above `baseURL`, this will
|
78
|
+
render `"/marketing/"`)
|
79
|
+
|
80
|
+
Also, because Hugo will serve `/livereload.js` from the root, it probably won't
|
81
|
+
work in development when running through the static-rails proxy in development.
|
82
|
+
You might consider disabling it with `--disableLiveReload`.
|
83
|
+
|
84
|
+
A static-rails config for a Hugo configuration in `sites` might look like:
|
85
|
+
|
86
|
+
```rb
|
87
|
+
{
|
88
|
+
name: "docs",
|
89
|
+
url_root_path: "/docs",
|
90
|
+
source_dir: "static/docs",
|
91
|
+
server_command: "hugo server",
|
92
|
+
server_port: 8080,
|
93
|
+
compile_command: "hugo",
|
94
|
+
compile_dir: "static/docs/public"
|
95
|
+
}
|
96
|
+
```
|
97
|
+
|
98
|
+
### Using Eleventy
|
99
|
+
|
100
|
+
If you are mounting your [Eleventy](https://www.11ty.dev) app to anything but
|
101
|
+
the root path, you'll want to configure a path prefix in `.eleventy.js`
|
102
|
+
|
103
|
+
```js
|
104
|
+
module.exports = {
|
105
|
+
pathPrefix: "/docs/"
|
106
|
+
}
|
107
|
+
```
|
108
|
+
|
109
|
+
Alternatively, you can specify this from the command line with `--pathprefix
|
110
|
+
/docs`.
|
111
|
+
|
112
|
+
A static-rails config for an Eleventy configuration in `sites` might look like:
|
113
|
+
|
114
|
+
```rb
|
115
|
+
{
|
116
|
+
name: "docs",
|
117
|
+
url_root_path: "/docs",
|
118
|
+
source_dir: "static/docs",
|
119
|
+
server_command: "npx @11ty/eleventy --serve --pathprefix /docs",
|
120
|
+
server_port: 8080,
|
121
|
+
compile_command: "npx @11ty/eleventy --pathprefix /docs",
|
122
|
+
compile_dir: "static/docs/_site"
|
123
|
+
}
|
124
|
+
```
|
125
|
+
|
126
|
+
### Using Gatsby
|
127
|
+
|
128
|
+
** ⚠️ Gatsby is unlikely to work in development mode, due to [this
|
129
|
+
issue](https://github.com/gatsbyjs/gatsby/issues/18143), wherein all the assets
|
130
|
+
are actually served over a socket.io WebSocket channel and not able to be
|
131
|
+
proxied effectively. ⚠️ **
|
132
|
+
|
133
|
+
If you're mounting a [Gatsby](https://www.gatsbyjs.org) site to a non-root path
|
134
|
+
(e.g. in static-rails, you've configured its `url_root_path` to, say,
|
135
|
+
`careers`), then you'll want to configure the same root path in Gatsby as well,
|
136
|
+
so that its development servers and built assets line up correctly.
|
137
|
+
|
138
|
+
To do this, first add the `pathPrefix` property to `gatsby-config.js`:
|
139
|
+
|
140
|
+
```js
|
141
|
+
module.exports = {
|
142
|
+
pathPrefix: `/careers`,
|
143
|
+
// …
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
147
|
+
Next, add the flag `--prefix-paths` to both the Gatsby site's `server_command`
|
148
|
+
and `compile_command`, or else the setting will be ignored.
|
149
|
+
|
150
|
+
A static-rails config for a Gatsby configuration in `sites` might look like:
|
151
|
+
|
152
|
+
```rb
|
153
|
+
{
|
154
|
+
name: "gatsby",
|
155
|
+
url_root_path: "/docs",
|
156
|
+
source_dir: "static/docs",
|
157
|
+
server_command: "npx gatsby develop --prefix-paths",
|
158
|
+
server_port: 8000,
|
159
|
+
compile_command: "npx gatsby build --prefix-paths",
|
160
|
+
compile_dir: "static/docs/public"
|
161
|
+
},
|
162
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rails/static/site"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module StaticRails
|
2
|
+
module Generators
|
3
|
+
class InitializerGenerator < ::Rails::Generators::Base
|
4
|
+
source_root File.expand_path("../../templates", __FILE__)
|
5
|
+
|
6
|
+
desc "Creates a sample static-rails initializer."
|
7
|
+
def copy_initializer
|
8
|
+
copy_file "static.rb", "config/initializers/static.rb"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
StaticRails.config do |config|
|
2
|
+
# Control whether static-rails adds a middleware to proxy requests to your static site servers
|
3
|
+
# config.proxy_requests = !Rails.env.production?
|
4
|
+
|
5
|
+
# Control whether static-rails adds a middleware to serve your sites' compiled static assets with Static::Rack (has no effect if proxy_requests is enabled)
|
6
|
+
# config.serve_compiled_assets = Rails.env.production?
|
7
|
+
|
8
|
+
# Timeout in seconds to wait when proxying to a static server
|
9
|
+
# (Applies when a site has both start_server and ping_server set to true)
|
10
|
+
# config.ping_server_timeout = 5
|
11
|
+
|
12
|
+
# The list of static sites you are hosting with static-rails.
|
13
|
+
# Note that order matters! Request will be forwarded to the first site that
|
14
|
+
# matches the subdomain and root path (this probably means you want any sites
|
15
|
+
# with subdomains listed first)
|
16
|
+
config.sites = [
|
17
|
+
# {
|
18
|
+
# # Unique name for the site
|
19
|
+
# name: "blog",
|
20
|
+
#
|
21
|
+
# # File path to the static app relative to Rails root path
|
22
|
+
# source_dir: "static/blog",
|
23
|
+
#
|
24
|
+
# # Constrain static app to the following subdomain (omit or leave nil if you aren't hosting the site on a subdomain)
|
25
|
+
# url_subdomain: "blog",
|
26
|
+
#
|
27
|
+
# # Mount the static site web hosting to a certain sub-path (e.g. "/docs")
|
28
|
+
# url_root_path: "/",
|
29
|
+
#
|
30
|
+
# # Whether to run the local development/test server or not
|
31
|
+
# start_server: !Rails.env.production?,
|
32
|
+
#
|
33
|
+
# # If start_server is true, wait to proxy requests to the server until it can connect to server_host over TCP on server_port
|
34
|
+
# ping_server: true
|
35
|
+
#
|
36
|
+
# # The command to execute when running the static app in local development/test environments
|
37
|
+
# server_command: "hugo server",
|
38
|
+
#
|
39
|
+
# # The host the local development/test server should be reached on
|
40
|
+
# server_host: "localhost",
|
41
|
+
#
|
42
|
+
# # The port the local development/test server should be reached on
|
43
|
+
# server_port: "1313",
|
44
|
+
#
|
45
|
+
# # The root path on the local development/test server to which requests should be forwarded
|
46
|
+
# server_path: "/",
|
47
|
+
#
|
48
|
+
# # The command to execute when building the static app for production
|
49
|
+
# compile_command: "hugo",
|
50
|
+
#
|
51
|
+
# # The destination of the production-compiled assets, relative to Rails root
|
52
|
+
# compile_dir: "static/blog/dist"
|
53
|
+
# },
|
54
|
+
]
|
55
|
+
end
|
data/lib/static-rails.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module StaticRails
|
2
|
+
def self.compile
|
3
|
+
CompilesSites.new.call(StaticRails.config)
|
4
|
+
end
|
5
|
+
|
6
|
+
class CompilesSites
|
7
|
+
def call(config)
|
8
|
+
config.sites.each do |site|
|
9
|
+
Dir.chdir(config.app.root.join(site.source_dir)) do
|
10
|
+
Bundler.with_unbundled_env do
|
11
|
+
puts "=> Compiling static site \"#{site.name}\" to #{site.compile_dir}"
|
12
|
+
result = system site.compile_command
|
13
|
+
unless result == true
|
14
|
+
raise Error.new("Compilation of static site \"#{site.name}\" failed (in directory \"#{site.source_dir}\" with command: `#{site.compile_command}`)")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative "site"
|
2
|
+
|
3
|
+
module StaticRails
|
4
|
+
def self.config(&blk)
|
5
|
+
@configuration ||= Configuration.new
|
6
|
+
|
7
|
+
@configuration.tap do |config|
|
8
|
+
blk&.call(config)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Configuration
|
13
|
+
# When Rails invokes our Railtie, we'll save off a reference to the Rails app
|
14
|
+
attr_accessor :app
|
15
|
+
|
16
|
+
# When true, the ProxyMiddleware will be added
|
17
|
+
attr_accessor :proxy_requests
|
18
|
+
|
19
|
+
# When true, the StaticMiddleware will be added
|
20
|
+
attr_accessor :serve_compiled_assets
|
21
|
+
|
22
|
+
# Number of seconds to wait on sites to confirm servers are ready
|
23
|
+
attr_accessor :ping_server_timeout
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@sites = []
|
27
|
+
@proxy_requests = !Rails.env.production?
|
28
|
+
@serve_compiled_assets = Rails.env.production?
|
29
|
+
@ping_server_timeout = 5
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :sites
|
33
|
+
def sites=(sites)
|
34
|
+
@sites = Array.wrap(sites).map { |site|
|
35
|
+
Site.new(site)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module StaticRails
|
2
|
+
class MatchesRequestToStaticSite
|
3
|
+
def call(request)
|
4
|
+
StaticRails.config.sites.find { |site|
|
5
|
+
subdomain_match?(site, request) && path_match?(site, request)
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def subdomain_match?(site, request)
|
12
|
+
return true if site.url_subdomain.nil?
|
13
|
+
|
14
|
+
expected = site.url_subdomain.split(".")
|
15
|
+
actual = request.host.split(".")
|
16
|
+
|
17
|
+
expected.enum_for.with_index.all? { |sub, i| actual[i] == sub }
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_match?(site, request)
|
21
|
+
request.path_info.start_with?(site.url_root_path)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "rack-proxy"
|
2
|
+
|
3
|
+
require_relative "matches_request_to_static_site"
|
4
|
+
require_relative "server_store"
|
5
|
+
|
6
|
+
module StaticRails
|
7
|
+
class ProxyMiddleware < Rack::Proxy
|
8
|
+
def initialize(app)
|
9
|
+
@matches_request_to_static_site = MatchesRequestToStaticSite.new
|
10
|
+
@app = app
|
11
|
+
@servers = {}
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform_request(env)
|
16
|
+
return @app.call(env) unless StaticRails.config.proxy_requests
|
17
|
+
ServerStore.instance.ensure_all_servers_are_started
|
18
|
+
|
19
|
+
req = Rack::Request.new(env)
|
20
|
+
server_store = ServerStore.instance
|
21
|
+
server_store.ensure_servers_are_up
|
22
|
+
|
23
|
+
if (req.get? || req.head?) && (site = @matches_request_to_static_site.call(req))
|
24
|
+
if site.ping_server && (server = server_store.server_for(site))
|
25
|
+
server.wait_until_ready
|
26
|
+
end
|
27
|
+
|
28
|
+
@backend = URI("http://#{site.server_host}:#{site.server_port}")
|
29
|
+
|
30
|
+
env["HTTP_HOST"] = @backend.host
|
31
|
+
env["PATH_INFO"] = forwarding_path(site, req)
|
32
|
+
env["HTTP_COOKIE"] = ""
|
33
|
+
super(env)
|
34
|
+
else
|
35
|
+
@app.call(env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def forwarding_path(site, req)
|
42
|
+
req_path = req.path_info
|
43
|
+
|
44
|
+
if req_path == site.url_root_path && !req_path.end_with?("/")
|
45
|
+
req_path + "/" # <- Necessary for getting jekyll, possibly hugo to serve the root
|
46
|
+
else
|
47
|
+
req_path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module StaticRails
|
2
|
+
# Shamelessly ripped out of @danmeyer's Coverband: https://github.com/danmayer/coverband/blob/master/lib/coverband/integrations/rack_server_check.rb#L14
|
3
|
+
#
|
4
|
+
# Copyright (c) 2010-2018 Dan Mayer
|
5
|
+
#
|
6
|
+
# Distributed under the MIT License
|
7
|
+
#
|
8
|
+
# Details:
|
9
|
+
# https://github.com/danmayer/coverband/blob/master/LICENSE.txt
|
10
|
+
class RackServerCheck
|
11
|
+
def self.running?
|
12
|
+
new(Kernel.caller_locations).running?
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(stack)
|
16
|
+
@stack = stack
|
17
|
+
end
|
18
|
+
|
19
|
+
def running?
|
20
|
+
rack_server? || rails_server?
|
21
|
+
end
|
22
|
+
|
23
|
+
def rack_server?
|
24
|
+
@stack.any? { |line| line.path.include?("lib/rack/") }
|
25
|
+
end
|
26
|
+
|
27
|
+
def rails_server?
|
28
|
+
@stack.any? do |location|
|
29
|
+
(
|
30
|
+
(location.path.include?("rails/commands/commands_tasks.rb") && location.label == "server") ||
|
31
|
+
(location.path.include?("rails/commands/server/server_command.rb") && location.label == "perform")
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "rack_server_check"
|
2
|
+
require_relative "server_store"
|
3
|
+
require_relative "proxy_middleware"
|
4
|
+
require_relative "static_middleware"
|
5
|
+
|
6
|
+
module StaticRails
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
rake_tasks do
|
9
|
+
load "tasks/static-rails.rake"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Note that user initializer won't have run yet, but we seem to need to
|
13
|
+
# register the middleware by now if it's going to properly get added to the
|
14
|
+
# stack. So if the user overrides these flags' defaults, the middleware will
|
15
|
+
# still be added but will be responsible itself for skipping each request
|
16
|
+
if StaticRails.config.proxy_requests
|
17
|
+
config.app_middleware.insert_before 0, ProxyMiddleware
|
18
|
+
elsif StaticRails.config.serve_compiled_assets
|
19
|
+
config.app_middleware.insert_before 0, StaticMiddleware
|
20
|
+
end
|
21
|
+
|
22
|
+
config.after_initialize do |app|
|
23
|
+
static_rails_config = StaticRails.config
|
24
|
+
static_rails_config.app = app
|
25
|
+
|
26
|
+
if RackServerCheck.running?
|
27
|
+
ServerStore.instance.ensure_all_servers_are_started
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative "waits_for_connection"
|
2
|
+
|
3
|
+
module StaticRails
|
4
|
+
class Server
|
5
|
+
def initialize(site)
|
6
|
+
@site = site
|
7
|
+
@ready = false
|
8
|
+
@waits_for_connection = WaitsForConnection.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
return if started?
|
13
|
+
@pid = spawn_process
|
14
|
+
set_at_exit_hook
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def started?
|
19
|
+
return false unless @pid.present?
|
20
|
+
|
21
|
+
begin
|
22
|
+
Process.getpgid(@pid)
|
23
|
+
true
|
24
|
+
rescue Errno::ESRCH
|
25
|
+
@ready = false
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def wait_until_ready
|
31
|
+
return if @ready
|
32
|
+
@waits_for_connection.call(@site)
|
33
|
+
@ready = true
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def spawn_process
|
39
|
+
options = {
|
40
|
+
in: "/dev/null",
|
41
|
+
out: "/dev/stdout",
|
42
|
+
err: "/dev/stderr",
|
43
|
+
close_others: true,
|
44
|
+
chdir: StaticRails.config.app.root.join(@site.source_dir).to_s
|
45
|
+
}
|
46
|
+
|
47
|
+
Rails.logger.info "=> Starting #{@site.name} static server"
|
48
|
+
Bundler.with_unbundled_env do
|
49
|
+
Process.spawn(ENV, @site.server_command, options).tap do |pid|
|
50
|
+
Process.detach(pid)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_at_exit_hook
|
56
|
+
return if @at_exit_hook_set
|
57
|
+
at_exit do
|
58
|
+
if started?
|
59
|
+
Rails.logger.info "=> Stopping #{@site.name} static server"
|
60
|
+
Process.kill("INT", @pid)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@at_exit_hook_set = true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "server"
|
2
|
+
|
3
|
+
module StaticRails
|
4
|
+
class ServerStore
|
5
|
+
def self.instance
|
6
|
+
@instance ||= new
|
7
|
+
end
|
8
|
+
|
9
|
+
def ensure_all_servers_are_started
|
10
|
+
StaticRails.config.sites.select(&:start_server).each do |site|
|
11
|
+
server_for(site).start
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def server_for(site)
|
16
|
+
@servers[site] ||= Server.new(site)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ensure_servers_are_up
|
20
|
+
@servers.values.each(&:start)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@servers = {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module StaticRails
|
2
|
+
class Site < Struct.new(
|
3
|
+
:name,
|
4
|
+
:url_subdomain,
|
5
|
+
:url_root_path,
|
6
|
+
:source_dir,
|
7
|
+
:start_server,
|
8
|
+
:ping_server,
|
9
|
+
:server_command,
|
10
|
+
:server_host,
|
11
|
+
:server_port,
|
12
|
+
:server_path,
|
13
|
+
:compile_command,
|
14
|
+
:compile_dir,
|
15
|
+
keyword_init: true
|
16
|
+
)
|
17
|
+
|
18
|
+
def initialize(
|
19
|
+
url_root_path: "/",
|
20
|
+
start_server: !Rails.env.production?,
|
21
|
+
ping_server: true,
|
22
|
+
server_host: "localhost",
|
23
|
+
server_path: "/",
|
24
|
+
**other_kwargs
|
25
|
+
)
|
26
|
+
@start_server = start_server
|
27
|
+
@server_host = server_host
|
28
|
+
@server_path = server_path
|
29
|
+
@ping_server = ping_server
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "rack-proxy"
|
2
|
+
|
3
|
+
require_relative "matches_request_to_static_site"
|
4
|
+
|
5
|
+
module StaticRails
|
6
|
+
class StaticMiddleware
|
7
|
+
def initialize(app)
|
8
|
+
@matches_request_to_static_site = MatchesRequestToStaticSite.new
|
9
|
+
@app = app
|
10
|
+
@file_handlers = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
return @app.call(env) unless StaticRails.config.serve_compiled_assets
|
15
|
+
req = Rack::Request.new env
|
16
|
+
|
17
|
+
if (req.get? || req.head?) && (site = @matches_request_to_static_site.call(req))
|
18
|
+
file_handler = file_handler_for(site)
|
19
|
+
path = req.path_info.gsub(/^#{site.url_root_path}/, "").chomp("/")
|
20
|
+
if (match = file_handler.match?(path))
|
21
|
+
req.path_info = match
|
22
|
+
return file_handler.serve(req)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@app.call(req.env)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# The same file handler used by Rails when serving up files from /public
|
32
|
+
# See: actionpack/lib/action_dispatch/middleware/static.rb
|
33
|
+
def file_handler_for(site)
|
34
|
+
@file_handlers[site] ||= ActionDispatch::FileHandler.new(
|
35
|
+
StaticRails.config.app.root.join(site.compile_dir).to_s
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module StaticRails
|
2
|
+
class WaitsForConnection
|
3
|
+
def call(site)
|
4
|
+
timeout = StaticRails.config.ping_server_timeout
|
5
|
+
start = Time.new
|
6
|
+
wait_message_logged = false
|
7
|
+
|
8
|
+
loop do
|
9
|
+
Socket.tcp(site.server_host, site.server_port, connect_timeout: 5)
|
10
|
+
break
|
11
|
+
rescue Errno::ECONNREFUSED
|
12
|
+
elapsed = Time.new - start
|
13
|
+
if elapsed > timeout
|
14
|
+
raise Error.new("Static site server \"#{site.name}\" failed to start within #{timeout} seconds. You can change the timeout with `StaticRails.config.ping_server_timeout = 42`")
|
15
|
+
else
|
16
|
+
unless wait_message_logged
|
17
|
+
Rails.logger.info "=> Static site server \"#{site.name}\" is not yet accepting connections on #{site.server_host}:#{site.server_port}. Will try to connect for #{timeout} more seconds"
|
18
|
+
wait_message_logged = true
|
19
|
+
end
|
20
|
+
sleep 0.3
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
$stdout.sync = true
|
2
|
+
|
3
|
+
def enhance_assets_precompile
|
4
|
+
Rake::Task["assets:precompile"].enhance([]) do
|
5
|
+
Rake::Task["static:compile"].invoke
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
namespace :static do
|
10
|
+
desc "Compile static sites configured with static-rails"
|
11
|
+
task compile: :environment do
|
12
|
+
StaticRails.compile
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
if Rake::Task.task_defined?("assets:precompile")
|
17
|
+
enhance_assets_precompile
|
18
|
+
else
|
19
|
+
Rake::Task.define_task("assets:precompile" => "static:compile")
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative "lib/static-rails/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "static-rails"
|
5
|
+
spec.version = StaticRails::VERSION
|
6
|
+
spec.authors = ["Justin Searls"]
|
7
|
+
spec.email = ["searls@gmail.com"]
|
8
|
+
|
9
|
+
spec.summary = "Build & serve static sites (e.g. Jekyll, Hugo) from your Rails app"
|
10
|
+
spec.homepage = "https://github.com/testdouble/static-rails"
|
11
|
+
spec.license = "MIT"
|
12
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
13
|
+
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
15
|
+
|
16
|
+
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
17
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|example)/}) }
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_dependency "railties", ">= 5.0.0"
|
24
|
+
spec.add_dependency "rack-proxy", "~> 0.6"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: static-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Searls
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: railties
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-proxy
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.6'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- searls@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".circleci/config.yml"
|
49
|
+
- ".gitignore"
|
50
|
+
- ".standard.yml"
|
51
|
+
- ".travis.yml"
|
52
|
+
- Gemfile
|
53
|
+
- Gemfile.lock
|
54
|
+
- LICENSE.txt
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- bin/console
|
58
|
+
- bin/setup
|
59
|
+
- lib/generators/static_rails/initializer_generator.rb
|
60
|
+
- lib/generators/templates/static.rb
|
61
|
+
- lib/static-rails.rb
|
62
|
+
- lib/static-rails/compile.rb
|
63
|
+
- lib/static-rails/configuration.rb
|
64
|
+
- lib/static-rails/error.rb
|
65
|
+
- lib/static-rails/matches_request_to_static_site.rb
|
66
|
+
- lib/static-rails/proxy_middleware.rb
|
67
|
+
- lib/static-rails/rack_server_check.rb
|
68
|
+
- lib/static-rails/railtie.rb
|
69
|
+
- lib/static-rails/server.rb
|
70
|
+
- lib/static-rails/server_store.rb
|
71
|
+
- lib/static-rails/site.rb
|
72
|
+
- lib/static-rails/static_middleware.rb
|
73
|
+
- lib/static-rails/version.rb
|
74
|
+
- lib/static-rails/waits_for_connection.rb
|
75
|
+
- lib/tasks/static-rails.rake
|
76
|
+
- static-rails.gemspec
|
77
|
+
homepage: https://github.com/testdouble/static-rails
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata:
|
81
|
+
homepage_uri: https://github.com/testdouble/static-rails
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 2.3.0
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubygems_version: 3.1.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Build & serve static sites (e.g. Jekyll, Hugo) from your Rails app
|
101
|
+
test_files: []
|