s3_website_monadic 0.0.35 → 0.0.36

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a588d29f02ef097314aa16ec55674fa6eccbe09
4
- data.tar.gz: caf04222e53fdd2cacdadcc6897f9d59cfd7b850
3
+ metadata.gz: e7c4690fe767a7e3acb2814561d70516adb70a37
4
+ data.tar.gz: 58348dcb010d4f4ba30132c26e7cc8008e97f4f9
5
5
  SHA512:
6
- metadata.gz: 61d6891706690b50a98966cd6860e0680ccd9e478f5a1ec2fc0c0c92a0af6e42777c546486d826177b85c73a3911eeabce9ec4b1a32f2b8a645778f99d7ad771
7
- data.tar.gz: 0d49bbcd125387e1d9fcbd8346e784ff4f4957e30ecc0839fcb2714680a68c3a4bc9f7ddfacfc63238fd542d7dc013d63af61ed4a2d09d8d2cdb25cf17c273d3
6
+ metadata.gz: ede3a36d8687fe7d9794405810b8425c01a7ce4c5b19c196d77ee7f1737fb2913d0d970767e3d04b0c5cfa4580ca14cd5f430d55b0a532d93523d44edca504e1
7
+ data.tar.gz: bf26d74d0b3179386e48fd12d42ae9d58a51f7bf94b4a7ad84014513736b461c5cf4b7f2345cecbe799c611d20ee68dbd7a03a2706d9d124fdb7949187b9a681
data/.travis.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  language: scala
2
2
  scala:
3
- 2.11.0
3
+ 2.11.1
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
- ## Simulating deployments
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
- ## Development
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
- ### Tests
370
+ ## Development
367
371
 
368
- * Install bundler and run `bundle install`
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
- # Vagrant
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`
@@ -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
- .select { |jar_path|
104
- File.exists? jar_path
105
- }
106
- .first
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
@@ -4,7 +4,7 @@ name := "s3_website"
4
4
 
5
5
  version := "0.0.1"
6
6
 
7
- scalaVersion := "2.11.0"
7
+ scalaVersion := "2.11.1"
8
8
 
9
9
  scalacOptions += "-feature"
10
10
 
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. This means that you need Java 1.6
28
- or above to run the command `s3_website push`.
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
 
@@ -1,3 +1,3 @@
1
1
  module S3Website
2
- VERSION = '0.0.35'
2
+ VERSION = '0.0.36'
3
3
  end
@@ -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(new BasicAWSCredentials(config.s3_id, config.s3_secret))
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 <- loadRequiredString("s3_id").right
35
- s3_secret <- loadRequiredString("s3_secret").right
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
- setLocalFileWithContentAndLastModified(("file.txt", "contents", new Date(1400000000000L)))
594
+ setLocalFileWithContent(("file.txt", "first run"))
595
595
  push
596
- // Simulate the situation where someone else has deleted file.txt, and we push for the second time
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[LocalFileWithContentAndLastModified] = 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
- setLocalFileWithContentAndLastModified((fileNameAndContent._1, fileNameAndContent._2, new Date))
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[LocalFileWithContentAndLastModified]): CliArgs = {
819
+ private def siteWithFilesAndContent(config: String = "", localFilesWithContent: mutable.Set[LocalFileWithContent]): CliArgs = {
820
820
  localFilesWithContent.foreach {
821
- case (filePath, content, lastModified) =>
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.35
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-28 00:00:00.000000000 Z
11
+ date: 2014-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor