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 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