s3_website 2.7.3 → 2.7.4
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/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
|