s3_website 2.7.3 → 2.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/changelog.md +6 -0
- data/lib/s3_website/version.rb +1 -1
- data/src/main/scala/s3/website/Logger.scala +5 -5
- data/src/main/scala/s3/website/Push.scala +3 -2
- data/src/main/scala/s3/website/model/Site.scala +44 -13
- data/src/test/scala/s3/website/S3WebsiteSpec.scala +93 -78
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30adad59b826ea4b90e8f5caafd9a203ccfdf053
|
4
|
+
data.tar.gz: 6e3db53bfcfe0d88d5567300831062bb3f6672de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20b06946d5644940c64c9817c8e1710b80d2c5ee83078bbf34796d828ca090585a246f6e255509a1b7b83512d906dc5fc46b27a2e79408678e75ffee84e34ca3
|
7
|
+
data.tar.gz: 349e3aebe7e012b497d08612b50156f23fc35fd2ca6f8969da4599250aa8f74602306c9871843acf3e3ff89c5362830b4d40af470d165123affbaf935f9ba5d6
|
data/changelog.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
This project uses [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## 2.7.4
|
6
|
+
|
7
|
+
* Show a helpful error message if the configured site is missing
|
8
|
+
|
9
|
+
See <https://github.com/laurilehmijoki/s3_website/issues/144> for discussion.
|
10
|
+
|
5
11
|
## 2.7.3
|
6
12
|
|
7
13
|
* Support valid URI characters in max_age glob
|
data/lib/s3_website/version.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
package s3.website
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Logger(val verboseOutput: Boolean) {
|
3
|
+
class Logger(val verboseOutput: Boolean, onLog: Option[(String) => _] = None) {
|
6
4
|
def debug(msg: String) = if (verboseOutput) log(Debug, msg)
|
7
5
|
def info(msg: String) = log(Info, msg)
|
8
6
|
def fail(msg: String) = log(Failure, msg)
|
@@ -12,9 +10,11 @@ class Logger(val verboseOutput: Boolean) {
|
|
12
10
|
|
13
11
|
def pending(msg: String) = log(Wait, msg)
|
14
12
|
|
15
|
-
private def log(logType: LogType, msgRaw: String)
|
13
|
+
private def log(logType: LogType, msgRaw: String) = {
|
16
14
|
val msg = msgRaw.replaceAll("\\n", "\n ") // Indent new lines, so that they arrange nicely with other log lines
|
17
|
-
|
15
|
+
val decoratedLogMessage = s"[$logType] $msg"
|
16
|
+
onLog.foreach(_(decoratedLogMessage))
|
17
|
+
println(decoratedLogMessage)
|
18
18
|
}
|
19
19
|
|
20
20
|
sealed trait LogType {
|
@@ -32,9 +32,11 @@ object Push {
|
|
32
32
|
|
33
33
|
def main(args: Array[String]) {
|
34
34
|
implicit val cliArgs = parseArguments(classOf[CliArgs], args:_*)
|
35
|
+
implicit val logger: Logger = new Logger(cliArgs.verbose)
|
35
36
|
implicit val s3Settings = S3Setting()
|
36
37
|
implicit val cloudFrontSettings = CloudFrontSetting()
|
37
38
|
implicit val workingDirectory = new File(System.getProperty("user.dir")).getAbsoluteFile
|
39
|
+
|
38
40
|
System exit push
|
39
41
|
}
|
40
42
|
|
@@ -48,8 +50,7 @@ object Push {
|
|
48
50
|
@Option(longName = Array("force")) def force: Boolean
|
49
51
|
}
|
50
52
|
|
51
|
-
def push(implicit cliArgs: CliArgs, s3Settings: S3Setting, cloudFrontSettings: CloudFrontSetting, workingDirectory: File): ExitCode = {
|
52
|
-
implicit val logger: Logger = new Logger(cliArgs.verbose)
|
53
|
+
def push(implicit cliArgs: CliArgs, s3Settings: S3Setting, cloudFrontSettings: CloudFrontSetting, workingDirectory: File, logger: Logger): ExitCode = {
|
53
54
|
implicit val pushOptions = new PushOptions {
|
54
55
|
def dryRun = cliArgs.dryRun
|
55
56
|
def force = cliArgs.force
|
@@ -86,30 +86,61 @@ object Site {
|
|
86
86
|
def loadSite(implicit yamlConfig: S3_website_yml, cliArgs: CliArgs, workingDirectory: File, logger: Logger): Either[ErrorReport, Site] =
|
87
87
|
parseConfig.right.flatMap { cfg =>
|
88
88
|
implicit val config: Config = cfg
|
89
|
-
|
90
|
-
errorOrSiteDir.right.map(Site(_, config))
|
89
|
+
resolveSiteDir.right.map(Site(_, config))
|
91
90
|
}
|
92
91
|
|
93
|
-
|
94
|
-
"""|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
def noSiteFound(explanation: String) =
|
93
|
+
s"""|
|
94
|
+
|$explanation.
|
95
|
+
|Either use the --site=DIR command-line argument or define the location of the site in s3_website.yml.
|
96
|
+
|
|
97
|
+
|Here's an example of how you can define the site directory in s3_website.yml:
|
98
|
+
| site: dist/website""".stripMargin
|
99
99
|
|
100
|
-
def resolveSiteDir(implicit yamlConfig: S3_website_yml, config: Config, cliArgs: CliArgs, workingDirectory: File):
|
100
|
+
def resolveSiteDir(implicit yamlConfig: S3_website_yml, config: Config, cliArgs: CliArgs, workingDirectory: File): Either[ErrorReport, File] = {
|
101
101
|
val siteFromAutoDetect = autodetectSiteDir(workingDirectory)
|
102
|
-
val
|
102
|
+
val errOrSiteFromCliArgs: Either[ErrorReport, Option[File]] = Option(cliArgs.site) match {
|
103
|
+
case Some(siteDirFromCliArgs) =>
|
104
|
+
val f = new File(siteDirFromCliArgs)
|
105
|
+
if (f.exists())
|
106
|
+
Right(Some(f))
|
107
|
+
else
|
108
|
+
Left(ErrorReport(noSiteFound(s"Could not find a site at $siteDirFromCliArgs. Check the --site argument.")))
|
109
|
+
case None => Right(None)
|
110
|
+
}
|
103
111
|
|
104
|
-
|
112
|
+
val errOrAvailableSiteDirs: Either[ErrorReport, List[File]] = for {
|
113
|
+
s1 <- errOrSiteFromCliArgs.right
|
114
|
+
s2 <- siteFromConfig.right
|
115
|
+
s3 <- Right(siteFromAutoDetect).right
|
116
|
+
} yield {
|
117
|
+
(s1 :: s2 :: s3 :: Nil) collect {
|
118
|
+
case Some(file) => file
|
119
|
+
}
|
120
|
+
}
|
121
|
+
errOrAvailableSiteDirs.right.flatMap {
|
122
|
+
case mostPreferredSiteDir :: xs => Right(mostPreferredSiteDir)
|
123
|
+
case Nil => Left(ErrorReport(noSiteFound("Could not find a website.")))
|
124
|
+
}
|
105
125
|
}
|
106
126
|
|
107
|
-
def siteFromConfig(implicit yamlConfig: S3_website_yml, config: Config, workingDirectory: File): Option[File] =
|
108
|
-
config
|
127
|
+
def siteFromConfig(implicit yamlConfig: S3_website_yml, config: Config, workingDirectory: File): Either[ErrorReport, Option[File]] = {
|
128
|
+
val siteConfig = config
|
109
129
|
.site
|
110
130
|
.map(new File(_))
|
111
131
|
.map { siteDir =>
|
112
132
|
if (siteDir.isAbsolute) siteDir
|
113
133
|
else new File(yamlConfig.file.getParentFile, siteDir.getPath)
|
114
134
|
}
|
135
|
+
|
136
|
+
siteConfig match {
|
137
|
+
case s @ Some(siteDir) =>
|
138
|
+
if (siteDir.exists())
|
139
|
+
Right(s)
|
140
|
+
else
|
141
|
+
Left(ErrorReport(noSiteFound(s"Could not find a website. (The site setting in s3_website.yml points to a non-existing file $siteDir)")))
|
142
|
+
case None =>
|
143
|
+
Right(None)
|
144
|
+
}
|
145
|
+
}
|
115
146
|
}
|
@@ -26,6 +26,7 @@ import s3.website.model.Ssg.automaticallySupportedSiteGenerators
|
|
26
26
|
import s3.website.model._
|
27
27
|
|
28
28
|
import scala.collection.JavaConversions._
|
29
|
+
import scala.collection.mutable
|
29
30
|
import scala.concurrent.duration._
|
30
31
|
import scala.util.Random
|
31
32
|
|
@@ -36,7 +37,7 @@ class S3WebsiteSpec extends Specification {
|
|
36
37
|
config = "gzip: true"
|
37
38
|
setLocalFileWithContent(("styles.css", "<h1>hi again</h1>"))
|
38
39
|
setS3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
|
39
|
-
push
|
40
|
+
push()
|
40
41
|
sentPutObjectRequest.getKey must equalTo("styles.css")
|
41
42
|
}
|
42
43
|
|
@@ -44,7 +45,7 @@ class S3WebsiteSpec extends Specification {
|
|
44
45
|
config = "gzip: true"
|
45
46
|
setLocalFileWithContent(("styles.css", "<h1>hi</h1>"))
|
46
47
|
setS3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
|
47
|
-
push
|
48
|
+
push()
|
48
49
|
noUploadsOccurred must beTrue
|
49
50
|
}
|
50
51
|
}
|
@@ -60,7 +61,7 @@ class S3WebsiteSpec extends Specification {
|
|
60
61
|
""".stripMargin
|
61
62
|
setLocalFileWithContent(("file.xml", "<h1>hi again</h1>"))
|
62
63
|
setS3File("file.xml", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
|
63
|
-
push
|
64
|
+
push()
|
64
65
|
sentPutObjectRequest.getKey must equalTo("file.xml")
|
65
66
|
}
|
66
67
|
}
|
@@ -69,33 +70,33 @@ class S3WebsiteSpec extends Specification {
|
|
69
70
|
"not upload a file if it has not changed" in new BasicSetup {
|
70
71
|
setLocalFileWithContent(("index.html", "<div>hello</div>"))
|
71
72
|
setS3File("index.html", md5Hex("<div>hello</div>"))
|
72
|
-
push
|
73
|
+
push()
|
73
74
|
noUploadsOccurred must beTrue
|
74
75
|
}
|
75
76
|
|
76
77
|
"update a file if it has changed" in new BasicSetup {
|
77
78
|
setLocalFileWithContent(("index.html", "<h1>old text</h1>"))
|
78
79
|
setS3File("index.html", md5Hex("<h1>new text</h1>"))
|
79
|
-
push
|
80
|
+
push()
|
80
81
|
sentPutObjectRequest.getKey must equalTo("index.html")
|
81
82
|
}
|
82
83
|
|
83
84
|
"create a file if does not exist on S3" in new BasicSetup {
|
84
85
|
setLocalFile("index.html")
|
85
|
-
push
|
86
|
+
push()
|
86
87
|
sentPutObjectRequest.getKey must equalTo("index.html")
|
87
88
|
}
|
88
89
|
|
89
90
|
"delete files that are on S3 but not on local file system" in new BasicSetup {
|
90
91
|
setS3File("old.html", md5Hex("<h1>old text</h1>"))
|
91
|
-
push
|
92
|
+
push()
|
92
93
|
sentDelete must equalTo("old.html")
|
93
94
|
}
|
94
95
|
|
95
96
|
"try again if the upload fails" in new BasicSetup {
|
96
97
|
setLocalFile("index.html")
|
97
98
|
uploadFailsAndThenSucceeds(howManyFailures = 5)
|
98
|
-
push
|
99
|
+
push()
|
99
100
|
verify(amazonS3Client, times(6)).putObject(Matchers.any(classOf[PutObjectRequest]))
|
100
101
|
}
|
101
102
|
|
@@ -106,7 +107,7 @@ class S3WebsiteSpec extends Specification {
|
|
106
107
|
e.setStatusCode(403)
|
107
108
|
e
|
108
109
|
}
|
109
|
-
push
|
110
|
+
push()
|
110
111
|
verify(amazonS3Client, times(1)).putObject(Matchers.any(classOf[PutObjectRequest]))
|
111
112
|
}
|
112
113
|
|
@@ -126,21 +127,21 @@ class S3WebsiteSpec extends Specification {
|
|
126
127
|
}
|
127
128
|
}
|
128
129
|
setLocalFile("index.html")
|
129
|
-
val exitStatus = push
|
130
|
+
val exitStatus = push()
|
130
131
|
verify(amazonS3Client, times(2)).putObject(Matchers.any(classOf[PutObjectRequest]))
|
131
132
|
}
|
132
133
|
|
133
134
|
"try again if the delete fails" in new BasicSetup {
|
134
135
|
setS3File("old.html", md5Hex("<h1>old text</h1>"))
|
135
136
|
deleteFailsAndThenSucceeds(howManyFailures = 5)
|
136
|
-
push
|
137
|
+
push()
|
137
138
|
verify(amazonS3Client, times(6)).deleteObject(Matchers.anyString(), Matchers.anyString())
|
138
139
|
}
|
139
140
|
|
140
141
|
"try again if the object listing fails" in new BasicSetup {
|
141
142
|
setS3File("old.html", md5Hex("<h1>old text</h1>"))
|
142
143
|
objectListingFailsAndThenSucceeds(howManyFailures = 5)
|
143
|
-
push
|
144
|
+
push()
|
144
145
|
verify(amazonS3Client, times(6)).listObjects(Matchers.any(classOf[ListObjectsRequest]))
|
145
146
|
}
|
146
147
|
}
|
@@ -150,14 +151,14 @@ class S3WebsiteSpec extends Specification {
|
|
150
151
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
151
152
|
setLocalFiles("css/test.css", "articles/index.html")
|
152
153
|
setOutdatedS3Keys("css/test.css", "articles/index.html")
|
153
|
-
push
|
154
|
+
push()
|
154
155
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/css/test.css" :: "/articles/index.html" :: Nil).sorted)
|
155
156
|
}
|
156
157
|
|
157
158
|
"not send CloudFront invalidation requests on new objects" in new BasicSetup {
|
158
159
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
159
160
|
setLocalFile("newfile.js")
|
160
|
-
push
|
161
|
+
push()
|
161
162
|
noInvalidationsOccurred must beTrue
|
162
163
|
}
|
163
164
|
|
@@ -167,7 +168,7 @@ class S3WebsiteSpec extends Specification {
|
|
167
168
|
|redirects:
|
168
169
|
| /index.php: index.html
|
169
170
|
""".stripMargin
|
170
|
-
push
|
171
|
+
push()
|
171
172
|
noInvalidationsOccurred must beTrue
|
172
173
|
}
|
173
174
|
|
@@ -176,7 +177,7 @@ class S3WebsiteSpec extends Specification {
|
|
176
177
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
177
178
|
setLocalFile("test.css")
|
178
179
|
setOutdatedS3Keys("test.css")
|
179
|
-
push must equalTo(0) // The retries should finally result in a success
|
180
|
+
push() must equalTo(0) // The retries should finally result in a success
|
180
181
|
sentInvalidationRequests.length must equalTo(4)
|
181
182
|
}
|
182
183
|
|
@@ -185,7 +186,7 @@ class S3WebsiteSpec extends Specification {
|
|
185
186
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
186
187
|
setLocalFile("test.css")
|
187
188
|
setOutdatedS3Keys("test.css")
|
188
|
-
push
|
189
|
+
push()
|
189
190
|
sentInvalidationRequests.length must equalTo(6)
|
190
191
|
}
|
191
192
|
|
@@ -193,7 +194,7 @@ class S3WebsiteSpec extends Specification {
|
|
193
194
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
194
195
|
setLocalFile("articles/arnold's file.html")
|
195
196
|
setOutdatedS3Keys("articles/arnold's file.html")
|
196
|
-
push
|
197
|
+
push()
|
197
198
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/arnold's%20file.html" :: Nil).sorted)
|
198
199
|
}
|
199
200
|
|
@@ -201,7 +202,7 @@ class S3WebsiteSpec extends Specification {
|
|
201
202
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
202
203
|
setLocalFile("maybe-index.html")
|
203
204
|
setOutdatedS3Keys("maybe-index.html")
|
204
|
-
push
|
205
|
+
push()
|
205
206
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/" :: "/maybe-index.html" :: Nil).sorted)
|
206
207
|
}
|
207
208
|
}
|
@@ -214,7 +215,7 @@ class S3WebsiteSpec extends Specification {
|
|
214
215
|
""".stripMargin
|
215
216
|
setLocalFile("articles/index.html")
|
216
217
|
setOutdatedS3Keys("articles/index.html")
|
217
|
-
push
|
218
|
+
push()
|
218
219
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq must contain("/articles/")
|
219
220
|
}
|
220
221
|
|
@@ -225,7 +226,7 @@ class S3WebsiteSpec extends Specification {
|
|
225
226
|
""".stripMargin
|
226
227
|
setLocalFile("articles/index.html")
|
227
228
|
setOutdatedS3Keys("articles/index.html")
|
228
|
-
push
|
229
|
+
push()
|
229
230
|
sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq must contain("/index.html")
|
230
231
|
}
|
231
232
|
}
|
@@ -236,7 +237,7 @@ class S3WebsiteSpec extends Specification {
|
|
236
237
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
237
238
|
setLocalFiles(files:_*)
|
238
239
|
setOutdatedS3Keys(files:_*)
|
239
|
-
push
|
240
|
+
push()
|
240
241
|
sentInvalidationRequests.length must equalTo(2)
|
241
242
|
sentInvalidationRequests(0).getInvalidationBatch.getPaths.getItems.length must equalTo(1000)
|
242
243
|
sentInvalidationRequests(1).getInvalidationBatch.getPaths.getItems.length must equalTo(2)
|
@@ -246,13 +247,13 @@ class S3WebsiteSpec extends Specification {
|
|
246
247
|
"push exit status" should {
|
247
248
|
"be 0 all uploads succeed" in new BasicSetup {
|
248
249
|
setLocalFiles("file.txt")
|
249
|
-
push must equalTo(0)
|
250
|
+
push() must equalTo(0)
|
250
251
|
}
|
251
252
|
|
252
253
|
"be 1 if any of the uploads fails" in new BasicSetup {
|
253
254
|
setLocalFiles("file.txt")
|
254
255
|
when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow(new AmazonServiceException("AWS failed"))
|
255
|
-
push must equalTo(1)
|
256
|
+
push() must equalTo(1)
|
256
257
|
}
|
257
258
|
|
258
259
|
"be 1 if any of the redirects fails" in new BasicSetup {
|
@@ -261,14 +262,14 @@ class S3WebsiteSpec extends Specification {
|
|
261
262
|
| index.php: /index.html
|
262
263
|
""".stripMargin
|
263
264
|
when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow(new AmazonServiceException("AWS failed"))
|
264
|
-
push must equalTo(1)
|
265
|
+
push() must equalTo(1)
|
265
266
|
}
|
266
267
|
|
267
268
|
"be 0 if CloudFront invalidations and uploads succeed"in new BasicSetup {
|
268
269
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
269
270
|
setLocalFile("test.css")
|
270
271
|
setOutdatedS3Keys("test.css")
|
271
|
-
push must equalTo(0)
|
272
|
+
push() must equalTo(0)
|
272
273
|
}
|
273
274
|
|
274
275
|
"be 1 if CloudFront is unreachable or broken"in new BasicSetup {
|
@@ -276,32 +277,32 @@ class S3WebsiteSpec extends Specification {
|
|
276
277
|
config = "cloudfront_distribution_id: EGM1J2JJX9Z"
|
277
278
|
setLocalFile("test.css")
|
278
279
|
setOutdatedS3Keys("test.css")
|
279
|
-
push must equalTo(1)
|
280
|
+
push() must equalTo(1)
|
280
281
|
}
|
281
282
|
|
282
283
|
"be 0 if upload retry succeeds" in new BasicSetup {
|
283
284
|
setLocalFile("index.html")
|
284
285
|
uploadFailsAndThenSucceeds(howManyFailures = 1)
|
285
|
-
push must equalTo(0)
|
286
|
+
push() must equalTo(0)
|
286
287
|
}
|
287
288
|
|
288
289
|
"be 1 if delete retry fails" in new BasicSetup {
|
289
290
|
setLocalFile("index.html")
|
290
291
|
uploadFailsAndThenSucceeds(howManyFailures = 6)
|
291
|
-
push must equalTo(1)
|
292
|
+
push() must equalTo(1)
|
292
293
|
}
|
293
294
|
|
294
295
|
"be 1 if an object listing fails" in new BasicSetup {
|
295
296
|
setS3File("old.html", md5Hex("<h1>old text</h1>"))
|
296
297
|
objectListingFailsAndThenSucceeds(howManyFailures = 6)
|
297
|
-
push must equalTo(1)
|
298
|
+
push() must equalTo(1)
|
298
299
|
}
|
299
300
|
}
|
300
301
|
|
301
302
|
"s3_website.yml file" should {
|
302
303
|
"never be uploaded" in new BasicSetup {
|
303
304
|
setLocalFile("s3_website.yml")
|
304
|
-
push
|
305
|
+
push()
|
305
306
|
noUploadsOccurred must beTrue
|
306
307
|
}
|
307
308
|
}
|
@@ -309,7 +310,7 @@ class S3WebsiteSpec extends Specification {
|
|
309
310
|
".env file" should { // The .env file is the https://github.com/bkeepers/dotenv file
|
310
311
|
"never be uploaded" in new BasicSetup {
|
311
312
|
setLocalFile(".env")
|
312
|
-
push
|
313
|
+
push()
|
313
314
|
noUploadsOccurred must beTrue
|
314
315
|
}
|
315
316
|
}
|
@@ -318,7 +319,7 @@ class S3WebsiteSpec extends Specification {
|
|
318
319
|
"result in matching files not being uploaded" in new BasicSetup {
|
319
320
|
config = "exclude_from_upload: .DS_.*?"
|
320
321
|
setLocalFile(".DS_Store")
|
321
|
-
push
|
322
|
+
push()
|
322
323
|
noUploadsOccurred must beTrue
|
323
324
|
}
|
324
325
|
}
|
@@ -335,7 +336,7 @@ class S3WebsiteSpec extends Specification {
|
|
335
336
|
| - logs
|
336
337
|
""".stripMargin
|
337
338
|
setLocalFiles(".DS_Store", "logs/test.log")
|
338
|
-
push
|
339
|
+
push()
|
339
340
|
noUploadsOccurred must beTrue
|
340
341
|
}
|
341
342
|
}
|
@@ -344,14 +345,14 @@ class S3WebsiteSpec extends Specification {
|
|
344
345
|
"not delete the S3 objects that match the ignore value" in new BasicSetup {
|
345
346
|
config = "ignore_on_server: logs"
|
346
347
|
setS3File("logs/log.txt")
|
347
|
-
push
|
348
|
+
push()
|
348
349
|
noDeletesOccurred must beTrue
|
349
350
|
}
|
350
351
|
|
351
352
|
"support non-US-ASCII files" in new BasicSetup {
|
352
353
|
setS3File("tags/笔记/test.html", "")
|
353
354
|
config = "ignore_on_server: tags/笔记/test.html"
|
354
|
-
push
|
355
|
+
push()
|
355
356
|
noDeletesOccurred must beTrue
|
356
357
|
}
|
357
358
|
}
|
@@ -362,7 +363,7 @@ class S3WebsiteSpec extends Specification {
|
|
362
363
|
|ignore_on_server: $DELETE_NOTHING_MAGIC_WORD
|
363
364
|
""".stripMargin
|
364
365
|
setS3File("file.txt")
|
365
|
-
push
|
366
|
+
push()
|
366
367
|
noDeletesOccurred
|
367
368
|
}
|
368
369
|
}
|
@@ -378,7 +379,7 @@ class S3WebsiteSpec extends Specification {
|
|
378
379
|
| - .*txt
|
379
380
|
""".stripMargin
|
380
381
|
setS3File("logs/log.txt", "")
|
381
|
-
push
|
382
|
+
push()
|
382
383
|
noDeletesOccurred must beTrue
|
383
384
|
}
|
384
385
|
|
@@ -388,11 +389,24 @@ class S3WebsiteSpec extends Specification {
|
|
388
389
|
|ignore_on_server:
|
389
390
|
| - tags/笔记/test.html
|
390
391
|
""".stripMargin
|
391
|
-
push
|
392
|
+
push()
|
392
393
|
noDeletesOccurred must beTrue
|
393
394
|
}
|
394
395
|
}
|
395
396
|
|
397
|
+
"error message" should {
|
398
|
+
"be helpful when the site directory is missing" in new BasicSetup {
|
399
|
+
config = "site: nonexisting_site_dir"
|
400
|
+
val logEntries = new mutable.MutableList[String]
|
401
|
+
push(logCapturer = Some((logEntry: String) =>
|
402
|
+
logEntries += logEntry
|
403
|
+
))
|
404
|
+
logEntries must contain(
|
405
|
+
s"Could not find a website. (The site setting in s3_website.yml points to a non-existing file $siteDirectory/nonexisting_site_dir)"
|
406
|
+
)
|
407
|
+
}
|
408
|
+
}
|
409
|
+
|
396
410
|
"site in config" should {
|
397
411
|
"let the user deploy a site from a custom location" in new CustomSiteDirectory with EmptySite with MockAWS with DefaultRunMode {
|
398
412
|
config = s"site: $siteDirectory"
|
@@ -401,14 +415,14 @@ class S3WebsiteSpec extends Specification {
|
|
401
415
|
new File(siteDirectory, ".vimrc").exists() must beTrue // Sanity check
|
402
416
|
siteDirectory must not equalTo workingDirectory // Sanity check
|
403
417
|
|
404
|
-
push
|
418
|
+
push()
|
405
419
|
sentPutObjectRequest.getKey must equalTo(".vimrc")
|
406
420
|
}
|
407
421
|
|
408
422
|
"not override the --site command-line switch" in new BasicSetup {
|
409
|
-
config = s"site:
|
423
|
+
config = s"site: ${System.getProperty("java.io.tmpdir")}"
|
410
424
|
setLocalFile(".vimrc") // This creates a file in the directory into which the --site CLI arg points
|
411
|
-
push
|
425
|
+
push()
|
412
426
|
sentPutObjectRequest.getKey must equalTo(".vimrc")
|
413
427
|
}
|
414
428
|
|
@@ -417,7 +431,7 @@ class S3WebsiteSpec extends Specification {
|
|
417
431
|
addContentToAutomaticallyDetectedSite(workingDirectory)
|
418
432
|
config = s"site: $siteDirectory"
|
419
433
|
setLocalFile(".vimrc") // Add content to the custom site directory
|
420
|
-
push
|
434
|
+
push()
|
421
435
|
sentPutObjectRequest.getKey must equalTo(".vimrc")
|
422
436
|
}
|
423
437
|
|
@@ -433,7 +447,7 @@ class S3WebsiteSpec extends Specification {
|
|
433
447
|
"be applied to all files" in new BasicSetup {
|
434
448
|
config = "max_age: 60"
|
435
449
|
setLocalFile("index.html")
|
436
|
-
push
|
450
|
+
push()
|
437
451
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=60")
|
438
452
|
}
|
439
453
|
|
@@ -444,7 +458,7 @@ class S3WebsiteSpec extends Specification {
|
|
444
458
|
""".stripMargin
|
445
459
|
val allValidUrlCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=" // See http://stackoverflow.com/a/1547940/990356 for discussion
|
446
460
|
setLocalFile(s"$allValidUrlCharacters.html")
|
447
|
-
push
|
461
|
+
push()
|
448
462
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
|
449
463
|
}
|
450
464
|
|
@@ -454,7 +468,7 @@ class S3WebsiteSpec extends Specification {
|
|
454
468
|
| "*.html": 90
|
455
469
|
""".stripMargin
|
456
470
|
setLocalFile("index.html")
|
457
|
-
push
|
471
|
+
push()
|
458
472
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
|
459
473
|
}
|
460
474
|
|
@@ -464,7 +478,7 @@ class S3WebsiteSpec extends Specification {
|
|
464
478
|
| "assets/**/*.js": 90
|
465
479
|
""".stripMargin
|
466
480
|
setLocalFile("assets/lib/jquery.js")
|
467
|
-
push
|
481
|
+
push()
|
468
482
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
|
469
483
|
}
|
470
484
|
|
@@ -474,14 +488,14 @@ class S3WebsiteSpec extends Specification {
|
|
474
488
|
| "*.js": 90
|
475
489
|
""".stripMargin
|
476
490
|
setLocalFile("index.html")
|
477
|
-
push
|
491
|
+
push()
|
478
492
|
sentPutObjectRequest.getMetadata.getCacheControl must beNull
|
479
493
|
}
|
480
494
|
|
481
495
|
"be used to disable caching" in new BasicSetup {
|
482
496
|
config = "max_age: 0"
|
483
497
|
setLocalFile("index.html")
|
484
|
-
push
|
498
|
+
push()
|
485
499
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("no-cache; max-age=0")
|
486
500
|
}
|
487
501
|
|
@@ -491,7 +505,7 @@ class S3WebsiteSpec extends Specification {
|
|
491
505
|
| "*": 21600
|
492
506
|
""".stripMargin
|
493
507
|
setLocalFile("tags/笔记/index.html")
|
494
|
-
push must equalTo(0)
|
508
|
+
push() must equalTo(0)
|
495
509
|
}
|
496
510
|
}
|
497
511
|
|
@@ -503,7 +517,7 @@ class S3WebsiteSpec extends Specification {
|
|
503
517
|
| "assets/*.gif": 86400
|
504
518
|
""".stripMargin
|
505
519
|
setLocalFiles("assets/jquery.js", "assets/picture.gif")
|
506
|
-
push
|
520
|
+
push()
|
507
521
|
sentPutObjectRequests.find(_.getKey == "assets/jquery.js").get.getMetadata.getCacheControl must equalTo("max-age=150")
|
508
522
|
sentPutObjectRequests.find(_.getKey == "assets/picture.gif").get.getMetadata.getCacheControl must equalTo("max-age=86400")
|
509
523
|
}
|
@@ -513,7 +527,7 @@ class S3WebsiteSpec extends Specification {
|
|
513
527
|
"result in uploads being marked with reduced redundancy" in new BasicSetup {
|
514
528
|
config = "s3_reduced_redundancy: true"
|
515
529
|
setLocalFile("file.exe")
|
516
|
-
push
|
530
|
+
push()
|
517
531
|
sentPutObjectRequest.getStorageClass must equalTo("REDUCED_REDUNDANCY")
|
518
532
|
}
|
519
533
|
}
|
@@ -522,7 +536,7 @@ class S3WebsiteSpec extends Specification {
|
|
522
536
|
"result in uploads being marked with the default storage class" in new BasicSetup {
|
523
537
|
config = "s3_reduced_redundancy: false"
|
524
538
|
setLocalFile("file.exe")
|
525
|
-
push
|
539
|
+
push()
|
526
540
|
sentPutObjectRequest.getStorageClass must beNull
|
527
541
|
}
|
528
542
|
}
|
@@ -533,7 +547,7 @@ class S3WebsiteSpec extends Specification {
|
|
533
547
|
|redirects:
|
534
548
|
| index.php: /index.html
|
535
549
|
""".stripMargin
|
536
|
-
push
|
550
|
+
push()
|
537
551
|
sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
|
538
552
|
}
|
539
553
|
|
@@ -542,7 +556,7 @@ class S3WebsiteSpec extends Specification {
|
|
542
556
|
|redirects:
|
543
557
|
| index.php: index.html
|
544
558
|
""".stripMargin
|
545
|
-
push
|
559
|
+
push()
|
546
560
|
sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
|
547
561
|
}
|
548
562
|
|
@@ -551,7 +565,7 @@ class S3WebsiteSpec extends Specification {
|
|
551
565
|
|redirects:
|
552
566
|
| index.php: http://www.youtube.com/watch?v=dQw4w9WgXcQ
|
553
567
|
""".stripMargin
|
554
|
-
push
|
568
|
+
push()
|
555
569
|
sentPutObjectRequest.getRedirectLocation must equalTo("http://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
556
570
|
}
|
557
571
|
|
@@ -560,7 +574,7 @@ class S3WebsiteSpec extends Specification {
|
|
560
574
|
|redirects:
|
561
575
|
| index.php: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
562
576
|
""".stripMargin
|
563
|
-
push
|
577
|
+
push()
|
564
578
|
sentPutObjectRequest.getRedirectLocation must equalTo("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
565
579
|
}
|
566
580
|
|
@@ -569,7 +583,7 @@ class S3WebsiteSpec extends Specification {
|
|
569
583
|
|redirects:
|
570
584
|
| index.php: /index.html
|
571
585
|
""".stripMargin
|
572
|
-
push
|
586
|
+
push()
|
573
587
|
sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=0, no-cache")
|
574
588
|
}
|
575
589
|
}
|
@@ -582,7 +596,7 @@ class S3WebsiteSpec extends Specification {
|
|
582
596
|
""".stripMargin
|
583
597
|
setLocalFile("index.php")
|
584
598
|
setS3File("index.php", "md5")
|
585
|
-
push
|
599
|
+
push()
|
586
600
|
noDeletesOccurred must beTrue
|
587
601
|
}
|
588
602
|
}
|
@@ -590,7 +604,7 @@ class S3WebsiteSpec extends Specification {
|
|
590
604
|
"dotfiles" should {
|
591
605
|
"be included in the pushed files" in new BasicSetup {
|
592
606
|
setLocalFile(".vimrc")
|
593
|
-
push
|
607
|
+
push()
|
594
608
|
sentPutObjectRequest.getKey must equalTo(".vimrc")
|
595
609
|
}
|
596
610
|
}
|
@@ -598,25 +612,25 @@ class S3WebsiteSpec extends Specification {
|
|
598
612
|
"content type inference" should {
|
599
613
|
"add charset=utf-8 to all html documents" in new BasicSetup {
|
600
614
|
setLocalFile("index.html")
|
601
|
-
push
|
615
|
+
push()
|
602
616
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("text/html; charset=utf-8")
|
603
617
|
}
|
604
618
|
|
605
619
|
"add charset=utf-8 to all text documents" in new BasicSetup {
|
606
620
|
setLocalFile("index.txt")
|
607
|
-
push
|
621
|
+
push()
|
608
622
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("text/plain; charset=utf-8")
|
609
623
|
}
|
610
624
|
|
611
625
|
"add charset=utf-8 to all json documents" in new BasicSetup {
|
612
626
|
setLocalFile("data.json")
|
613
|
-
push
|
627
|
+
push()
|
614
628
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("application/json; charset=utf-8")
|
615
629
|
}
|
616
630
|
|
617
631
|
"resolve the content type from file contents" in new BasicSetup {
|
618
632
|
setLocalFileWithContent(("index", "<html><body><h1>hi</h1></body></html>"))
|
619
|
-
push
|
633
|
+
push()
|
620
634
|
sentPutObjectRequest.getMetadata.getContentType must equalTo("text/html; charset=utf-8")
|
621
635
|
}
|
622
636
|
}
|
@@ -627,7 +641,7 @@ class S3WebsiteSpec extends Specification {
|
|
627
641
|
|redirects:
|
628
642
|
|<%= ('a'..'f').to_a.map do |t| ' '+t+ ': /'+t+'.html' end.join('\n')%>
|
629
643
|
""".stripMargin
|
630
|
-
push
|
644
|
+
push()
|
631
645
|
sentPutObjectRequests.length must equalTo(6)
|
632
646
|
sentPutObjectRequests.forall(_.getRedirectLocation != null) must beTrue
|
633
647
|
}
|
@@ -637,7 +651,7 @@ class S3WebsiteSpec extends Specification {
|
|
637
651
|
"push all the files whether they have changed or not" in new ForcePush {
|
638
652
|
setLocalFileWithContent(("index.html", "<h1>hi</h1>"))
|
639
653
|
setS3File("index.html", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
|
640
|
-
push
|
654
|
+
push()
|
641
655
|
sentPutObjectRequest.getKey must equalTo("index.html")
|
642
656
|
}
|
643
657
|
}
|
@@ -646,7 +660,7 @@ class S3WebsiteSpec extends Specification {
|
|
646
660
|
"not push updates" in new DryRun {
|
647
661
|
setLocalFileWithContent(("index.html", "<div>new</div>"))
|
648
662
|
setS3File("index.html", md5Hex("<div>old</div>"))
|
649
|
-
push
|
663
|
+
push()
|
650
664
|
noUploadsOccurred must beTrue
|
651
665
|
}
|
652
666
|
|
@@ -656,26 +670,26 @@ class S3WebsiteSpec extends Specification {
|
|
656
670
|
|redirects:
|
657
671
|
| index.php: /index.html
|
658
672
|
""".stripMargin
|
659
|
-
push
|
673
|
+
push()
|
660
674
|
noUploadsOccurred must beTrue
|
661
675
|
}
|
662
676
|
|
663
677
|
"not push deletes" in new DryRun {
|
664
678
|
setS3File("index.html", md5Hex("<div>old</div>"))
|
665
|
-
push
|
679
|
+
push()
|
666
680
|
noUploadsOccurred must beTrue
|
667
681
|
}
|
668
682
|
|
669
683
|
"not push new files" in new DryRun {
|
670
684
|
setLocalFile("index.html")
|
671
|
-
push
|
685
|
+
push()
|
672
686
|
noUploadsOccurred must beTrue
|
673
687
|
}
|
674
688
|
|
675
689
|
"not invalidate files" in new DryRun {
|
676
690
|
config = "cloudfront_invalidation_id: AABBCC"
|
677
691
|
setS3File("index.html", md5Hex("<div>old</div>"))
|
678
|
-
push
|
692
|
+
push()
|
679
693
|
noInvalidationsOccurred must beTrue
|
680
694
|
}
|
681
695
|
}
|
@@ -683,7 +697,7 @@ class S3WebsiteSpec extends Specification {
|
|
683
697
|
"Jekyll site" should {
|
684
698
|
"be detected automatically" in new JekyllSite with EmptySite with MockAWS with DefaultRunMode {
|
685
699
|
setLocalFile("index.html")
|
686
|
-
push
|
700
|
+
push()
|
687
701
|
sentPutObjectRequests.length must equalTo(1)
|
688
702
|
}
|
689
703
|
}
|
@@ -691,7 +705,7 @@ class S3WebsiteSpec extends Specification {
|
|
691
705
|
"Nanoc site" should {
|
692
706
|
"be detected automatically" in new NanocSite with EmptySite with MockAWS with DefaultRunMode {
|
693
707
|
setLocalFile("index.html")
|
694
|
-
push
|
708
|
+
push()
|
695
709
|
sentPutObjectRequests.length must equalTo(1)
|
696
710
|
}
|
697
711
|
}
|
@@ -705,7 +719,7 @@ class S3WebsiteSpec extends Specification {
|
|
705
719
|
| index.php: /index.html
|
706
720
|
""".stripMargin
|
707
721
|
setRedirectObject("index.php")
|
708
|
-
push
|
722
|
+
push()
|
709
723
|
noUploadsOccurred
|
710
724
|
}
|
711
725
|
|
@@ -717,7 +731,7 @@ class S3WebsiteSpec extends Specification {
|
|
717
731
|
| index.php: /index.html
|
718
732
|
""".stripMargin
|
719
733
|
setRedirectObject("index.php")
|
720
|
-
push
|
734
|
+
push()
|
721
735
|
noDeletesOccurred
|
722
736
|
}
|
723
737
|
|
@@ -728,7 +742,7 @@ class S3WebsiteSpec extends Specification {
|
|
728
742
|
|redirects:
|
729
743
|
| index.php: /index.html
|
730
744
|
""".stripMargin
|
731
|
-
push
|
745
|
+
push()
|
732
746
|
sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
|
733
747
|
}
|
734
748
|
|
@@ -740,7 +754,7 @@ class S3WebsiteSpec extends Specification {
|
|
740
754
|
| index.php: /index.html
|
741
755
|
""".stripMargin
|
742
756
|
setRedirectObject("index.php")
|
743
|
-
push
|
757
|
+
push()
|
744
758
|
sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
|
745
759
|
}
|
746
760
|
}
|
@@ -1000,13 +1014,14 @@ class S3WebsiteSpec extends Specification {
|
|
1000
1014
|
}
|
1001
1015
|
}
|
1002
1016
|
|
1003
|
-
def push(implicit
|
1017
|
+
def push(logCapturer: Option[(String) => _] = None)(implicit
|
1004
1018
|
emptyYamlConfig: S3_website_yml,
|
1005
1019
|
configString: ConfigString,
|
1006
1020
|
cliArgs: CliArgs,
|
1007
1021
|
s3Settings: S3Setting,
|
1008
1022
|
cloudFrontSettings: CloudFrontSetting,
|
1009
1023
|
workingDirectory: File) = {
|
1024
|
+
implicit val logger = new Logger(verboseOutput = true, logCapturer)
|
1010
1025
|
write(emptyYamlConfig.file, configString.yaml) // Write the yaml config lazily, so that the tests can override the default yaml config
|
1011
1026
|
Push.push
|
1012
1027
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: s3_website
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.
|
4
|
+
version: 2.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lauri Lehmijoki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|