s3_website_monadic 0.0.24 → 0.0.25
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 +4 -4
- data/README.md +25 -63
- data/bin/s3_website_monadic +21 -4
- data/changelog.md +2 -0
- data/lib/s3_website/version.rb +1 -1
- data/s3_website.gemspec +1 -0
- data/src/main/scala/s3/website/Push.scala +10 -6
- data/src/main/scala/s3/website/S3.scala +1 -4
- data/src/test/scala/s3/website/S3WebsiteSpec.scala +220 -169
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da17db15b39a92b2f9f1deb27b63da2cb1bf97ac
|
4
|
+
data.tar.gz: 2cc1ba66a2d6ad0a7fccb559d544331d6d151747
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22968e31e4d71e18a41a7d32e853297f35323670f926097c8d5c6e7dec2cabcd75faf176ae60cd3a7efd3724bb0597caeadf9dda1f46a986c10d1c6ebb057705
|
7
|
+
data.tar.gz: 13400d48b4ea5ee5a5fa247535706c73ac419d9f4f553d85bdb0e4635b16bea9e9f5e14b2fa2c6a0c26838a0c7689103fa8b2af0c413e81a759acff629a9a73e
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/laurilehmijoki/s3_website)
|
4
4
|
[](http://badge.fury.io/rb/s3_website)
|
5
5
|
|
6
|
-
## What `
|
6
|
+
## What `s3_website_monadic` can do for you
|
7
7
|
|
8
8
|
* Create and configure an S3 website for you
|
9
9
|
* Upload your static website to AWS S3
|
@@ -15,10 +15,10 @@
|
|
15
15
|
|
16
16
|
## Install
|
17
17
|
|
18
|
-
gem install
|
18
|
+
gem install s3_website_monadic
|
19
19
|
|
20
|
-
`
|
21
|
-
<http://www.ruby-lang.org/en/downloads
|
20
|
+
`s3_website_monadic` requires Ruby and Java. Here is documentation on installing Ruby:
|
21
|
+
<http://www.ruby-lang.org/en/downloads/>, and here is documentation for Java:
|
22
22
|
<http://www.oracle.com/technetwork/java/javase/downloads/index.html>.
|
23
23
|
|
24
24
|
## Usage
|
@@ -28,11 +28,11 @@ Here's how you can get started:
|
|
28
28
|
* Create API credentials that have sufficient permissions to S3. More info
|
29
29
|
[here](https://github.com/laurilehmijoki/s3_website/blob/master/additional-docs/setting-up-aws-credentials.md).
|
30
30
|
* Go to your website directory
|
31
|
-
* Run `
|
31
|
+
* Run `s3_website_monadic cfg create`. This generates a configuration file called `s3_website.yml`.
|
32
32
|
* Put your AWS credentials and the S3 bucket name into the file
|
33
|
-
* Run `
|
33
|
+
* Run `s3_website_monadic cfg apply`. This will configure your bucket to function as an
|
34
34
|
S3 website. If the bucket does not exist, the command will create it for you.
|
35
|
-
* Run `
|
35
|
+
* Run `s3_website_monadic push` to push your website to S3. Congratulations! You are live.
|
36
36
|
|
37
37
|
**Important security note:** if the source code of your website is publicly
|
38
38
|
available, ensure that the `s3_website.yml` file is in the list of ignored files.
|
@@ -52,11 +52,11 @@ S3_website will automatically discover your website in the *public/output* direc
|
|
52
52
|
It's a good idea to store the `s3_website.yml` file in your project's root.
|
53
53
|
Let's say the contents you wish to upload to your S3 website bucket are in
|
54
54
|
*my_website_output*. You can upload the contents of that directory with
|
55
|
-
`
|
55
|
+
`s3_website_monadic push --site my_website_output`.
|
56
56
|
|
57
57
|
If you want to store the `s3_website.yml` file in a directory other than
|
58
58
|
the project's root you can specify the directory.
|
59
|
-
`
|
59
|
+
`s3_website_monadic push --config_dir config`.
|
60
60
|
|
61
61
|
### Using environment variables
|
62
62
|
|
@@ -87,8 +87,8 @@ you can omit the `s3_id` and `s3_secret` keys in the config file.)
|
|
87
87
|
* Be as fast as possible. Do in parallel all that can be done in parallel.
|
88
88
|
* Maintain 90% backward compatibility with the jekyll-s3 gem
|
89
89
|
|
90
|
-
`
|
91
|
-
understand and use. For example, `
|
90
|
+
`s3_website_monadic` attempts to be a command-line interface tool that is easy to
|
91
|
+
understand and use. For example, `s3_website_monadic --help` should print you all the
|
92
92
|
things it can perform. Please create an issue if you think the tool is
|
93
93
|
incomprehensible or inconsistent.
|
94
94
|
|
@@ -148,21 +148,9 @@ from the zopfli algorithm, which is 100% compatible with the traditional gzip
|
|
148
148
|
algorithm. A zopfli compression takes longer but results in about 5% smaller
|
149
149
|
files.
|
150
150
|
|
151
|
-
### Specifying a MIME type for files without extensions
|
152
|
-
|
153
|
-
`s3_website` will look up the MIME type of each file it uploads, and infer the Content-Type from it automatically. By default, files without an extension will have a blank Content-Type.
|
154
|
-
|
155
|
-
You can specify a default MIME type for files without an extension using a line like this in `s3_website.yml`:
|
156
|
-
|
157
|
-
```yaml
|
158
|
-
extensionless_mime_type: text/html
|
159
|
-
```
|
160
|
-
|
161
|
-
This is useful when you are uploading HTML files for which you want 'clean' URLs, e.g. `www.domain.com/info`.
|
162
|
-
|
163
151
|
### Using non-standard AWS regions
|
164
152
|
|
165
|
-
By default, `
|
153
|
+
By default, `s3_website_monadic` uses the US Standard Region. You can upload your
|
166
154
|
website to other regions by adding the setting `s3_endpoint` into the
|
167
155
|
`s3_website.yml` file.
|
168
156
|
|
@@ -195,7 +183,7 @@ ignore_on_server:
|
|
195
183
|
|
196
184
|
### Excluding files from upload
|
197
185
|
|
198
|
-
You can instruct `
|
186
|
+
You can instruct `s3_website_monadic` not to push certain files:
|
199
187
|
|
200
188
|
```yaml
|
201
189
|
exclude_from_upload: test
|
@@ -223,7 +211,7 @@ It is easy to deliver your S3-based web site via Cloudfront, the CDN of Amazon.
|
|
223
211
|
|
224
212
|
#### Creating a new CloudFront distribution
|
225
213
|
|
226
|
-
When you run the command `
|
214
|
+
When you run the command `s3_website_monadic cfg apply`, it will ask you whether you
|
227
215
|
want to deliver your website via CloudFront. If you answer yes, the command will
|
228
216
|
create a CloudFront distribution for you.
|
229
217
|
|
@@ -234,12 +222,12 @@ S3 bucket, just add the following line into the file `s3_website.yml`:
|
|
234
222
|
|
235
223
|
cloudfront_distribution_id: your-dist-id
|
236
224
|
|
237
|
-
Next time you run `
|
225
|
+
Next time you run `s3_website_monadic push`, it will invalidate the items on CloudFront and
|
238
226
|
thus force the CDN system to reload the changes from your website S3 bucket.
|
239
227
|
|
240
228
|
#### Specifying custom settings for your CloudFront distribution
|
241
229
|
|
242
|
-
`
|
230
|
+
`s3_website_monadic` lets you define custom settings for your CloudFront distribution.
|
243
231
|
|
244
232
|
For example, like this you can define a your own TTL and CNAME:
|
245
233
|
|
@@ -254,11 +242,11 @@ cloudfront_distribution_config:
|
|
254
242
|
```
|
255
243
|
|
256
244
|
Once you've saved the configuration into `s3_website.yml`, you can apply them by
|
257
|
-
running `
|
245
|
+
running `s3_website_monadic cfg apply`.
|
258
246
|
|
259
247
|
#### Invalidating root resources instead of index.htmls
|
260
248
|
|
261
|
-
By default, `
|
249
|
+
By default, `s3_website_monadic push` calls the CloudFront invalidation API with the
|
262
250
|
file-name-as-it-is. This means that if your file is *article/index.html*, the
|
263
251
|
push command will call the invalidation API on the resource
|
264
252
|
*article/index.html*.
|
@@ -288,7 +276,7 @@ Redirects method. Otherwise, use the Routing Rules method.
|
|
288
276
|
|
289
277
|
#### Simple Redirects
|
290
278
|
|
291
|
-
For simple redirects `
|
279
|
+
For simple redirects `s3_website_monadic` uses Amazon S3's
|
292
280
|
[`x-amz-website-redirect-location`](http://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html)
|
293
281
|
metadata. It will create zero-byte objects for each path you want
|
294
282
|
redirected with the appropriate `x-amz-website-redirect-location` value.
|
@@ -318,43 +306,17 @@ routing_rules:
|
|
318
306
|
http_redirect_code: 301
|
319
307
|
```
|
320
308
|
|
321
|
-
After adding the configuration, run the command `
|
309
|
+
After adding the configuration, run the command `s3_website_monadic cfg apply` on your
|
322
310
|
command-line interface. This will apply the routing rules on your S3 bucket.
|
323
311
|
|
324
312
|
For more information on configuring redirects, see the documentation of the
|
325
313
|
[configure-s3-website](https://github.com/laurilehmijoki/configure-s3-website#configuring-redirects)
|
326
|
-
gem, which comes as a transitive dependency of the `
|
327
|
-
command `
|
328
|
-
|
329
|
-
### Using `s3_website` as a library
|
330
|
-
|
331
|
-
By nature, `s3_website` is a command-line interface tool. You can, however, use
|
332
|
-
it programmatically by calling the same API as the executable `s3_website` does:
|
333
|
-
|
334
|
-
````ruby
|
335
|
-
require 's3_website'
|
336
|
-
is_headless = true
|
337
|
-
S3Website::Tasks.push('/website/root', '/path/to/your/website/_site/', is_headless)
|
338
|
-
````
|
339
|
-
|
340
|
-
You can also use a basic `Hash` instead of a `s3_website.yml` file:
|
341
|
-
|
342
|
-
```ruby
|
343
|
-
config = {
|
344
|
-
"s3_id" => YOUR_AWS_S3_ACCESS_KEY_ID,
|
345
|
-
"s3_secret" => YOUR_AWS_S3_SECRET_ACCESS_KEY,
|
346
|
-
"s3_bucket" => "your.blog.bucket.com"
|
347
|
-
}
|
348
|
-
in_headless = true
|
349
|
-
S3Website::Uploader.run('/path/to/your/website/_site/', config, in_headless)
|
350
|
-
```
|
351
|
-
|
352
|
-
The code above will assume that you have the `s3_website.yml` in the directory
|
353
|
-
`/path/to/your/website`.
|
314
|
+
gem, which comes as a transitive dependency of the `s3_website_monadic` gem. (The
|
315
|
+
command `s3_website_monadic cfg apply` internally calls the `configure-s3-website` gem.)
|
354
316
|
|
355
317
|
### Specifying custom concurrency level
|
356
318
|
|
357
|
-
By default, `
|
319
|
+
By default, `s3_website_monadic` does 3 operations in parallel. An operation can be an
|
358
320
|
HTTP PUT operation against the S3 API, for example.
|
359
321
|
|
360
322
|
You can increase the concurrency level by adding the following setting into the
|
@@ -386,10 +348,10 @@ Please create an issue and send a pull request if you spot any.
|
|
386
348
|
|
387
349
|
### Versioning
|
388
350
|
|
389
|
-
|
351
|
+
s3_website_monadic uses [Semantic Versioning](http://semver.org).
|
390
352
|
|
391
353
|
In the spirit of semantic versioning, here is the definition of public API for
|
392
|
-
s3_website: Within a major version,
|
354
|
+
s3_website: Within a major version, s3_website_monadic will not break
|
393
355
|
backwards-compatibility of anything that is mentioned in this README file.
|
394
356
|
|
395
357
|
### Tests
|
data/bin/s3_website_monadic
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require File.expand_path(File.dirname(__FILE__)+ '/../lib/s3_website')
|
4
|
+
require 'colored'
|
4
5
|
|
5
6
|
class Cfg < Thor
|
6
7
|
desc 'create', 'Create a config file with placeholder values'
|
@@ -75,7 +76,7 @@ def run_s3_website_jar(jar_file, call_dir)
|
|
75
76
|
site_path = File.expand_path(S3Website::Paths.infer_site_path options[:site], call_dir)
|
76
77
|
config_dir = File.expand_path options[:config_dir]
|
77
78
|
args = "--site=#{site_path} --config-dir=#{config_dir}"
|
78
|
-
|
79
|
+
debug_msg "Using #{jar_file}"
|
79
80
|
if system("java -cp #{jar_file} s3.website.Push #{args}")
|
80
81
|
exit 0
|
81
82
|
else
|
@@ -116,12 +117,12 @@ def download_jar(released_jar_lookup_paths)
|
|
116
117
|
File.writable? File.dirname(jar_path)
|
117
118
|
}.first
|
118
119
|
unless downloaded_jar
|
119
|
-
|
120
|
-
|
120
|
+
fail_msg "Neither #{released_jar_lookup_paths.join ' or '} is writable. Cannot download s3_website.jar."
|
121
|
+
fail_msg "Set either directory as writable to the current user and try again."
|
121
122
|
exit 1
|
122
123
|
end
|
123
124
|
download_url = "https://github.com/laurilehmijoki/s3_website/releases/download/#{tag_name}/s3_website.jar"
|
124
|
-
|
125
|
+
info_msg "Downloading #{download_url} into #{downloaded_jar}"
|
125
126
|
require 'open-uri'
|
126
127
|
open(downloaded_jar, 'wb') do |file|
|
127
128
|
file << open(download_url).read
|
@@ -129,4 +130,20 @@ def download_jar(released_jar_lookup_paths)
|
|
129
130
|
downloaded_jar
|
130
131
|
end
|
131
132
|
|
133
|
+
def debug_msg(msg)
|
134
|
+
print_msg 'debg'.cyan, msg
|
135
|
+
end
|
136
|
+
|
137
|
+
def info_msg(msg)
|
138
|
+
print_msg 'info'.blue, msg
|
139
|
+
end
|
140
|
+
|
141
|
+
def fail_msg(msg)
|
142
|
+
print_msg 'fail'.red, msg
|
143
|
+
end
|
144
|
+
|
145
|
+
def print_msg(prefix, msg)
|
146
|
+
puts "[#{prefix}] #{msg}"
|
147
|
+
end
|
148
|
+
|
132
149
|
Cli.start(ARGV)
|
data/changelog.md
CHANGED
@@ -23,6 +23,8 @@ for more info.
|
|
23
23
|
s3_website always deletes the files that are on the S3 bucket but not on the local file system.
|
24
24
|
Use the settings `ignore_on_server` and `exclude_from_upload` to control the retained files.
|
25
25
|
|
26
|
+
* You can no longer use this gem as a Ruby library
|
27
|
+
|
26
28
|
## 1.7.6
|
27
29
|
|
28
30
|
* Remove a test setting from *Gemfile*
|
data/lib/s3_website/version.rb
CHANGED
data/s3_website.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_dependency 'thor', '= 0.18.1'
|
21
21
|
s.add_dependency 'configure-s3-website', '= 1.5.5'
|
22
|
+
s.add_dependency 'colored', '1.2'
|
22
23
|
|
23
24
|
s.add_development_dependency 'rake', '10.1.1'
|
24
25
|
s.add_development_dependency 'octokit', '3.1.0'
|
@@ -186,19 +186,23 @@ object Push {
|
|
186
186
|
|
187
187
|
def main(args: Array[String]) {
|
188
188
|
val cliArgs = CliFactory.parseArguments(classOf[CliArgs], args:_*)
|
189
|
-
val
|
189
|
+
implicit val s3Settings = S3Settings()
|
190
|
+
implicit val cloudFrontSettings = CloudFrontSettings()
|
191
|
+
val errorOrPushStatus = push(siteInDirectory = cliArgs.site, withConfigDirectory = cliArgs.configDir)
|
192
|
+
errorOrPushStatus.left foreach (err => fail(s"Could not load the site: ${err.reportMessage}"))
|
193
|
+
System exit errorOrPushStatus.fold(_ => 1, pushStatus => pushStatus)
|
194
|
+
}
|
195
|
+
|
196
|
+
def push(siteInDirectory: String, withConfigDirectory: String)
|
197
|
+
(implicit s3Settings: S3Settings, cloudFrontSettings: CloudFrontSettings) =
|
198
|
+
loadSite(withConfigDirectory + "/s3_website.yml", siteInDirectory)
|
190
199
|
.right
|
191
200
|
.map {
|
192
201
|
implicit site =>
|
193
202
|
val threadPool = newFixedThreadPool(site.config.concurrency_level)
|
194
203
|
implicit val executor = fromExecutor(threadPool)
|
195
|
-
implicit val s3Settings = S3Settings()
|
196
|
-
implicit val cloudFrontSettings = CloudFrontSettings()
|
197
204
|
val pushStatus = pushSite
|
198
205
|
threadPool.shutdownNow()
|
199
206
|
pushStatus
|
200
207
|
}
|
201
|
-
errorOrPushStatus.left foreach (err => fail(s"Could not load the site: ${err.reportMessage}"))
|
202
|
-
System.exit(errorOrPushStatus.fold(_ => 1, pushStatus => pushStatus))
|
203
|
-
}
|
204
208
|
}
|
@@ -141,10 +141,7 @@ object S3 {
|
|
141
141
|
val metadata = putObjectRequest.getMetadata
|
142
142
|
def metadataReport =
|
143
143
|
(metadata.getCacheControl :: metadata.getContentType :: metadata.getContentEncoding :: putObjectRequest.getStorageClass :: Nil)
|
144
|
-
.
|
145
|
-
.collect {
|
146
|
-
case Some(metadatum) => metadatum
|
147
|
-
}
|
144
|
+
.filterNot(_ == null)
|
148
145
|
.mkString(" | ")
|
149
146
|
|
150
147
|
def reportMessage =
|
@@ -4,7 +4,6 @@ import org.specs2.mutable.{After, Specification}
|
|
4
4
|
import s3.website.model._
|
5
5
|
import org.specs2.specification.Scope
|
6
6
|
import org.apache.commons.io.FileUtils
|
7
|
-
import org.apache.commons.codec.digest.DigestUtils
|
8
7
|
import java.io.File
|
9
8
|
import scala.util.Random
|
10
9
|
import org.mockito.{Mockito, Matchers, ArgumentCaptor}
|
@@ -14,39 +13,34 @@ import com.amazonaws.services.s3.model._
|
|
14
13
|
import scala.concurrent.ExecutionContext.Implicits.global
|
15
14
|
import scala.concurrent.Await
|
16
15
|
import scala.concurrent.duration._
|
17
|
-
import s3.website.S3.
|
16
|
+
import s3.website.S3.S3Settings
|
18
17
|
import scala.collection.JavaConversions._
|
19
18
|
import s3.website.model.NewFile
|
20
|
-
import
|
21
|
-
import com.amazonaws.{AmazonClientException, AmazonServiceException}
|
19
|
+
import com.amazonaws.AmazonServiceException
|
22
20
|
import org.apache.commons.codec.digest.DigestUtils.md5Hex
|
23
|
-
import s3.website.CloudFront.
|
21
|
+
import s3.website.CloudFront.CloudFrontSettings
|
24
22
|
import com.amazonaws.services.cloudfront.AmazonCloudFront
|
25
23
|
import com.amazonaws.services.cloudfront.model.{CreateInvalidationResult, CreateInvalidationRequest, TooManyInvalidationsInProgressException}
|
26
24
|
import org.mockito.stubbing.Answer
|
27
25
|
import org.mockito.invocation.InvocationOnMock
|
28
|
-
import com.amazonaws.AmazonServiceException.ErrorType
|
29
26
|
import java.util.concurrent.atomic.AtomicInteger
|
30
|
-
import
|
27
|
+
import org.apache.commons.io.FileUtils.write
|
28
|
+
import scala.collection.mutable
|
31
29
|
|
32
30
|
class S3WebsiteSpec extends Specification {
|
33
31
|
|
34
32
|
"gzip: true" should {
|
35
|
-
"update a gzipped S3 object if the contents has changed" in new
|
36
|
-
|
37
|
-
|
38
|
-
localFilesWithContent = ("styles.css", "<h1>hi again</h1>") :: Nil
|
39
|
-
)
|
33
|
+
"update a gzipped S3 object if the contents has changed" in new EmptySite with MockAWS {
|
34
|
+
config = "gzip: true"
|
35
|
+
setLocalFileWithContent(("styles.css", "<h1>hi again</h1>"))
|
40
36
|
setS3Files(S3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
|
41
37
|
Push.pushSite
|
42
38
|
sentPutObjectRequest.getKey must equalTo("styles.css")
|
43
39
|
}
|
44
40
|
|
45
|
-
"not update a gzipped S3 object if the contents has not changed" in new
|
46
|
-
|
47
|
-
|
48
|
-
localFilesWithContent = ("styles.css", "<h1>hi</h1>") :: Nil
|
49
|
-
)
|
41
|
+
"not update a gzipped S3 object if the contents has not changed" in new EmptySite with MockAWS {
|
42
|
+
config = "gzip: true"
|
43
|
+
setLocalFileWithContent(("styles.css", "<h1>hi</h1>"))
|
50
44
|
setS3Files(S3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
|
51
45
|
Push.pushSite
|
52
46
|
noUploadsOccurred must beTrue
|
@@ -57,55 +51,54 @@ class S3WebsiteSpec extends Specification {
|
|
57
51
|
gzip:
|
58
52
|
- .xml
|
59
53
|
""" should {
|
60
|
-
"update a gzipped S3 object if the contents has changed" in new
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
"update a gzipped S3 object if the contents has changed" in new EmptySite with MockAWS {
|
55
|
+
config = """
|
56
|
+
|gzip:
|
57
|
+
| - .xml
|
58
|
+
""".stripMargin
|
59
|
+
setLocalFileWithContent(("file.xml", "<h1>hi again</h1>"))
|
65
60
|
setS3Files(S3File("file.xml", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
|
66
61
|
Push.pushSite
|
67
62
|
sentPutObjectRequest.getKey must equalTo("file.xml")
|
68
63
|
}
|
69
64
|
}
|
70
65
|
|
71
|
-
|
72
66
|
"push" should {
|
73
|
-
"not upload a file if it has not changed" in new
|
74
|
-
|
67
|
+
"not upload a file if it has not changed" in new EmptySite with MockAWS {
|
68
|
+
setLocalFileWithContent(("index.html", "<div>hello</div>"))
|
75
69
|
setS3Files(S3File("index.html", md5Hex("<div>hello</div>")))
|
76
70
|
Push.pushSite
|
77
71
|
noUploadsOccurred must beTrue
|
78
72
|
}
|
79
73
|
|
80
|
-
"update a file if it has changed" in new
|
81
|
-
|
74
|
+
"update a file if it has changed" in new EmptySite with MockAWS {
|
75
|
+
setLocalFileWithContent(("index.html", "<h1>old text</h1>"))
|
82
76
|
setS3Files(S3File("index.html", md5Hex("<h1>new text</h1>")))
|
83
77
|
Push.pushSite
|
84
78
|
sentPutObjectRequest.getKey must equalTo("index.html")
|
85
79
|
}
|
86
80
|
|
87
|
-
"create a file if does not exist on S3" in new
|
88
|
-
|
81
|
+
"create a file if does not exist on S3" in new EmptySite with MockAWS {
|
82
|
+
setLocalFile("index.html")
|
89
83
|
Push.pushSite
|
90
84
|
sentPutObjectRequest.getKey must equalTo("index.html")
|
91
85
|
}
|
92
86
|
|
93
|
-
"delete files that are on S3 but not on local file system" in new
|
94
|
-
implicit val site = buildSite()
|
87
|
+
"delete files that are on S3 but not on local file system" in new EmptySite with MockAWS {
|
95
88
|
setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
|
96
89
|
Push.pushSite
|
97
90
|
sentDelete must equalTo("old.html")
|
98
91
|
}
|
99
92
|
|
100
|
-
"try again if the upload fails" in new
|
101
|
-
|
93
|
+
"try again if the upload fails" in new EmptySite with MockAWS {
|
94
|
+
setLocalFile("index.html")
|
102
95
|
uploadFailsAndThenSucceeds(howManyFailures = 5)
|
103
96
|
Push.pushSite
|
104
97
|
verify(amazonS3Client, times(6)).putObject(Matchers.any(classOf[PutObjectRequest]))
|
105
98
|
}
|
106
99
|
|
107
|
-
"not try again if the upload fails on because of invalid credentials" in new
|
108
|
-
|
100
|
+
"not try again if the upload fails on because of invalid credentials" in new EmptySite with MockAWS {
|
101
|
+
setLocalFile("index.html")
|
109
102
|
when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow {
|
110
103
|
val e = new AmazonServiceException("your credentials are incorrect")
|
111
104
|
e.setStatusCode(403)
|
@@ -115,16 +108,14 @@ class S3WebsiteSpec extends Specification {
|
|
115
108
|
verify(amazonS3Client, times(1)).putObject(Matchers.any(classOf[PutObjectRequest]))
|
116
109
|
}
|
117
110
|
|
118
|
-
"try again if the delete fails" in new
|
119
|
-
implicit val site = buildSite()
|
111
|
+
"try again if the delete fails" in new EmptySite with MockAWS {
|
120
112
|
setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
|
121
113
|
deleteFailsAndThenSucceeds(howManyFailures = 5)
|
122
114
|
Push.pushSite
|
123
115
|
verify(amazonS3Client, times(6)).deleteObject(Matchers.anyString(), Matchers.anyString())
|
124
116
|
}
|
125
117
|
|
126
|
-
"try again if the object listing fails" in new
|
127
|
-
implicit val site = buildSite()
|
118
|
+
"try again if the object listing fails" in new EmptySite with MockAWS {
|
128
119
|
setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
|
129
120
|
objectListingFailsAndThenSucceeds(howManyFailures = 5)
|
130
121
|
Push.pushSite
|
@@ -133,60 +124,52 @@ class S3WebsiteSpec extends Specification {
|
|
133
124
|
}
|
134
125
|
|
135
126
|
"push with CloudFront" should {
|
136
|
-
"invalidate the updated CloudFront items" in new
|
137
|
-
|
138
|
-
|
139
|
-
localFiles = "css/test.css" :: "articles/index.html" :: Nil
|
140
|
-
)
|
127
|
+
"invalidate the updated CloudFront items" in new EmptySite with MockAWS {
|
128
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
129
|
+
setLocalFiles("css/test.css", "articles/index.html")
|
141
130
|
setOutdatedS3Keys("css/test.css", "articles/index.html")
|
142
131
|
Push.pushSite
|
143
132
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/css/test.css" :: "/articles/index.html" :: Nil).sorted)
|
144
133
|
}
|
145
134
|
|
146
|
-
"not send CloudFront invalidation requests on new objects" in new
|
147
|
-
|
148
|
-
|
149
|
-
localFiles = "newfile.js" :: Nil
|
150
|
-
)
|
135
|
+
"not send CloudFront invalidation requests on new objects" in new EmptySite with MockAWS {
|
136
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
137
|
+
setLocalFile("newfile.js")
|
151
138
|
Push.pushSite
|
152
139
|
noInvalidationsOccurred must beTrue
|
153
140
|
}
|
154
141
|
|
155
|
-
"not send CloudFront invalidation requests on redirect objects" in new
|
156
|
-
|
157
|
-
|
158
|
-
|
142
|
+
"not send CloudFront invalidation requests on redirect objects" in new EmptySite with MockAWS {
|
143
|
+
config = """
|
144
|
+
|cloudfront_distribution_id: EGM1J2JJX9Z
|
145
|
+
|redirects:
|
146
|
+
| /index.php: index.html
|
147
|
+
""".stripMargin
|
159
148
|
Push.pushSite
|
160
149
|
noInvalidationsOccurred must beTrue
|
161
150
|
}
|
162
151
|
|
163
|
-
"retry CloudFront responds with TooManyInvalidationsInProgressException" in new
|
152
|
+
"retry CloudFront responds with TooManyInvalidationsInProgressException" in new EmptySite with MockAWS {
|
164
153
|
setTooManyInvalidationsInProgress(4)
|
165
|
-
|
166
|
-
|
167
|
-
localFiles = "test.css" :: Nil
|
168
|
-
)
|
154
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
155
|
+
setLocalFile("test.css")
|
169
156
|
setOutdatedS3Keys("test.css")
|
170
157
|
Push.pushSite must equalTo(0) // The retries should finally result in a success
|
171
158
|
sentInvalidationRequests.length must equalTo(4)
|
172
159
|
}
|
173
160
|
|
174
|
-
"retry if CloudFront is temporarily unreachable" in new
|
161
|
+
"retry if CloudFront is temporarily unreachable" in new EmptySite with MockAWS {
|
175
162
|
invalidationsFailAndThenSucceed(5)
|
176
|
-
|
177
|
-
|
178
|
-
localFiles = "test.css" :: Nil
|
179
|
-
)
|
163
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
164
|
+
setLocalFile("test.css")
|
180
165
|
setOutdatedS3Keys("test.css")
|
181
166
|
Push.pushSite
|
182
167
|
sentInvalidationRequests.length must equalTo(6)
|
183
168
|
}
|
184
169
|
|
185
|
-
"encode unsafe characters in the keys" in new
|
186
|
-
|
187
|
-
|
188
|
-
localFiles = "articles/arnold's file.html" :: Nil
|
189
|
-
)
|
170
|
+
"encode unsafe characters in the keys" in new EmptySite with MockAWS {
|
171
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
172
|
+
setLocalFile("articles/arnold's file.html")
|
190
173
|
setOutdatedS3Keys("articles/arnold's file.html")
|
191
174
|
Push.pushSite
|
192
175
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/arnold's%20file.html" :: Nil).sorted)
|
@@ -196,11 +179,9 @@ class S3WebsiteSpec extends Specification {
|
|
196
179
|
* Because CloudFront supports Default Root Objects (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html),
|
197
180
|
* we have to guess
|
198
181
|
*/
|
199
|
-
"invalidate the root object '/' if a top-level object is updated or deleted" in new
|
200
|
-
|
201
|
-
|
202
|
-
localFiles = "maybe-index.html" :: Nil
|
203
|
-
)
|
182
|
+
"invalidate the root object '/' if a top-level object is updated or deleted" in new EmptySite with MockAWS {
|
183
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
184
|
+
setLocalFile("maybe-index.html")
|
204
185
|
setOutdatedS3Keys("maybe-index.html")
|
205
186
|
Push.pushSite
|
206
187
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/" :: "/maybe-index.html" :: Nil).sorted)
|
@@ -208,11 +189,12 @@ class S3WebsiteSpec extends Specification {
|
|
208
189
|
}
|
209
190
|
|
210
191
|
"cloudfront_invalidate_root: true" should {
|
211
|
-
"convert CloudFront invalidation paths with the '/index.html' suffix into '/'" in new
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
192
|
+
"convert CloudFront invalidation paths with the '/index.html' suffix into '/'" in new EmptySite with MockAWS {
|
193
|
+
config = """
|
194
|
+
|cloudfront_distribution_id: EGM1J2JJX9Z
|
195
|
+
|cloudfront_invalidate_root: true
|
196
|
+
""".stripMargin
|
197
|
+
setLocalFile("articles/index.html")
|
216
198
|
setOutdatedS3Keys("articles/index.html")
|
217
199
|
Push.pushSite
|
218
200
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/" :: Nil).sorted)
|
@@ -220,12 +202,10 @@ class S3WebsiteSpec extends Specification {
|
|
220
202
|
}
|
221
203
|
|
222
204
|
"a site with over 1000 items" should {
|
223
|
-
"split the CloudFront invalidation requests into batches of 1000 items" in new
|
205
|
+
"split the CloudFront invalidation requests into batches of 1000 items" in new EmptySite with MockAWS {
|
224
206
|
val files = (1 to 1002).map { i => s"lots-of-files/file-$i"}
|
225
|
-
|
226
|
-
|
227
|
-
localFiles = files
|
228
|
-
)
|
207
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
208
|
+
setLocalFiles(files:_*)
|
229
209
|
setOutdatedS3Keys(files:_*)
|
230
210
|
Push.pushSite
|
231
211
|
sentInvalidationRequests.length must equalTo(2)
|
@@ -235,55 +215,54 @@ class S3WebsiteSpec extends Specification {
|
|
235
215
|
}
|
236
216
|
|
237
217
|
"push exit status" should {
|
238
|
-
"be 0 all uploads succeed" in new
|
239
|
-
|
218
|
+
"be 0 all uploads succeed" in new EmptySite with MockAWS {
|
219
|
+
setLocalFiles("file.txt")
|
240
220
|
Push.pushSite must equalTo(0)
|
241
221
|
}
|
242
222
|
|
243
|
-
"be 1 if any of the uploads fails" in new
|
244
|
-
|
223
|
+
"be 1 if any of the uploads fails" in new EmptySite with MockAWS {
|
224
|
+
setLocalFiles("file.txt")
|
245
225
|
when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow(new AmazonServiceException("AWS failed"))
|
246
226
|
Push.pushSite must equalTo(1)
|
247
227
|
}
|
248
228
|
|
249
|
-
"be 1 if any of the redirects fails" in new
|
250
|
-
|
229
|
+
"be 1 if any of the redirects fails" in new EmptySite with MockAWS {
|
230
|
+
config = """
|
231
|
+
|redirects:
|
232
|
+
| index.php: /index.html
|
233
|
+
""".stripMargin
|
251
234
|
when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow(new AmazonServiceException("AWS failed"))
|
252
235
|
Push.pushSite must equalTo(1)
|
253
236
|
}
|
254
237
|
|
255
|
-
"be 0 if CloudFront invalidations and uploads succeed"in new
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
)
|
238
|
+
"be 0 if CloudFront invalidations and uploads succeed"in new EmptySite with MockAWS {
|
239
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
240
|
+
setLocalFile("test.css")
|
241
|
+
setOutdatedS3Keys("test.css")
|
260
242
|
Push.pushSite must equalTo(0)
|
261
243
|
}
|
262
244
|
|
263
|
-
"be 1 if CloudFront is unreachable or broken"in new
|
245
|
+
"be 1 if CloudFront is unreachable or broken"in new EmptySite with MockAWS {
|
264
246
|
setCloudFrontAsInternallyBroken()
|
265
|
-
|
266
|
-
|
267
|
-
localFiles = "test.css" :: Nil
|
268
|
-
)
|
247
|
+
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
248
|
+
setLocalFile("test.css")
|
269
249
|
setOutdatedS3Keys("test.css")
|
270
250
|
Push.pushSite must equalTo(1)
|
271
251
|
}
|
272
252
|
|
273
|
-
"be 0 if upload retry succeeds" in new
|
274
|
-
|
253
|
+
"be 0 if upload retry succeeds" in new EmptySite with MockAWS {
|
254
|
+
setLocalFile("index.html")
|
275
255
|
uploadFailsAndThenSucceeds(howManyFailures = 1)
|
276
256
|
Push.pushSite must equalTo(0)
|
277
257
|
}
|
278
258
|
|
279
|
-
"be 1 if delete retry fails" in new
|
280
|
-
|
259
|
+
"be 1 if delete retry fails" in new EmptySite with MockAWS {
|
260
|
+
setLocalFile("index.html")
|
281
261
|
uploadFailsAndThenSucceeds(howManyFailures = 6)
|
282
262
|
Push.pushSite must equalTo(1)
|
283
263
|
}
|
284
264
|
|
285
|
-
"be 1 if an object listing fails" in new
|
286
|
-
implicit val site = buildSite()
|
265
|
+
"be 1 if an object listing fails" in new EmptySite with MockAWS {
|
287
266
|
setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
|
288
267
|
objectListingFailsAndThenSucceeds(howManyFailures = 6)
|
289
268
|
Push.pushSite must equalTo(1)
|
@@ -291,19 +270,17 @@ class S3WebsiteSpec extends Specification {
|
|
291
270
|
}
|
292
271
|
|
293
272
|
"s3_website.yml file" should {
|
294
|
-
"never be uploaded" in new
|
295
|
-
|
273
|
+
"never be uploaded" in new EmptySite with MockAWS {
|
274
|
+
setLocalFile("s3_website.yml")
|
296
275
|
Push.pushSite
|
297
276
|
noUploadsOccurred must beTrue
|
298
277
|
}
|
299
278
|
}
|
300
279
|
|
301
280
|
"exclude_from_upload: string" should {
|
302
|
-
"result in matching files not being uploaded" in new
|
303
|
-
|
304
|
-
|
305
|
-
localFiles = ".DS_Store" :: Nil
|
306
|
-
)
|
281
|
+
"result in matching files not being uploaded" in new EmptySite with MockAWS {
|
282
|
+
config = "exclude_from_upload: .DS_.*?"
|
283
|
+
setLocalFile(".DS_Store")
|
307
284
|
Push.pushSite
|
308
285
|
noUploadsOccurred must beTrue
|
309
286
|
}
|
@@ -314,19 +291,21 @@ class S3WebsiteSpec extends Specification {
|
|
314
291
|
- regex
|
315
292
|
- another_exclusion
|
316
293
|
""" should {
|
317
|
-
"result in matching files not being uploaded" in new
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
294
|
+
"result in matching files not being uploaded" in new EmptySite with MockAWS {
|
295
|
+
config = """
|
296
|
+
|exclude_from_upload:
|
297
|
+
| - .DS_.*?
|
298
|
+
| - logs
|
299
|
+
""".stripMargin
|
300
|
+
setLocalFiles(".DS_Store", "logs/test.log")
|
322
301
|
Push.pushSite
|
323
302
|
noUploadsOccurred must beTrue
|
324
303
|
}
|
325
304
|
}
|
326
305
|
|
327
306
|
"ignore_on_server: value" should {
|
328
|
-
"not delete the S3 objects that match the ignore value" in new
|
329
|
-
|
307
|
+
"not delete the S3 objects that match the ignore value" in new EmptySite with MockAWS {
|
308
|
+
config = "ignore_on_server: logs"
|
330
309
|
setS3Files(S3File("logs/log.txt", ""))
|
331
310
|
Push.pushSite
|
332
311
|
noDeletesOccurred must beTrue
|
@@ -338,8 +317,11 @@ class S3WebsiteSpec extends Specification {
|
|
338
317
|
- regex
|
339
318
|
- another_ignore
|
340
319
|
""" should {
|
341
|
-
"not delete the S3 objects that match the ignore value" in new
|
342
|
-
|
320
|
+
"not delete the S3 objects that match the ignore value" in new EmptySite with MockAWS {
|
321
|
+
config = """
|
322
|
+
|ignore_on_server:
|
323
|
+
| - .*txt
|
324
|
+
""".stripMargin
|
343
325
|
setS3Files(S3File("logs/log.txt", ""))
|
344
326
|
Push.pushSite
|
345
327
|
noDeletesOccurred must beTrue
|
@@ -347,46 +329,59 @@ class S3WebsiteSpec extends Specification {
|
|
347
329
|
}
|
348
330
|
|
349
331
|
"max-age in config" can {
|
350
|
-
"be applied to all files" in new
|
351
|
-
|
332
|
+
"be applied to all files" in new EmptySite with MockAWS {
|
333
|
+
config = "max_age: 60"
|
334
|
+
setLocalFile("index.html")
|
352
335
|
Push.pushSite
|
353
336
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=60")
|
354
337
|
}
|
355
338
|
|
356
|
-
"be applied to files that match the glob" in new
|
357
|
-
|
339
|
+
"be applied to files that match the glob" in new EmptySite with MockAWS {
|
340
|
+
config = """
|
341
|
+
|max_age:
|
342
|
+
| "*.html": 90
|
343
|
+
""".stripMargin
|
344
|
+
setLocalFile("index.html")
|
358
345
|
Push.pushSite
|
359
346
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
|
360
347
|
}
|
361
348
|
|
362
|
-
"be applied to directories that match the glob" in new
|
363
|
-
|
349
|
+
"be applied to directories that match the glob" in new EmptySite with MockAWS {
|
350
|
+
config = """
|
351
|
+
|max_age:
|
352
|
+
| "assets/**/*.js": 90
|
353
|
+
""".stripMargin
|
354
|
+
setLocalFile("assets/lib/jquery.js")
|
364
355
|
Push.pushSite
|
365
356
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
|
366
357
|
}
|
367
358
|
|
368
|
-
"not be applied if the glob doesn't match" in new
|
369
|
-
|
359
|
+
"not be applied if the glob doesn't match" in new EmptySite with MockAWS {
|
360
|
+
config = """
|
361
|
+
|max_age:
|
362
|
+
| "*.js": 90
|
363
|
+
""".stripMargin
|
364
|
+
setLocalFile("index.html")
|
370
365
|
Push.pushSite
|
371
366
|
sentPutObjectRequest.getMetadata.getCacheControl must beNull
|
372
367
|
}
|
373
368
|
|
374
|
-
"be used to disable caching" in new
|
375
|
-
|
369
|
+
"be used to disable caching" in new EmptySite with MockAWS {
|
370
|
+
config = "max_age: 0"
|
371
|
+
setLocalFile("index.html")
|
376
372
|
Push.pushSite
|
377
373
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("no-cache; max-age=0")
|
378
374
|
}
|
379
375
|
}
|
380
376
|
|
381
377
|
"max-age in config" should {
|
382
|
-
"respect the more specific glob" in new
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
)
|
378
|
+
"respect the more specific glob" in new EmptySite with MockAWS {
|
379
|
+
config = """
|
380
|
+
|max_age:
|
381
|
+
| "assets/*": 150
|
382
|
+
| "assets/*.gif": 86400
|
383
|
+
""".stripMargin
|
384
|
+
setLocalFiles("assets/jquery.js", "assets/picture.gif")
|
390
385
|
Push.pushSite
|
391
386
|
sentPutObjectRequests.find(_.getKey == "assets/jquery.js").get.getMetadata.getCacheControl must equalTo("max-age=150")
|
392
387
|
sentPutObjectRequests.find(_.getKey == "assets/picture.gif").get.getMetadata.getCacheControl must equalTo("max-age=86400")
|
@@ -394,41 +389,50 @@ class S3WebsiteSpec extends Specification {
|
|
394
389
|
}
|
395
390
|
|
396
391
|
"s3_reduced_redundancy: true in config" should {
|
397
|
-
"result in uploads being marked with reduced redundancy" in new
|
398
|
-
|
392
|
+
"result in uploads being marked with reduced redundancy" in new EmptySite with MockAWS {
|
393
|
+
config = "s3_reduced_redundancy: true"
|
394
|
+
setLocalFile("file.exe")
|
399
395
|
Push.pushSite
|
400
396
|
sentPutObjectRequest.getStorageClass must equalTo("REDUCED_REDUNDANCY")
|
401
397
|
}
|
402
398
|
}
|
403
399
|
|
404
400
|
"s3_reduced_redundancy: false in config" should {
|
405
|
-
"result in uploads being marked with the default storage class" in new
|
406
|
-
|
401
|
+
"result in uploads being marked with the default storage class" in new EmptySite with MockAWS {
|
402
|
+
config = "s3_reduced_redundancy: false"
|
403
|
+
setLocalFile("file.exe")
|
407
404
|
Push.pushSite
|
408
405
|
sentPutObjectRequest.getStorageClass must beNull
|
409
406
|
}
|
410
407
|
}
|
411
408
|
|
412
409
|
"redirect in config" should {
|
413
|
-
"result in a redirect instruction that is sent to AWS" in new
|
414
|
-
|
410
|
+
"result in a redirect instruction that is sent to AWS" in new EmptySite with MockAWS {
|
411
|
+
config = """
|
412
|
+
|redirects:
|
413
|
+
| index.php: /index.html
|
414
|
+
""".stripMargin
|
415
415
|
Push.pushSite
|
416
416
|
sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
|
417
417
|
}
|
418
418
|
|
419
|
-
"result in max-age=0 Cache-Control header on the object" in new
|
420
|
-
|
419
|
+
"result in max-age=0 Cache-Control header on the object" in new EmptySite with MockAWS {
|
420
|
+
config = """
|
421
|
+
|redirects:
|
422
|
+
| index.php: /index.html
|
423
|
+
""".stripMargin
|
421
424
|
Push.pushSite
|
422
425
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=0, no-cache")
|
423
426
|
}
|
424
427
|
}
|
425
428
|
|
426
429
|
"redirect in config and an object on the S3 bucket" should {
|
427
|
-
"not result in the S3 object being deleted" in new
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
430
|
+
"not result in the S3 object being deleted" in new EmptySite with MockAWS {
|
431
|
+
config = """
|
432
|
+
|redirects:
|
433
|
+
| index.php: /index.html
|
434
|
+
""".stripMargin
|
435
|
+
setLocalFile("index.php")
|
432
436
|
setS3Files(S3File("index.php", "md5"))
|
433
437
|
Push.pushSite
|
434
438
|
noDeletesOccurred must beTrue
|
@@ -436,38 +440,50 @@ class S3WebsiteSpec extends Specification {
|
|
436
440
|
}
|
437
441
|
|
438
442
|
"dotfiles" should {
|
439
|
-
"be included in the pushed files" in new
|
440
|
-
|
443
|
+
"be included in the pushed files" in new EmptySite with MockAWS {
|
444
|
+
setLocalFile(".vimrc")
|
441
445
|
Push.pushSite
|
442
446
|
sentPutObjectRequest.getKey must equalTo(".vimrc")
|
443
447
|
}
|
444
448
|
}
|
445
449
|
|
446
450
|
"content type inference" should {
|
447
|
-
"add charset=utf-8 to all html documents" in new
|
448
|
-
|
451
|
+
"add charset=utf-8 to all html documents" in new EmptySite with MockAWS {
|
452
|
+
setLocalFile("index.html")
|
449
453
|
Push.pushSite
|
450
454
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("text/html; charset=utf-8")
|
451
455
|
}
|
452
456
|
|
453
|
-
"add charset=utf-8 to all text documents" in new
|
454
|
-
|
457
|
+
"add charset=utf-8 to all text documents" in new EmptySite with MockAWS {
|
458
|
+
setLocalFile("index.txt")
|
455
459
|
Push.pushSite
|
456
460
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("text/plain; charset=utf-8")
|
457
461
|
}
|
458
462
|
|
459
|
-
"add charset=utf-8 to all json documents" in new
|
460
|
-
|
463
|
+
"add charset=utf-8 to all json documents" in new EmptySite with MockAWS {
|
464
|
+
setLocalFile("data.json")
|
461
465
|
Push.pushSite
|
462
466
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("application/json; charset=utf-8")
|
463
467
|
}
|
464
468
|
|
465
|
-
"resolve the content type from file contents" in new
|
466
|
-
|
469
|
+
"resolve the content type from file contents" in new EmptySite with MockAWS {
|
470
|
+
setLocalFileWithContent(("index", "<html><body><h1>hi</h1></body></html>"))
|
467
471
|
Push.pushSite
|
468
472
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("text/html; charset=utf-8")
|
469
473
|
}
|
470
474
|
}
|
475
|
+
|
476
|
+
"ERB in config file" should {
|
477
|
+
"be evaluated" in new EmptySite with MockAWS {
|
478
|
+
config = """
|
479
|
+
|redirects:
|
480
|
+
|<%= ('a'..'f').to_a.map do |t| ' '+t+ ': /'+t+'.html' end.join('\n')%>
|
481
|
+
""".stripMargin
|
482
|
+
Push.pushSite
|
483
|
+
sentPutObjectRequests.length must equalTo(6)
|
484
|
+
sentPutObjectRequests.forall(_.getRedirectLocation != null) must beTrue
|
485
|
+
}
|
486
|
+
}
|
471
487
|
|
472
488
|
trait MockAWS extends MockS3 with MockCloudFront with Scope
|
473
489
|
|
@@ -620,22 +636,57 @@ class S3WebsiteSpec extends Specification {
|
|
620
636
|
def after {
|
621
637
|
FileUtils.forceDelete(siteDir)
|
622
638
|
}
|
639
|
+
}
|
640
|
+
|
641
|
+
trait EmptySite extends SiteDirectory {
|
642
|
+
type LocalFileWithContent = (String, String)
|
643
|
+
|
644
|
+
val localFilesWithContent: mutable.Buffer[LocalFileWithContent] = mutable.Buffer()
|
645
|
+
def setLocalFile(fileName: String) = setLocalFileWithContent((fileName, ""))
|
646
|
+
def setLocalFiles(fileNames: String*) = fileNames foreach setLocalFile
|
647
|
+
def setLocalFileWithContent(fileNameAndContent: LocalFileWithContent) = localFilesWithContent += fileNameAndContent
|
648
|
+
def setLocalFilesWithContent(fileNamesAndContent: LocalFileWithContent*) = fileNamesAndContent foreach setLocalFileWithContent
|
649
|
+
var config = ""
|
650
|
+
var baseConfig =
|
651
|
+
"""
|
652
|
+
|s3_id: foo
|
653
|
+
|s3_secret: bar
|
654
|
+
|s3_bucket: bucket
|
655
|
+
""".stripMargin
|
656
|
+
|
657
|
+
implicit def site2: Site = siteWithFilesAndContent(config, localFilesWithContent)
|
658
|
+
|
659
|
+
def buildSite(
|
660
|
+
config: String = "",
|
661
|
+
baseConfig: String =
|
662
|
+
"""
|
663
|
+
|s3_id: foo
|
664
|
+
|s3_secret: bar
|
665
|
+
|s3_bucket: bucket
|
666
|
+
""".stripMargin
|
667
|
+
): Site = {
|
668
|
+
val configFile = new File(siteDir, "s3_website.yml")
|
669
|
+
write(configFile,
|
670
|
+
s"""
|
671
|
+
|$baseConfig
|
672
|
+
|$config
|
673
|
+
""".stripMargin
|
674
|
+
)
|
675
|
+
val errorOrSite: Either[ErrorReport, Site] = Site.loadSite(configFile.getAbsolutePath, siteDir.getAbsolutePath)
|
676
|
+
errorOrSite.left.foreach (error => throw new RuntimeException(error.reportMessage))
|
677
|
+
errorOrSite.right.get
|
678
|
+
}
|
623
679
|
|
624
|
-
def
|
625
|
-
|
626
|
-
def siteWithFilesAndContent(config: Config = defaultConfig, localFilesWithContent: Seq[(String, String)]): Site = {
|
680
|
+
def siteWithFilesAndContent(config: String = "", localFilesWithContent: Seq[LocalFileWithContent]): Site = {
|
627
681
|
localFilesWithContent.foreach {
|
628
682
|
case (filePath, content) =>
|
629
683
|
val file = new File(siteDir, filePath)
|
630
684
|
FileUtils.forceMkdir(file.getParentFile)
|
631
685
|
file.createNewFile()
|
632
|
-
|
686
|
+
write(file, content)
|
633
687
|
}
|
634
688
|
buildSite(config)
|
635
689
|
}
|
636
|
-
|
637
|
-
def siteWithFiles(config: Config = defaultConfig, localFiles: Seq[String]): Site =
|
638
|
-
siteWithFilesAndContent(config, localFilesWithContent = localFiles.map((_, "file contents")))
|
639
690
|
}
|
640
691
|
|
641
692
|
val defaultConfig = Config(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: s3_website_monadic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.25
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lauri Lehmijoki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.5.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colored
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.2'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|