s3_website_monadic 0.0.35 → 0.0.36
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 +1 -1
- data/README.md +19 -22
- data/additional-docs/development.md +21 -1
- data/bin/s3_website_monadic +57 -5
- data/build.sbt +1 -1
- data/changelog.md +15 -3
- data/lib/s3_website/version.rb +1 -1
- data/src/main/scala/s3/website/CloudFront.scala +2 -2
- data/src/main/scala/s3/website/S3.scala +2 -1
- data/src/main/scala/s3/website/model/Config.scala +17 -2
- data/src/main/scala/s3/website/model/Site.scala +2 -2
- data/src/test/scala/s3/website/S3WebsiteSpec.scala +10 -11
- 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: e7c4690fe767a7e3acb2814561d70516adb70a37
|
4
|
+
data.tar.gz: 58348dcb010d4f4ba30132c26e7cc8008e97f4f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ede3a36d8687fe7d9794405810b8425c01a7ce4c5b19c196d77ee7f1737fb2913d0d970767e3d04b0c5cfa4580ca14cd5f430d55b0a532d93523d44edca504e1
|
7
|
+
data.tar.gz: bf26d74d0b3179386e48fd12d42ae9d58a51f7bf94b4a7ad84014513736b461c5cf4b7f2345cecbe799c611d20ee68dbd7a03a2706d9d124fdb7949187b9a681
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -17,10 +17,6 @@
|
|
17
17
|
|
18
18
|
gem install s3_website_monadic
|
19
19
|
|
20
|
-
`s3_website_monadic` requires Ruby and Java. Here is documentation on installing Ruby:
|
21
|
-
<http://www.ruby-lang.org/en/downloads/>, and here is documentation for Java:
|
22
|
-
<http://www.oracle.com/technetwork/java/javase/downloads/index.html>.
|
23
|
-
|
24
20
|
## Usage
|
25
21
|
|
26
22
|
Here's how you can get started:
|
@@ -34,11 +30,6 @@ Here's how you can get started:
|
|
34
30
|
S3 website. If the bucket does not exist, the command will create it for you.
|
35
31
|
* Run `s3_website_monadic push` to push your website to S3. Congratulations! You are live.
|
36
32
|
|
37
|
-
**Important security note:** if the source code of your website is publicly
|
38
|
-
available, ensure that the `s3_website.yml` file is in the list of ignored files.
|
39
|
-
For git users this means that the file `.gitignore` should mention the
|
40
|
-
`s3_website.yml` file.
|
41
|
-
|
42
33
|
### For Jekyll users
|
43
34
|
|
44
35
|
S3_website will automatically discover your website in the *_site* directory.
|
@@ -334,7 +325,7 @@ If you experience the "too many open files" error, either increase the amount of
|
|
334
325
|
maximum open files (on Unix-like systems, see `man ulimit`) or decrease the
|
335
326
|
`concurrency_level` setting.
|
336
327
|
|
337
|
-
|
328
|
+
### Simulating deployments
|
338
329
|
|
339
330
|
You can simulate the `s3_website_monadic push` operation by adding the
|
340
331
|
`--dry-run` switch. The dry run mode will not apply any modifications on your S3
|
@@ -344,18 +335,31 @@ operation would actually do if run without the dry switch.
|
|
344
335
|
You can use the dry run mode if you are unsure what kind of effects the `push`
|
345
336
|
operation would cause to your live website.
|
346
337
|
|
338
|
+
## Migrating from v1 to v2
|
339
|
+
|
340
|
+
Please read the [release note](/changelog.md#200) on version 2. It contains
|
341
|
+
information on backward incompatible changes.
|
342
|
+
|
347
343
|
## Example configurations
|
348
344
|
|
349
345
|
See
|
350
346
|
<https://github.com/laurilehmijoki/s3_website/blob/master/additional-docs/example-configurations.md>.
|
351
347
|
|
348
|
+
## On security
|
349
|
+
|
350
|
+
If the source code of your website is publicly
|
351
|
+
available, ensure that the `s3_website.yml` file is in the list of ignored files.
|
352
|
+
For git users this means that the file `.gitignore` should mention the
|
353
|
+
`s3_website.yml` file.
|
354
|
+
|
355
|
+
If you use the .dotenv gem, ensure that you do not push the `.env` file to a
|
356
|
+
public git repository.
|
357
|
+
|
352
358
|
## Known issues
|
353
359
|
|
354
360
|
Please create an issue and send a pull request if you spot any.
|
355
361
|
|
356
|
-
##
|
357
|
-
|
358
|
-
### Versioning
|
362
|
+
## Versioning
|
359
363
|
|
360
364
|
s3_website_monadic uses [Semantic Versioning](http://semver.org).
|
361
365
|
|
@@ -363,10 +367,9 @@ In the spirit of semantic versioning, here is the definition of public API for
|
|
363
367
|
s3_website: Within a major version, s3_website_monadic will not break
|
364
368
|
backwards-compatibility of anything that is mentioned in this README file.
|
365
369
|
|
366
|
-
|
370
|
+
## Development
|
367
371
|
|
368
|
-
|
369
|
-
* Run all tests by invoking `rake test`
|
372
|
+
See [development](additional-docs/development.md).
|
370
373
|
|
371
374
|
### Contributing
|
372
375
|
|
@@ -382,12 +385,6 @@ If you are not sure how to test your pull request, you can ask the [gem owners
|
|
382
385
|
However, by including proper tests, you increase the chances of your pull
|
383
386
|
request being incorporated into future releases.
|
384
387
|
|
385
|
-
#### Checklist for new features
|
386
|
-
|
387
|
-
* Is it tested?
|
388
|
-
* Is it documented in README?
|
389
|
-
* Is it mentioned in `resources/configuration_file_template.yml`?
|
390
|
-
|
391
388
|
## License
|
392
389
|
|
393
390
|
MIT. See the LICENSE file for more information.
|
@@ -1,4 +1,23 @@
|
|
1
|
-
|
1
|
+
## Coding
|
2
|
+
|
3
|
+
Install a Scala editor. Intellij IDEA has great Scala support.
|
4
|
+
|
5
|
+
If you use IDEA, install the [Grep
|
6
|
+
Console](http://plugins.jetbrains.com/plugin/?idea&pluginId=7125) plugin. It
|
7
|
+
shows the ANSI colors in your IDEA console.
|
8
|
+
|
9
|
+
### Test runs with IDEA
|
10
|
+
|
11
|
+
1. Create a run profile: *Run* –> *Edit Configurations...*.
|
12
|
+
2. Add *Application*
|
13
|
+
3. Set *Main class* to `s3.website.Push`
|
14
|
+
4. Set *Program arguments* to `--site=/Users/you/yourtestsite/_site --config-dir=/Users/you/yourtestsite --verbose`
|
15
|
+
|
16
|
+
## Automated tests
|
17
|
+
|
18
|
+
./sbt test
|
19
|
+
|
20
|
+
## Test Linux distributions
|
2
21
|
|
3
22
|
Use Vagrant for testing the installation procedure on Linux.
|
4
23
|
|
@@ -7,3 +26,4 @@ Here's howto:
|
|
7
26
|
1. Install <https://www.vagrantup.com/downloads.html>
|
8
27
|
2. `cd vagrant && vagrant status`
|
9
28
|
3. launch with `vagrant up <name>` and ssh into with `vagrant ssh <name>`
|
29
|
+
4. test the latest release with `gem install s3_website && s3_website push`
|
data/bin/s3_website_monadic
CHANGED
@@ -83,6 +83,11 @@ class Cli < Thor
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def run_s3_website_jar(jar_file, call_dir, logger)
|
86
|
+
java_installed = resolve_exit_status('which javas') or resolve_exit_status('javas -version')
|
87
|
+
unless java_installed
|
88
|
+
logger.info_msg "Cannot find Java. s3_website push is implemented in Scala, and it needs Java to run."
|
89
|
+
autoinstall_java_or_print_help_and_exit(logger)
|
90
|
+
end
|
86
91
|
args = ARGV.join(' ').sub('push', '')
|
87
92
|
logger.debug_msg "Using #{jar_file}"
|
88
93
|
if system("java -cp #{jar_file} s3.website.Push #{args}")
|
@@ -92,6 +97,53 @@ def run_s3_website_jar(jar_file, call_dir, logger)
|
|
92
97
|
end
|
93
98
|
end
|
94
99
|
|
100
|
+
def resolve_exit_status(cmd)
|
101
|
+
`#{cmd}`
|
102
|
+
cmd_succeeded = $? == 0
|
103
|
+
rescue
|
104
|
+
cmd_succeeded = false
|
105
|
+
end
|
106
|
+
|
107
|
+
def autoinstall_java_or_print_help_and_exit(logger)
|
108
|
+
@logger = logger
|
109
|
+
automatic_methods = [
|
110
|
+
{
|
111
|
+
:package_manager_lookup => 'which apt-get',
|
112
|
+
:install_command => 'sudo apt-get install --assume-yes openjdk-7-jre'
|
113
|
+
},
|
114
|
+
{
|
115
|
+
:package_manager_lookup => 'which yum',
|
116
|
+
:install_command => 'sudo yum install --assumeyes java-1.7.0-openjdk'
|
117
|
+
}
|
118
|
+
]
|
119
|
+
|
120
|
+
def print_manual_method_and_exit
|
121
|
+
@logger.info_msg 'Go to http://java.com, install Java and then try again.'
|
122
|
+
exit 1
|
123
|
+
end
|
124
|
+
|
125
|
+
manual_method_help =
|
126
|
+
|
127
|
+
automatic_method = automatic_methods.find { |automatic_method|
|
128
|
+
resolve_exit_status automatic_method.fetch(:package_manager_lookup)
|
129
|
+
}
|
130
|
+
if automatic_method
|
131
|
+
@logger.info_msg "Do you want me to install Java with the command `#{automatic_method.fetch(:install_command)}`? [Y/n]"
|
132
|
+
user_answer = $stdin.gets
|
133
|
+
if user_answer.chomp.downcase == 'y' or user_answer == "\n"
|
134
|
+
automatic_method_succeeded = system automatic_method.fetch(:install_command)
|
135
|
+
unless automatic_method_succeeded
|
136
|
+
@logger.fail_msg "Could not automatically install Java. Try setting it up manually:"
|
137
|
+
print_manual_method_and_exit
|
138
|
+
end
|
139
|
+
else
|
140
|
+
print_manual_method_and_exit
|
141
|
+
end
|
142
|
+
else
|
143
|
+
print_manual_method_and_exit
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
95
147
|
def resolve_jar(project_root, logger)
|
96
148
|
development_jar_path =
|
97
149
|
project_root + '/target/scala-2.11/s3_website.jar'
|
@@ -99,11 +151,11 @@ def resolve_jar(project_root, logger)
|
|
99
151
|
project_root + "/s3_website-#{S3Website::VERSION}.jar",
|
100
152
|
(ENV['TMPDIR'] || '/tmp') + "/s3_website-#{S3Website::VERSION}.jar"
|
101
153
|
]
|
102
|
-
found_jar = ([development_jar_path] + released_jar_lookup_paths)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
154
|
+
found_jar = ([development_jar_path] + released_jar_lookup_paths).
|
155
|
+
select { |jar_path|
|
156
|
+
File.exists? jar_path
|
157
|
+
}.
|
158
|
+
first
|
107
159
|
jar_file =
|
108
160
|
if found_jar
|
109
161
|
found_jar
|
data/build.sbt
CHANGED
data/changelog.md
CHANGED
@@ -8,6 +8,11 @@ This project uses [Semantic Versioning](http://semver.org).
|
|
8
8
|
|
9
9
|
* Faster uploads for extra large sites
|
10
10
|
|
11
|
+
Use a local database for calculating diffs. This removes the need to read all
|
12
|
+
the files of the website, when you call the `s3_website push` command.
|
13
|
+
|
14
|
+
Use proper multithreading with JVM threads.
|
15
|
+
|
11
16
|
* Simulate deployments with `push --dry-run`
|
12
17
|
|
13
18
|
* Support CloudFront invalidations when the site contains over 3000 files
|
@@ -22,10 +27,16 @@ This project uses [Semantic Versioning](http://semver.org).
|
|
22
27
|
|
23
28
|
* Fault tolerance – do not crash if one of the uploads fails
|
24
29
|
|
30
|
+
Before, the push command crashed if something unexpected happened. From now
|
31
|
+
on, s3_website will run all the operations it can, and report errors in the
|
32
|
+
end.
|
33
|
+
|
25
34
|
### Java is now required
|
26
35
|
|
27
|
-
* The `push` command is now written in Scala
|
28
|
-
|
36
|
+
* The `push` command is now written in Scala
|
37
|
+
|
38
|
+
This means that you need Java 1.6 or above to run the command `s3_website
|
39
|
+
push`.
|
29
40
|
|
30
41
|
### Removed features
|
31
42
|
|
@@ -38,7 +49,8 @@ This project uses [Semantic Versioning](http://semver.org).
|
|
38
49
|
s3_website always deletes the files that are on the S3 bucket but not on the local file system.
|
39
50
|
Use the settings `ignore_on_server` and `exclude_from_upload` to control the retained files.
|
40
51
|
|
41
|
-
* You can no longer use this gem as a Ruby library
|
52
|
+
* You can no longer use this gem as a Ruby library. You can migrate by calling
|
53
|
+
the `s3_website push --site=x --config-dir=y` system command from your Ruby code.
|
42
54
|
|
43
55
|
* `gzip_zopfli: true`
|
44
56
|
|
data/lib/s3_website/version.rb
CHANGED
@@ -9,6 +9,7 @@ import s3.website.S3.{SuccessfulDelete, PushSuccessReport, SuccessfulUpload}
|
|
9
9
|
import com.amazonaws.auth.BasicAWSCredentials
|
10
10
|
import java.net.URI
|
11
11
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
12
|
+
import s3.website.model.Config.awsCredentials
|
12
13
|
|
13
14
|
object CloudFront {
|
14
15
|
def invalidate(invalidationBatch: InvalidationBatch, distributionId: String, attempt: Attempt = 1)
|
@@ -63,8 +64,7 @@ object CloudFront {
|
|
63
64
|
def reportMessage = s"Failed to invalidate the CloudFront distribution (${error.getMessage})"
|
64
65
|
}
|
65
66
|
|
66
|
-
def awsCloudFrontClient(config: Config) =
|
67
|
-
new AmazonCloudFrontClient(new BasicAWSCredentials(config.s3_id, config.s3_secret))
|
67
|
+
def awsCloudFrontClient(config: Config) = new AmazonCloudFrontClient(awsCredentials(config))
|
68
68
|
|
69
69
|
def toInvalidationBatches(pushSuccessReports: Seq[PushSuccessReport])(implicit config: Config): Seq[InvalidationBatch] = {
|
70
70
|
val invalidationPaths: Seq[String] = {
|
@@ -21,6 +21,7 @@ import scala.concurrent.duration.TimeUnit
|
|
21
21
|
import java.util.concurrent.TimeUnit.SECONDS
|
22
22
|
import s3.website.S3.SuccessfulUpload.humanizeUploadSpeed
|
23
23
|
import java.io.FileInputStream
|
24
|
+
import s3.website.model.Config.awsCredentials
|
24
25
|
|
25
26
|
object S3 {
|
26
27
|
|
@@ -105,7 +106,7 @@ object S3 {
|
|
105
106
|
None // We are not interested in tracking durations of PUT requests that don't contain data. Redirect is an example of such request.
|
106
107
|
}
|
107
108
|
|
108
|
-
def awsS3Client(config: Config) = new AmazonS3Client(
|
109
|
+
def awsS3Client(config: Config) = new AmazonS3Client(awsCredentials(config))
|
109
110
|
|
110
111
|
def resolveS3Files(nextMarker: Option[String] = None, alreadyResolved: Seq[S3File] = Nil, attempt: Attempt = 1)
|
111
112
|
(implicit config: Config, s3Settings: S3Setting, ec: ExecutionContextExecutor, logger: Logger, pushMode: PushMode):
|
@@ -4,10 +4,11 @@ import scala.util.{Failure, Try}
|
|
4
4
|
import scala.collection.JavaConversions._
|
5
5
|
import s3.website.Ruby.rubyRuntime
|
6
6
|
import s3.website.ErrorReport
|
7
|
+
import com.amazonaws.auth.{AWSCredentialsProvider, BasicAWSCredentials, DefaultAWSCredentialsProviderChain}
|
7
8
|
|
8
9
|
case class Config(
|
9
|
-
s3_id: String,
|
10
|
-
s3_secret: String,
|
10
|
+
s3_id: Option[String], // If undefined, use IAM Roles (http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-roles.html)
|
11
|
+
s3_secret: Option[String], // If undefined, use IAM Roles (http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-roles.html)
|
11
12
|
s3_bucket: String,
|
12
13
|
s3_endpoint: S3Endpoint,
|
13
14
|
max_age: Option[Either[Int, Map[String, Int]]],
|
@@ -23,6 +24,20 @@ case class Config(
|
|
23
24
|
)
|
24
25
|
|
25
26
|
object Config {
|
27
|
+
|
28
|
+
def awsCredentials(config: Config): AWSCredentialsProvider = {
|
29
|
+
val credentialsFromConfigFile = for {
|
30
|
+
s3_id <- config.s3_id
|
31
|
+
s3_secret <- config.s3_secret
|
32
|
+
} yield new BasicAWSCredentials(s3_id, s3_secret)
|
33
|
+
credentialsFromConfigFile.fold(new DefaultAWSCredentialsProviderChain: AWSCredentialsProvider)(credentials =>
|
34
|
+
new AWSCredentialsProvider {
|
35
|
+
def getCredentials = credentials
|
36
|
+
def refresh() = {}
|
37
|
+
}
|
38
|
+
)
|
39
|
+
}
|
40
|
+
|
26
41
|
def loadOptionalBooleanOrStringSeq(key: String)(implicit unsafeYaml: UnsafeYaml): Either[ErrorReport, Option[Either[Boolean, Seq[String]]]] = {
|
27
42
|
val yamlValue = for {
|
28
43
|
optionalValue <- loadOptionalValue(key)
|
@@ -31,8 +31,8 @@ object Site {
|
|
31
31
|
case Success(yamlObject) =>
|
32
32
|
implicit val unsafeYaml = UnsafeYaml(yamlObject)
|
33
33
|
val config: Either[ErrorReport, Config] = for {
|
34
|
-
s3_id <-
|
35
|
-
s3_secret <-
|
34
|
+
s3_id <- loadOptionalString("s3_id").right
|
35
|
+
s3_secret <- loadOptionalString("s3_secret").right
|
36
36
|
s3_bucket <- loadRequiredString("s3_bucket").right
|
37
37
|
s3_endpoint <- loadEndpoint.right
|
38
38
|
max_age <- loadMaxAge.right
|
@@ -591,10 +591,9 @@ class S3WebsiteSpec extends Specification {
|
|
591
591
|
}
|
592
592
|
|
593
593
|
"push locally unchanged files that are missing from S3" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
|
594
|
-
|
594
|
+
setLocalFileWithContent(("file.txt", "first run"))
|
595
595
|
push
|
596
|
-
|
597
|
-
setLocalFileWithContentAndLastModified(("file.txt", "contents", new Date(1400000000000L)))
|
596
|
+
removeAllFilesFromS3()
|
598
597
|
push
|
599
598
|
sentPutObjectRequests.length must equalTo(2) // Even though we use the local db, we should notice that someone else has deleted file.txt
|
600
599
|
}
|
@@ -702,6 +701,10 @@ class S3WebsiteSpec extends Specification {
|
|
702
701
|
}
|
703
702
|
}
|
704
703
|
|
704
|
+
def removeAllFilesFromS3() {
|
705
|
+
setS3Files(Nil: _*) // This corresponds to the situation where the S3 bucket is empty
|
706
|
+
}
|
707
|
+
|
705
708
|
def uploadFailsAndThenSucceeds(implicit howManyFailures: Int, callCount: AtomicInteger = new AtomicInteger(0)) {
|
706
709
|
doAnswer(temporaryFailure(classOf[PutObjectResult]))
|
707
710
|
.when(amazonS3Client)
|
@@ -795,15 +798,12 @@ class S3WebsiteSpec extends Specification {
|
|
795
798
|
|
796
799
|
trait EmptySite extends Directories {
|
797
800
|
type LocalFileWithContent = (String, String)
|
798
|
-
type LocalFileWithContentAndLastModified = (String, String, Date)
|
799
801
|
|
800
|
-
val localFilesWithContent: mutable.Set[
|
802
|
+
val localFilesWithContent: mutable.Set[LocalFileWithContent] = mutable.Set()
|
801
803
|
def setLocalFile(fileName: String) = setLocalFileWithContent((fileName, ""))
|
802
804
|
def setLocalFiles(fileNames: String*) = fileNames foreach setLocalFile
|
803
805
|
def setLocalFileWithContent(fileNameAndContent: LocalFileWithContent) =
|
804
|
-
|
805
|
-
def setLocalFileWithContentAndLastModified(fileNameAndContentAndLastModified: LocalFileWithContentAndLastModified) =
|
806
|
-
localFilesWithContent += fileNameAndContentAndLastModified
|
806
|
+
localFilesWithContent += fileNameAndContent
|
807
807
|
def setLocalFilesWithContent(fileNamesAndContent: LocalFileWithContent*) = fileNamesAndContent foreach setLocalFileWithContent
|
808
808
|
var config = ""
|
809
809
|
val baseConfig =
|
@@ -816,14 +816,13 @@ class S3WebsiteSpec extends Specification {
|
|
816
816
|
implicit def cliArgs: CliArgs = siteWithFilesAndContent(config, localFilesWithContent)
|
817
817
|
def pushMode: PushMode // Represents the --dry-run switch
|
818
818
|
|
819
|
-
private def siteWithFilesAndContent(config: String = "", localFilesWithContent: mutable.Set[
|
819
|
+
private def siteWithFilesAndContent(config: String = "", localFilesWithContent: mutable.Set[LocalFileWithContent]): CliArgs = {
|
820
820
|
localFilesWithContent.foreach {
|
821
|
-
case (filePath, content
|
821
|
+
case (filePath, content) =>
|
822
822
|
val file = new File(siteDirectory, filePath)
|
823
823
|
forceMkdir(file.getParentFile)
|
824
824
|
file.createNewFile()
|
825
825
|
write(file, content)
|
826
|
-
file.setLastModified(lastModified.getTime)
|
827
826
|
}
|
828
827
|
localFilesWithContent.clear() // Once we've persisted the changes on the disk, clear the queue. I.e., keep the state where it should be – on the disk.
|
829
828
|
buildCliArgs(config)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: s3_website_monadic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.36
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lauri Lehmijoki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|