s3_website_monadic 0.0.24 → 0.0.25

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae8555de8425a14c5d8deb7d0dfebea2c7f39fb2
4
- data.tar.gz: b6b4da900adcb7afb5422e74ffad3573bf272aa1
3
+ metadata.gz: da17db15b39a92b2f9f1deb27b63da2cb1bf97ac
4
+ data.tar.gz: 2cc1ba66a2d6ad0a7fccb559d544331d6d151747
5
5
  SHA512:
6
- metadata.gz: fe00d27cab7b1a4462221005eb771177485b4e341eb69844768069e1dc86319b25a05bfef076b2dd9192261ebd8d2bc85d891f7791c24d4ef326c8c7f895eacb
7
- data.tar.gz: 11363a4e8640319cc8a4a68c8c008346eadf1a3a6397e7e8a4d954202cd1ec784ddba3c9c90c76e53e00bd1c325780d11bdbf7dfcadcf41011d34768c954d9ed
6
+ metadata.gz: 22968e31e4d71e18a41a7d32e853297f35323670f926097c8d5c6e7dec2cabcd75faf176ae60cd3a7efd3724bb0597caeadf9dda1f46a986c10d1c6ebb057705
7
+ data.tar.gz: 13400d48b4ea5ee5a5fa247535706c73ac419d9f4f553d85bdb0e4635b16bea9e9f5e14b2fa2c6a0c26838a0c7689103fa8b2af0c413e81a759acff629a9a73e
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build Status](https://travis-ci.org/laurilehmijoki/s3_website.png?branch=master)](https://travis-ci.org/laurilehmijoki/s3_website)
4
4
  [![Gem Version](https://fury-badge.herokuapp.com/rb/s3_website.png)](http://badge.fury.io/rb/s3_website)
5
5
 
6
- ## What `s3_website` can do for you
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 s3_website
18
+ gem install s3_website_monadic
19
19
 
20
- `s3_website` requires Ruby and Java. Here is documentation on installing Ruby:
21
- <http://www.ruby-lang.org/en/downloads/>. and here is documentation for Java:
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 `s3_website cfg create`. This generates a configuration file called `s3_website.yml`.
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 `s3_website cfg apply`. This will configure your bucket to function as an
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 `s3_website push` to push your website to S3. Congratulations! You are live.
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
- `s3_website push --site my_website_output`.
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
- `s3_website push --config_dir config`.
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
- `s3_website` attempts to be a command-line interface tool that is easy to
91
- understand and use. For example, `s3_website --help` should print you all the
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, `s3_website` uses the US Standard Region. You can upload your
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 `s3_website` not to push certain files:
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 `s3_website cfg apply`, it will ask you whether you
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 `s3_website push`, it will invalidate the items on CloudFront and
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
- `s3_website` lets you define custom settings for your CloudFront distribution.
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 `s3_website cfg apply`.
245
+ running `s3_website_monadic cfg apply`.
258
246
 
259
247
  #### Invalidating root resources instead of index.htmls
260
248
 
261
- By default, `s3_website push` calls the CloudFront invalidation API with the
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 `s3_website` uses Amazon S3's
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 `s3_website cfg apply` on your
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 `s3_website` gem. (The
327
- command `s3_website cfg apply` internally calls the `configure-s3-website` gem.)
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, `s3_website` does 3 operations in parallel. An operation can be an
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
- s3_website uses [Semantic Versioning](http://semver.org).
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, s3_website will not break
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
@@ -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
- puts "[debg] Using #{jar_file}"
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
- puts "[error] Neither #{released_jar_lookup_paths.join ' or '} is writable. Cannot download s3_website.jar."
120
- puts "[error] Set either directory as writable to the current user and try again."
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
- puts "[info] Downloading #{download_url} into #{downloaded_jar}"
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*
@@ -1,3 +1,3 @@
1
1
  module S3Website
2
- VERSION = '0.0.24'
2
+ VERSION = '0.0.25'
3
3
  end
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 errorOrPushStatus = loadSite(cliArgs.configDir + "/s3_website.yml", cliArgs.site)
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
- .map(Option(_))
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.{S3Settings, S3ClientProvider}
16
+ import s3.website.S3.S3Settings
18
17
  import scala.collection.JavaConversions._
19
18
  import s3.website.model.NewFile
20
- import scala.Some
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.{CloudFrontSettings, CloudFrontClientProvider}
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 scala.collection.immutable.IndexedSeq
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 SiteDirectory with MockAWS {
36
- implicit val site = siteWithFilesAndContent(
37
- config = defaultConfig.copy(gzip = Some(Left(true))),
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 SiteDirectory with MockAWS {
46
- implicit val site = siteWithFilesAndContent(
47
- config = defaultConfig.copy(gzip = Some(Left(true))),
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 SiteDirectory with MockAWS {
61
- implicit val site = siteWithFilesAndContent(
62
- config = defaultConfig.copy(gzip = Some(Right(".xml" :: Nil))),
63
- localFilesWithContent = ("file.xml", "<h1>hi again</h1>") :: Nil
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 SiteDirectory with MockAWS {
74
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index.html", "<div>hello</div>") :: Nil)
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 SiteDirectory with MockAWS {
81
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index.html", "<h1>old text</h1>") :: Nil)
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 SiteDirectory with MockAWS {
88
- implicit val site = siteWithFiles(localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
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 SiteDirectory with MockAWS {
101
- implicit val site = siteWithFiles(localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
108
- implicit val site = siteWithFiles(localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
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 SiteDirectory with MockAWS {
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 SiteDirectory with MockAWS {
137
- implicit val site = siteWithFiles(
138
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
147
- implicit val site = siteWithFiles(
148
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
156
- implicit val site = buildSite(
157
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z"), redirects = Some(Map("/index.php" -> "index.html")))
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 SiteDirectory with MockAWS {
152
+ "retry CloudFront responds with TooManyInvalidationsInProgressException" in new EmptySite with MockAWS {
164
153
  setTooManyInvalidationsInProgress(4)
165
- implicit val site = siteWithFiles(
166
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
161
+ "retry if CloudFront is temporarily unreachable" in new EmptySite with MockAWS {
175
162
  invalidationsFailAndThenSucceed(5)
176
- implicit val site = siteWithFiles(
177
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
186
- implicit val site = siteWithFiles(
187
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
200
- implicit val site = siteWithFiles(
201
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
212
- implicit val site = siteWithFiles(
213
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z"), cloudfront_invalidate_root = Some(true)),
214
- localFiles = "articles/index.html" :: Nil
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 SiteDirectory with MockAWS {
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
- implicit val site = siteWithFiles(
226
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
239
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index.html", "<h1>hello</h1>") :: Nil)
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 SiteDirectory with MockAWS {
244
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index.html", "<h1>hello</h1>") :: Nil)
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 SiteDirectory with MockAWS {
250
- implicit val site = buildSite(defaultConfig.copy(redirects = Some(Map("index.php" -> "/index.html"))))
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 SiteDirectory with MockAWS {
256
- implicit val site = siteWithFiles(
257
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
258
- localFiles = "test.css" :: "articles/index.html" :: Nil
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 SiteDirectory with MockAWS {
245
+ "be 1 if CloudFront is unreachable or broken"in new EmptySite with MockAWS {
264
246
  setCloudFrontAsInternallyBroken()
265
- implicit val site = siteWithFiles(
266
- config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
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 SiteDirectory with MockAWS {
274
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index.html", "<h1>hello</h1>") :: Nil)
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 SiteDirectory with MockAWS {
280
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index.html", "<h1>hello</h1>") :: Nil)
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 SiteDirectory with MockAWS {
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 SiteDirectory with MockAWS {
295
- implicit val site = siteWithFiles(localFiles = "s3_website.yml" :: Nil)
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 SiteDirectory with MockAWS {
303
- implicit val site = siteWithFiles(
304
- config = defaultConfig.copy(exclude_from_upload = Some(Left(".DS_.*?"))),
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 SiteDirectory with MockAWS {
318
- implicit val site = siteWithFiles(
319
- config = defaultConfig.copy(exclude_from_upload = Some(Right(".DS_.*?" :: "logs" :: Nil))),
320
- localFiles = ".DS_Store" :: "logs/test.log" :: Nil
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 SiteDirectory with MockAWS {
329
- implicit val site = buildSite(config = defaultConfig.copy(ignore_on_server = Some(Left("logs"))))
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 SiteDirectory with MockAWS {
342
- implicit val site = buildSite(config = defaultConfig.copy(ignore_on_server = Some(Right(".*txt" :: Nil))))
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 SiteDirectory with MockAWS {
351
- implicit val site = siteWithFiles(defaultConfig.copy(max_age = Some(Left(60))), localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
357
- implicit val site = siteWithFiles(defaultConfig.copy(max_age = Some(Right(Map("*.html" -> 90)))), localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
363
- implicit val site = siteWithFiles(defaultConfig.copy(max_age = Some(Right(Map("assets/**/*.js" -> 90)))), localFiles = "assets/lib/jquery.js" :: Nil)
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 SiteDirectory with MockAWS {
369
- implicit val site = siteWithFiles(defaultConfig.copy(max_age = Some(Right(Map("*.js" -> 90)))), localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
375
- implicit val site = siteWithFiles(defaultConfig.copy(max_age = Some(Left(0))), localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
383
- implicit val site = siteWithFiles(
384
- defaultConfig.copy(max_age = Some(Right(Map(
385
- "assets/*" -> 150,
386
- "assets/*.gif" -> 86400
387
- )))),
388
- localFiles = "assets/jquery.js" :: "assets/picture.gif" :: Nil
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 SiteDirectory with MockAWS {
398
- implicit val site = siteWithFiles(defaultConfig.copy(s3_reduced_redundancy = Some(true)), localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
406
- implicit val site = siteWithFiles(defaultConfig.copy(s3_reduced_redundancy = Some(false)), localFiles = "index.html" :: Nil)
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 SiteDirectory with MockAWS {
414
- implicit val site = buildSite(defaultConfig.copy(redirects = Some(Map("index.php" -> "/index.html"))))
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 SiteDirectory with MockAWS {
420
- implicit val site = buildSite(defaultConfig.copy(redirects = Some(Map("index.php" -> "/index.html"))))
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 SiteDirectory with MockAWS {
428
- implicit val site = siteWithFiles(
429
- localFiles = "index.html" :: Nil,
430
- config = defaultConfig.copy(redirects = Some(Map("index.php" -> "/index.html")))
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 SiteDirectory with MockAWS {
440
- implicit val site = siteWithFiles(localFiles = ".vimrc" :: Nil)
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 SiteDirectory with MockAWS {
448
- implicit val site = siteWithFiles(localFiles = "file.html" :: Nil)
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 SiteDirectory with MockAWS {
454
- implicit val site = siteWithFiles(localFiles = "file.txt" :: Nil)
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 SiteDirectory with MockAWS {
460
- implicit val site = siteWithFiles(localFiles = "file.json" :: Nil)
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 SiteDirectory with MockAWS {
466
- implicit val site = siteWithFilesAndContent(localFilesWithContent = ("index", "<html><body><h1>hi</h1></body></html>") :: Nil)
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 buildSite(config: Config = defaultConfig): Site = Site(siteDir.getAbsolutePath, config)
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
- FileUtils.write(file, content)
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.24
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-09 00:00:00.000000000 Z
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