s3_website 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -1
- data/README.md +8 -0
- data/bin/s3_website +6 -0
- data/changelog.md +7 -0
- data/lib/s3_website/version.rb +1 -1
- data/src/main/scala/s3/website/CloudFront.scala +4 -4
- data/src/main/scala/s3/website/Push.scala +10 -8
- data/src/main/scala/s3/website/S3.scala +9 -9
- data/src/main/scala/s3/website/{Diff.scala → UploadHelper.scala} +15 -9
- data/src/main/scala/s3/website/package.scala +15 -7
- data/src/test/scala/s3/website/S3WebsiteSpec.scala +35 -10
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0ae62a6f1d23ff2c577665bf70c0f0ab7a4eb56
|
4
|
+
data.tar.gz: 012d1764cf766d1b166647c9fe109186622d92a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9801caa38458f53ca30095735cd051eed8e302370a4026d97980d09f3b2a90370ac0a21d5c8fe5c2717f6c57df5eb9f88e851d0ac106adcf34d2ee234719cf04
|
7
|
+
data.tar.gz: 3cf7599615e920cf6000ef4d2525013f74421b5ca759add184012df9d44ef4facdb3657d73c0d9a3c4526e3293e25fdd34869514edaeaaf730156142add4fdcf
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -110,6 +110,9 @@ max_age:
|
|
110
110
|
|
111
111
|
Place the configuration into the file `s3_website.yml`.
|
112
112
|
|
113
|
+
After changing the `max_age` setting, push with the `--force` option.
|
114
|
+
Force-pushing allows you to update the S3 object metadata of existing files.
|
115
|
+
|
113
116
|
### Gzip Compression
|
114
117
|
|
115
118
|
If you choose, you can use compress certain file types before uploading them to
|
@@ -136,6 +139,8 @@ gzip:
|
|
136
139
|
Remember that the extensions here are referring to the *compiled* extensions,
|
137
140
|
not the pre-processed extensions.
|
138
141
|
|
142
|
+
After changing the `gzip` setting, push with the `--force` option.
|
143
|
+
|
139
144
|
### Using non-standard AWS regions
|
140
145
|
|
141
146
|
By default, `s3_website` uses the US Standard Region. You can upload your
|
@@ -196,6 +201,9 @@ You can reduce the cost of hosting your blog on S3 by using Reduced Redundancy S
|
|
196
201
|
* All objects uploaded after this change will use the Reduced Redundancy Storage.
|
197
202
|
* If you want to change all of the files in the bucket, you can change them through the AWS console, or update the timestamp on the files before running `s3_website` again
|
198
203
|
|
204
|
+
After changing the `s3_reduced_redundancy` setting, push with the `--force`
|
205
|
+
option.
|
206
|
+
|
199
207
|
### How to use Cloudfront to deliver your blog
|
200
208
|
|
201
209
|
It is easy to deliver your S3-based web site via Cloudfront, the CDN of Amazon.
|
data/bin/s3_website
CHANGED
@@ -78,6 +78,12 @@ class Cli < Thor
|
|
78
78
|
:default => false,
|
79
79
|
:desc => "Print verbose output"
|
80
80
|
)
|
81
|
+
option(
|
82
|
+
:force,
|
83
|
+
:type => :boolean,
|
84
|
+
:default => false,
|
85
|
+
:desc => "Skip diff calculation and push all the files. This option is useful when you need to update metadata on the S3 objects."
|
86
|
+
)
|
81
87
|
option(
|
82
88
|
:dry_run,
|
83
89
|
:type => :boolean,
|
data/changelog.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
This project uses [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## 2.5.0
|
6
|
+
|
7
|
+
* Add `push --force` option
|
8
|
+
|
9
|
+
When the user pushes with force, s3_website skips the diff. This is helpful for the
|
10
|
+
users who wish to update the S3 object metadata.
|
11
|
+
|
5
12
|
## 2.4.0
|
6
13
|
|
7
14
|
* Add `ignore_on_server: _DELETE_NOTHING_ON_THE_S3_BUCKET_` for the sake of convenience
|
data/lib/s3_website/version.rb
CHANGED
@@ -14,9 +14,9 @@ import s3.website.model.Config.awsCredentials
|
|
14
14
|
|
15
15
|
object CloudFront {
|
16
16
|
def invalidate(invalidationBatch: InvalidationBatch, distributionId: String, attempt: Attempt = 1)
|
17
|
-
(implicit ec: ExecutionContextExecutor, cloudFrontSettings: CloudFrontSetting, config: Config, logger: Logger,
|
17
|
+
(implicit ec: ExecutionContextExecutor, cloudFrontSettings: CloudFrontSetting, config: Config, logger: Logger, pushOptions: PushOptions): InvalidationResult =
|
18
18
|
Future {
|
19
|
-
if (!
|
19
|
+
if (!pushOptions.dryRun) cloudFront createInvalidation new CreateInvalidationRequest(distributionId, invalidationBatch)
|
20
20
|
val result = SuccessfulInvalidation(invalidationBatch.getPaths.getItems.size())
|
21
21
|
logger.debug(invalidationBatch.getPaths.getItems.map(item => s"${Invalidated.renderVerb} $item") mkString "\n")
|
22
22
|
logger.info(result)
|
@@ -27,7 +27,7 @@ object CloudFront {
|
|
27
27
|
))
|
28
28
|
|
29
29
|
def tooManyInvalidationsRetry(invalidationBatch: InvalidationBatch, distributionId: String, attempt: Attempt)
|
30
|
-
(implicit ec: ExecutionContextExecutor, logger: Logger, cloudFrontSettings: CloudFrontSetting, config: Config,
|
30
|
+
(implicit ec: ExecutionContextExecutor, logger: Logger, cloudFrontSettings: CloudFrontSetting, config: Config, pushOptions: PushOptions):
|
31
31
|
PartialFunction[Throwable, InvalidationResult] = {
|
32
32
|
case e: TooManyInvalidationsInProgressException =>
|
33
33
|
val duration: Duration = Duration(
|
@@ -57,7 +57,7 @@ object CloudFront {
|
|
57
57
|
|
58
58
|
type CloudFrontClientProvider = (Config) => AmazonCloudFront
|
59
59
|
|
60
|
-
case class SuccessfulInvalidation(invalidatedItemsCount: Int)(implicit
|
60
|
+
case class SuccessfulInvalidation(invalidatedItemsCount: Int)(implicit pushOptions: PushOptions) extends SuccessReport {
|
61
61
|
def reportMessage = s"${Invalidated.renderVerb} ${invalidatedItemsCount ofType "item"} on CloudFront"
|
62
62
|
}
|
63
63
|
|
@@ -5,7 +5,7 @@ import s3.website.model.Site._
|
|
5
5
|
import scala.concurrent.{ExecutionContextExecutor, Future, Await}
|
6
6
|
import scala.concurrent.duration._
|
7
7
|
import scala.language.postfixOps
|
8
|
-
import s3.website.
|
8
|
+
import s3.website.UploadHelper.{resolveDeletes, resolveUploads}
|
9
9
|
import s3.website.S3._
|
10
10
|
import scala.concurrent.ExecutionContext.fromExecutor
|
11
11
|
import java.util.concurrent.Executors.newFixedThreadPool
|
@@ -44,12 +44,14 @@ object Push {
|
|
44
44
|
@Option(longName = Array("config-dir"), defaultToNull = true) def configDir: String
|
45
45
|
@Option def verbose: Boolean
|
46
46
|
@Option(longName = Array("dry-run")) def dryRun: Boolean
|
47
|
+
@Option(longName = Array("force")) def force: Boolean
|
47
48
|
}
|
48
49
|
|
49
50
|
def push(implicit cliArgs: CliArgs, s3Settings: S3Setting, cloudFrontSettings: CloudFrontSetting, workingDirectory: File): ExitCode = {
|
50
51
|
implicit val logger: Logger = new Logger(cliArgs.verbose)
|
51
|
-
implicit val
|
52
|
+
implicit val pushOptions = new PushOptions {
|
52
53
|
def dryRun = cliArgs.dryRun
|
54
|
+
def force = cliArgs.force
|
53
55
|
}
|
54
56
|
|
55
57
|
implicit val yamlConfig = S3_website_yml(new File(Option(cliArgs.configDir).getOrElse(workingDirectory.getPath) + "/s3_website.yml"))
|
@@ -75,7 +77,7 @@ object Push {
|
|
75
77
|
s3Settings: S3Setting,
|
76
78
|
cloudFrontSettings: CloudFrontSetting,
|
77
79
|
logger: Logger,
|
78
|
-
|
80
|
+
pushOptions: PushOptions
|
79
81
|
): ExitCode = {
|
80
82
|
logger.info(s"${Deploy.renderVerb} ${site.rootDirectory}/* to ${site.config.s3_bucket}")
|
81
83
|
val redirects = Redirect.resolveRedirects
|
@@ -83,7 +85,7 @@ object Push {
|
|
83
85
|
val redirectReports: PushReports = redirects.map(S3 uploadRedirect _) map (Right(_))
|
84
86
|
|
85
87
|
val pushReports: Future[PushReports] = for {
|
86
|
-
errorOrUploads: Either[ErrorReport, Seq[Upload]] <-
|
88
|
+
errorOrUploads: Either[ErrorReport, Seq[Upload]] <- resolveUploads(s3FilesFuture)
|
87
89
|
} yield {
|
88
90
|
val uploadReports: PushReports = errorOrUploads.fold(
|
89
91
|
error => Left(error) :: Nil,
|
@@ -108,7 +110,7 @@ object Push {
|
|
108
110
|
|
109
111
|
def invalidateCloudFrontItems
|
110
112
|
(finishedPushOperations: FinishedPushOperations)
|
111
|
-
(implicit config: Config, cloudFrontSettings: CloudFrontSetting, ec: ExecutionContextExecutor, logger: Logger,
|
113
|
+
(implicit config: Config, cloudFrontSettings: CloudFrontSetting, ec: ExecutionContextExecutor, logger: Logger, pushOptions: PushOptions):
|
112
114
|
Option[InvalidationSucceeded] =
|
113
115
|
config.cloudfront_distribution_id.map { distributionId =>
|
114
116
|
val pushSuccessReports =
|
@@ -141,7 +143,7 @@ object Push {
|
|
141
143
|
type InvalidationSucceeded = Boolean
|
142
144
|
|
143
145
|
def afterPushFinished(finishedPushOps: FinishedPushOperations, invalidationSucceeded: Option[Boolean])
|
144
|
-
(implicit config: Config, logger: Logger,
|
146
|
+
(implicit config: Config, logger: Logger, pushOptions: PushOptions): ExitCode = {
|
145
147
|
val pushCounts = resolvePushCounts(finishedPushOps)
|
146
148
|
logger.info(s"Summary: ${pushCountsToString(pushCounts)}")
|
147
149
|
val pushOpExitCode = finishedPushOps.foldLeft(0) { (memo, finishedUpload) =>
|
@@ -158,7 +160,7 @@ object Push {
|
|
158
160
|
val exitCode = (pushOpExitCode + cloudFrontInvalidationExitCode) min 1
|
159
161
|
|
160
162
|
exitCode match {
|
161
|
-
case 0 if !
|
163
|
+
case 0 if !pushOptions.dryRun && pushCounts.thereWasSomethingToPush =>
|
162
164
|
logger.info(s"Successfully pushed the website to http://${config.s3_bucket}.${config.s3_endpoint.s3WebsiteHostname}")
|
163
165
|
case 1 =>
|
164
166
|
logger.fail(s"Failed to push the website to http://${config.s3_bucket}.${config.s3_endpoint.s3WebsiteHostname}")
|
@@ -191,7 +193,7 @@ object Push {
|
|
191
193
|
)
|
192
194
|
}
|
193
195
|
|
194
|
-
def pushCountsToString(pushCounts: PushCounts)(implicit
|
196
|
+
def pushCountsToString(pushCounts: PushCounts)(implicit pushOptions: PushOptions): String =
|
195
197
|
pushCounts match {
|
196
198
|
case PushCounts(updates, newFiles, failures, redirects, deletes, _, _)
|
197
199
|
if updates == 0 && newFiles == 0 && failures == 0 && redirects == 0 && deletes == 0 =>
|
@@ -18,20 +18,20 @@ import scala.util.Try
|
|
18
18
|
object S3 {
|
19
19
|
|
20
20
|
def uploadRedirect(redirect: Redirect, a: Attempt = 1)
|
21
|
-
(implicit config: Config, s3Settings: S3Setting,
|
21
|
+
(implicit config: Config, s3Settings: S3Setting, pushOptions: PushOptions, executor: ExecutionContextExecutor, logger: Logger) =
|
22
22
|
uploadToS3(Right(redirect))
|
23
23
|
|
24
24
|
def uploadFile(up: Upload, a: Attempt = 1)
|
25
|
-
(implicit config: Config, s3Settings: S3Setting,
|
25
|
+
(implicit config: Config, s3Settings: S3Setting, pushOptions: PushOptions, executor: ExecutionContextExecutor, logger: Logger) =
|
26
26
|
uploadToS3(Left(up))
|
27
27
|
|
28
28
|
def uploadToS3(source: Either[Upload, Redirect], a: Attempt = 1)
|
29
|
-
(implicit config: Config, s3Settings: S3Setting,
|
29
|
+
(implicit config: Config, s3Settings: S3Setting, pushOptions: PushOptions, executor: ExecutionContextExecutor, logger: Logger):
|
30
30
|
Future[Either[FailedUpload, SuccessfulUpload]] =
|
31
31
|
Future {
|
32
32
|
val putObjectRequest = toPutObjectRequest(source).get
|
33
33
|
val uploadDuration =
|
34
|
-
if (
|
34
|
+
if (pushOptions.dryRun) None
|
35
35
|
else Some(recordUploadDuration(putObjectRequest, s3Settings.s3Client(config) putObject putObjectRequest))
|
36
36
|
val report = SuccessfulUpload(
|
37
37
|
source.fold(_.s3Key, _.s3Key),
|
@@ -49,10 +49,10 @@ object S3 {
|
|
49
49
|
)
|
50
50
|
|
51
51
|
def delete(s3Key: S3Key, a: Attempt = 1)
|
52
|
-
(implicit config: Config, s3Settings: S3Setting,
|
52
|
+
(implicit config: Config, s3Settings: S3Setting, pushOptions: PushOptions, executor: ExecutionContextExecutor, logger: Logger):
|
53
53
|
Future[Either[FailedDelete, SuccessfulDelete]] =
|
54
54
|
Future {
|
55
|
-
if (!
|
55
|
+
if (!pushOptions.dryRun) s3Settings.s3Client(config) deleteObject(config.s3_bucket, s3Key)
|
56
56
|
val report = SuccessfulDelete(s3Key)
|
57
57
|
logger.info(report)
|
58
58
|
Right(report)
|
@@ -110,7 +110,7 @@ object S3 {
|
|
110
110
|
def awsS3Client(config: Config) = new AmazonS3Client(awsCredentials(config))
|
111
111
|
|
112
112
|
def resolveS3Files(nextMarker: Option[String] = None, alreadyResolved: Seq[S3File] = Nil, attempt: Attempt = 1)
|
113
|
-
(implicit config: Config, s3Settings: S3Setting, ec: ExecutionContextExecutor, logger: Logger,
|
113
|
+
(implicit config: Config, s3Settings: S3Setting, ec: ExecutionContextExecutor, logger: Logger, pushOptions: PushOptions):
|
114
114
|
Future[Either[ErrorReport, Seq[S3File]]] = Future {
|
115
115
|
logger.debug(nextMarker.fold
|
116
116
|
("Querying S3 files")
|
@@ -152,7 +152,7 @@ object S3 {
|
|
152
152
|
case class SuccessfulUpload(s3Key: S3Key,
|
153
153
|
details: Either[SuccessfulNewOrCreatedDetails, SuccessfulRedirectDetails],
|
154
154
|
putObjectRequest: PutObjectRequest)
|
155
|
-
(implicit
|
155
|
+
(implicit pushOptions: PushOptions, logger: Logger) extends PushSuccessReport {
|
156
156
|
def reportMessage =
|
157
157
|
details.fold(
|
158
158
|
newOrCreatedDetails => s"${newOrCreatedDetails.uploadType.pushAction} $s3Key ($reportDetails)",
|
@@ -205,7 +205,7 @@ object S3 {
|
|
205
205
|
}
|
206
206
|
}
|
207
207
|
|
208
|
-
case class SuccessfulDelete(s3Key: String)(implicit
|
208
|
+
case class SuccessfulDelete(s3Key: String)(implicit pushOptions: PushOptions) extends PushSuccessReport {
|
209
209
|
def reportMessage = s"${Deleted.renderVerb} $s3Key"
|
210
210
|
}
|
211
211
|
|
@@ -1,27 +1,31 @@
|
|
1
1
|
package s3.website
|
2
2
|
|
3
|
+
import s3.website.model.Files.listSiteFiles
|
3
4
|
import s3.website.model._
|
4
5
|
import s3.website.Ruby.rubyRegexMatches
|
5
6
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
6
7
|
import scala.util.{Failure, Success, Try}
|
7
8
|
import java.io.File
|
8
9
|
|
9
|
-
object
|
10
|
+
object UploadHelper {
|
10
11
|
|
11
12
|
type FutureUploads = Future[Either[ErrorReport, Seq[Upload]]]
|
12
13
|
|
13
|
-
def
|
14
|
-
(implicit site: Site, logger: Logger, executor: ExecutionContextExecutor): FutureUploads =
|
15
|
-
|
14
|
+
def resolveUploads(s3FilesFuture: Future[Either[ErrorReport, Seq[S3File]]])
|
15
|
+
(implicit site: Site, pushOptions: PushOptions, logger: Logger, executor: ExecutionContextExecutor): FutureUploads =
|
16
|
+
resolveUploadsAgainstGetBucketResponse(s3FilesFuture)
|
16
17
|
|
17
|
-
private def
|
18
|
-
|
18
|
+
private def resolveUploadsAgainstGetBucketResponse(s3FilesFuture: Future[Either[ErrorReport, Seq[S3File]]])
|
19
|
+
(implicit site: Site,
|
20
|
+
pushOptions: PushOptions,
|
21
|
+
logger: Logger,
|
22
|
+
executor: ExecutionContextExecutor): FutureUploads =
|
19
23
|
s3FilesFuture.map { errorOrS3Files =>
|
20
24
|
errorOrS3Files.right.flatMap { s3Files =>
|
21
25
|
Try {
|
22
26
|
val s3KeyIndex = s3Files.map(_.s3Key).toSet
|
23
27
|
val s3Md5Index = s3Files.map(_.md5).toSet
|
24
|
-
val siteFiles =
|
28
|
+
val siteFiles = listSiteFiles
|
25
29
|
val existsOnS3 = (f: File) => s3KeyIndex contains site.resolveS3Key(f)
|
26
30
|
val isChangedOnS3 = (upload: Upload) => !(s3Md5Index contains upload.md5.get)
|
27
31
|
val newUploads = siteFiles collect {
|
@@ -29,7 +33,7 @@ object Diff {
|
|
29
33
|
}
|
30
34
|
val changedUploads = siteFiles collect {
|
31
35
|
case file if existsOnS3(file) => Upload(file, FileUpdate)
|
32
|
-
} filter isChangedOnS3
|
36
|
+
} filter (if (pushOptions.force) selectAllFiles else isChangedOnS3)
|
33
37
|
newUploads ++ changedUploads
|
34
38
|
} match {
|
35
39
|
case Success(ok) => Right(ok)
|
@@ -38,13 +42,15 @@ object Diff {
|
|
38
42
|
}
|
39
43
|
}
|
40
44
|
|
45
|
+
val selectAllFiles = (upload: Upload) => true
|
46
|
+
|
41
47
|
def resolveDeletes(s3Files: Future[Either[ErrorReport, Seq[S3File]]], redirects: Seq[Redirect])
|
42
48
|
(implicit site: Site, logger: Logger, executor: ExecutionContextExecutor): Future[Either[ErrorReport, Seq[S3Key]]] =
|
43
49
|
if (site.config.ignore_on_server.contains(Left(DELETE_NOTHING_MAGIC_WORD))) {
|
44
50
|
logger.debug(s"Ignoring all files on the bucket, since the setting $DELETE_NOTHING_MAGIC_WORD is on.")
|
45
51
|
Future(Right(Nil))
|
46
52
|
} else {
|
47
|
-
val localS3Keys =
|
53
|
+
val localS3Keys = listSiteFiles.map(site resolveS3Key)
|
48
54
|
|
49
55
|
s3Files map { s3Files: Either[ErrorReport, Seq[S3File]] =>
|
50
56
|
for {
|
@@ -40,8 +40,16 @@ package object website {
|
|
40
40
|
def retryTimeUnit: TimeUnit
|
41
41
|
}
|
42
42
|
|
43
|
-
trait
|
43
|
+
trait PushOptions {
|
44
|
+
/**
|
45
|
+
* @return true if the CLI option --dry-run is on
|
46
|
+
*/
|
44
47
|
def dryRun: Boolean
|
48
|
+
|
49
|
+
/**
|
50
|
+
* @return true if the CLI option --force is on
|
51
|
+
*/
|
52
|
+
def force: Boolean
|
45
53
|
}
|
46
54
|
|
47
55
|
type S3Key = String
|
@@ -51,8 +59,8 @@ package object website {
|
|
51
59
|
trait PushAction {
|
52
60
|
def actionName = getClass.getSimpleName.replace("$", "") // case object class names contain the '$' char
|
53
61
|
|
54
|
-
def renderVerb(implicit
|
55
|
-
if (
|
62
|
+
def renderVerb(implicit pushOptions: PushOptions): String =
|
63
|
+
if (pushOptions.dryRun)
|
56
64
|
s"Would have ${actionName.toLowerCase}"
|
57
65
|
else
|
58
66
|
s"$actionName"
|
@@ -65,15 +73,15 @@ package object website {
|
|
65
73
|
case object Invalidated extends PushAction
|
66
74
|
case object Applied extends PushAction
|
67
75
|
case object PushNothing extends PushAction {
|
68
|
-
override def renderVerb(implicit
|
69
|
-
if (
|
76
|
+
override def renderVerb(implicit pushOptions: PushOptions) =
|
77
|
+
if (pushOptions.dryRun)
|
70
78
|
s"Would have pushed nothing"
|
71
79
|
else
|
72
80
|
s"There was nothing to push"
|
73
81
|
}
|
74
82
|
case object Deploy extends PushAction {
|
75
|
-
override def renderVerb(implicit
|
76
|
-
if (
|
83
|
+
override def renderVerb(implicit pushOptions: PushOptions) =
|
84
|
+
if (pushOptions.dryRun)
|
77
85
|
s"Simulating the deployment of"
|
78
86
|
else
|
79
87
|
s"Deploying"
|
@@ -18,7 +18,7 @@ import org.mockito.{ArgumentCaptor, Matchers, Mockito}
|
|
18
18
|
import org.specs2.mutable.{BeforeAfter, Specification}
|
19
19
|
import org.specs2.specification.Scope
|
20
20
|
import s3.website.CloudFront.CloudFrontSetting
|
21
|
-
import s3.website.
|
21
|
+
import s3.website.UploadHelper.DELETE_NOTHING_MAGIC_WORD
|
22
22
|
import s3.website.Push.{CliArgs}
|
23
23
|
import s3.website.S3.S3Setting
|
24
24
|
import s3.website.model.Config.S3_website_yml
|
@@ -611,15 +611,24 @@ class S3WebsiteSpec extends Specification {
|
|
611
611
|
}
|
612
612
|
}
|
613
613
|
|
614
|
+
"push --force" should {
|
615
|
+
"push all the files whether they have changed or not" in new ForcePush {
|
616
|
+
setLocalFileWithContent(("index.html", "<h1>hi</h1>"))
|
617
|
+
setS3Files(S3File("index.html", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
|
618
|
+
push
|
619
|
+
sentPutObjectRequest.getKey must equalTo("index.html")
|
620
|
+
}
|
621
|
+
}
|
622
|
+
|
614
623
|
"dry run" should {
|
615
|
-
"not push updates" in new
|
624
|
+
"not push updates" in new DryRun {
|
616
625
|
setLocalFileWithContent(("index.html", "<div>new</div>"))
|
617
626
|
setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
|
618
627
|
push
|
619
628
|
noUploadsOccurred must beTrue
|
620
629
|
}
|
621
630
|
|
622
|
-
"not push redirects" in new
|
631
|
+
"not push redirects" in new DryRun {
|
623
632
|
config =
|
624
633
|
"""
|
625
634
|
|redirects:
|
@@ -629,19 +638,19 @@ class S3WebsiteSpec extends Specification {
|
|
629
638
|
noUploadsOccurred must beTrue
|
630
639
|
}
|
631
640
|
|
632
|
-
"not push deletes" in new
|
641
|
+
"not push deletes" in new DryRun {
|
633
642
|
setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
|
634
643
|
push
|
635
644
|
noUploadsOccurred must beTrue
|
636
645
|
}
|
637
646
|
|
638
|
-
"not push new files" in new
|
647
|
+
"not push new files" in new DryRun {
|
639
648
|
setLocalFile("index.html")
|
640
649
|
push
|
641
650
|
noUploadsOccurred must beTrue
|
642
651
|
}
|
643
652
|
|
644
|
-
"not invalidate files" in new
|
653
|
+
"not invalidate files" in new DryRun {
|
645
654
|
config = "cloudfront_invalidation_id: AABBCC"
|
646
655
|
setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
|
647
656
|
push
|
@@ -667,18 +676,32 @@ class S3WebsiteSpec extends Specification {
|
|
667
676
|
|
668
677
|
trait BasicSetup extends SiteLocationFromCliArg with EmptySite with MockAWS with DefaultRunMode
|
669
678
|
|
679
|
+
trait ForcePush extends SiteLocationFromCliArg with EmptySite with MockAWS with ForcePushMode
|
680
|
+
|
681
|
+
trait DryRun extends SiteLocationFromCliArg with EmptySite with MockAWS with DryRunMode
|
682
|
+
|
670
683
|
trait DefaultRunMode {
|
671
|
-
implicit def
|
684
|
+
implicit def pushOptions: PushOptions = new PushOptions {
|
672
685
|
def dryRun = false
|
686
|
+
def force = false
|
673
687
|
}
|
674
688
|
}
|
675
689
|
|
676
690
|
trait DryRunMode {
|
677
|
-
implicit def
|
691
|
+
implicit def pushOptions: PushOptions = new PushOptions {
|
678
692
|
def dryRun = true
|
693
|
+
def force = false
|
679
694
|
}
|
680
695
|
}
|
681
696
|
|
697
|
+
trait ForcePushMode {
|
698
|
+
implicit def pushOptions: PushOptions = new PushOptions {
|
699
|
+
def dryRun = false
|
700
|
+
def force = true
|
701
|
+
}
|
702
|
+
}
|
703
|
+
|
704
|
+
|
682
705
|
trait MockAWS extends MockS3 with MockCloudFront with Scope
|
683
706
|
|
684
707
|
trait MockCloudFront extends MockAWSHelper {
|
@@ -884,17 +907,19 @@ class S3WebsiteSpec extends Specification {
|
|
884
907
|
""".stripMargin
|
885
908
|
)
|
886
909
|
|
887
|
-
def
|
910
|
+
def pushOptions: PushOptions
|
888
911
|
|
889
912
|
implicit def cliArgs: CliArgs =
|
890
913
|
new CliArgs {
|
891
914
|
def verbose = true
|
892
915
|
|
893
|
-
def dryRun =
|
916
|
+
def dryRun = pushOptions.dryRun
|
894
917
|
|
895
918
|
def site = if (siteDirFromCLIArg) siteDirectory.getAbsolutePath else null
|
896
919
|
|
897
920
|
def configDir = configDirectory.getAbsolutePath
|
921
|
+
|
922
|
+
def force = pushOptions.force
|
898
923
|
}
|
899
924
|
}
|
900
925
|
|
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.
|
4
|
+
version: 2.5.0
|
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-10-
|
11
|
+
date: 2014-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -129,11 +129,11 @@ files:
|
|
129
129
|
- src/main/resources/log4j.properties
|
130
130
|
- src/main/scala/s3/website/ByteHelper.scala
|
131
131
|
- src/main/scala/s3/website/CloudFront.scala
|
132
|
-
- src/main/scala/s3/website/Diff.scala
|
133
132
|
- src/main/scala/s3/website/Logger.scala
|
134
133
|
- src/main/scala/s3/website/Push.scala
|
135
134
|
- src/main/scala/s3/website/Ruby.scala
|
136
135
|
- src/main/scala/s3/website/S3.scala
|
136
|
+
- src/main/scala/s3/website/UploadHelper.scala
|
137
137
|
- src/main/scala/s3/website/model/Config.scala
|
138
138
|
- src/main/scala/s3/website/model/S3Endpoint.scala
|
139
139
|
- src/main/scala/s3/website/model/Site.scala
|