s3_website 2.4.0 → 2.5.0
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/.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
|