s3_website_monadic 0.0.29 → 0.0.30

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: e67a3b50d099328d1163b4ab28bb19ab5ea70bb2
4
- data.tar.gz: 87cb81ba31dcc20734987b90d194ea95a5d7d84a
3
+ metadata.gz: a489b6afb931e8384aa6d656acd8bf23a0c366be
4
+ data.tar.gz: 12234bf6f3689a83ad37a61c7029ce80900e2a67
5
5
  SHA512:
6
- metadata.gz: dde895e0d896fca1d47dfa8bb5b2ed599f698c037ef4c3cb870cbe4cfe86603964b0eb87fed90e2d45f1253b79a59600f995653fcbe31470a95e955a5544d074
7
- data.tar.gz: 479a0f6a3c586f26833a0fc2076bddbcc94618b2b40de911422337e874a3fa4c2018be98662e19571dfa81fab508776e9333f888b455a9eac66962d42218fbc4
6
+ metadata.gz: bbc33e009ccbe805fe72697433d59fd4c82691cc9edd3e95d4e8277fceb159715f8b0d4566fa2c1e85d092c20cab2eb37178e0cfde0db1fc2103682f90653a67
7
+ data.tar.gz: 6c8b915eab3220b1a7148d0912c267dfe7b05ac653367b033e83c9c6144d027d22d18ae2ec820314415b46433b0ec5335616a8f2cda730c46bab28577dbc8528
@@ -44,13 +44,11 @@ class Cli < Thor
44
44
  option(
45
45
  :site,
46
46
  :type => :string,
47
- :default => 'infer automatically',
48
- :desc => "The directory where your website files are. When not defined, s3_website will look for the site in #{S3Website::Paths.site_paths.join(' or ')}."
47
+ :desc => "The directory where your website files are. When not defined, s3_website will look for the site in either _site or public/output."
49
48
  )
50
49
  option(
51
50
  :config_dir,
52
51
  :type => :string,
53
- :default => Dir.pwd,
54
52
  :desc => "The directory where your config file is. When not defined, s3_website will look in the current working directory."
55
53
  )
56
54
  option(
@@ -74,11 +72,10 @@ class Cli < Thor
74
72
  call_dir = Dir.pwd
75
73
  project_root = File.expand_path(File.dirname(__FILE__)+ '/..')
76
74
  logger = Logger.new(options[:verbose])
77
- Dir.chdir(project_root) {
78
- jar_file = resolve_jar project_root, logger
79
- # Then run it
80
- run_s3_website_jar(jar_file, call_dir, logger)
81
- }
75
+ # Find the jar
76
+ jar_file = resolve_jar project_root, logger
77
+ # Then run it
78
+ run_s3_website_jar(jar_file, call_dir, logger)
82
79
  end
83
80
 
84
81
  desc 'cfg SUBCOMMAND ...ARGS', 'Operate on the config file'
@@ -86,15 +83,7 @@ class Cli < Thor
86
83
  end
87
84
 
88
85
  def run_s3_website_jar(jar_file, call_dir, logger)
89
- site_path = File.expand_path(S3Website::Paths.infer_site_path options[:site], call_dir)
90
- config_dir = File.expand_path options[:config_dir]
91
-
92
- args = [
93
- "--site=#{site_path}",
94
- "--config-dir=#{config_dir}",
95
- "#{'--verbose' if options[:verbose]}",
96
- "#{'--dry-run' if options[:dry_run]}"
97
- ].join ' '
86
+ args = ARGV.join(' ').sub('push', '')
98
87
  logger.debug_msg "Using #{jar_file}"
99
88
  if system("java -cp #{jar_file} s3.website.Push #{args}")
100
89
  exit 0
@@ -121,7 +110,9 @@ def resolve_jar(project_root, logger)
121
110
  else
122
111
  is_development = File.exists?(project_root + '/.git')
123
112
  if is_development
124
- system "./sbt assembly"
113
+ Dir.chdir(project_root) {
114
+ system "./sbt assembly"
115
+ }
125
116
  development_jar_path
126
117
  else
127
118
  download_jar(released_jar_lookup_paths, logger)
data/changelog.md CHANGED
@@ -4,8 +4,11 @@ This project uses [Semantic Versioning](http://semver.org).
4
4
 
5
5
  ## 2.0.0
6
6
 
7
- This release contains backward breaking changes. Please read the section below
8
- for more info.
7
+ ### New features
8
+
9
+ * Simulate deployments with `push --dry-run`
10
+
11
+ * Support CloudFront invalidations when the site contains over 3000 files
9
12
 
10
13
  ### Java is now required
11
14
 
@@ -1,3 +1,3 @@
1
1
  module S3Website
2
- VERSION = '0.0.29'
2
+ VERSION = '0.0.30'
3
3
  end
data/lib/s3_website.rb CHANGED
@@ -2,6 +2,3 @@ require 'rubygems'
2
2
  require 'thor'
3
3
 
4
4
  require File.dirname(__FILE__) + "/s3_website/version"
5
- require File.dirname(__FILE__) + "/s3_website/paths"
6
- require File.dirname(__FILE__) + "/s3_website/jekyll"
7
- require File.dirname(__FILE__) + "/s3_website/nanoc"
@@ -25,6 +25,8 @@ import s3.website.CloudFront.CloudFrontSetting
25
25
  import s3.website.S3.SuccessfulUpload
26
26
  import s3.website.CloudFront.FailedInvalidation
27
27
  import scala.Int
28
+ import java.io.File
29
+ import com.lexicalscope.jewel.cli.CliFactory.parseArguments
28
30
 
29
31
  object Push {
30
32
 
@@ -185,35 +187,44 @@ object Push {
185
187
  trait CliArgs {
186
188
  import com.lexicalscope.jewel.cli.Option
187
189
 
188
- @Option def site: String
189
- @Option(longName = Array("config-dir")) def configDir: String
190
+ @Option(defaultToNull = true) def site: String
191
+ @Option(longName = Array("config-dir"), defaultToNull = true) def configDir: String
190
192
  @Option def verbose: Boolean
191
193
  @Option(longName = Array("dry-run")) def dryRun: Boolean
192
194
  }
193
195
 
194
196
  def main(args: Array[String]) {
195
- val cliArgs = CliFactory.parseArguments(classOf[CliArgs], args:_*)
197
+ implicit val cliArgs = parseArguments(classOf[CliArgs], args:_*)
196
198
  implicit val s3Settings = S3Setting()
197
199
  implicit val cloudFrontSettings = CloudFrontSetting()
200
+ implicit val workingDirectory = new File(System.getProperty("user.dir")).getAbsoluteFile
201
+ System exit push
202
+ }
203
+
204
+ def push(implicit cliArgs: CliArgs, s3Settings: S3Setting, cloudFrontSettings: CloudFrontSetting, workingDirectory: File): ExitCode = {
198
205
  implicit val logger: Logger = new Logger(cliArgs.verbose)
199
206
  implicit val pushMode = new PushMode {
200
207
  def dryRun = cliArgs.dryRun
201
208
  }
202
- val errorOrPushStatus = push(siteInDirectory = cliArgs.site, withConfigDirectory = cliArgs.configDir)
203
- errorOrPushStatus.left foreach (err => logger.fail(s"Could not load the site: ${err.reportMessage}"))
204
- System exit errorOrPushStatus.fold(_ => 1, pushStatus => pushStatus)
205
- }
206
209
 
207
- def push(siteInDirectory: String, withConfigDirectory: String)
208
- (implicit s3Settings: S3Setting, cloudFrontSettings: CloudFrontSetting, logger: Logger, pushMode: PushMode) =
209
- loadSite(withConfigDirectory + "/s3_website.yml", siteInDirectory)
210
- .right
211
- .map {
212
- implicit site =>
213
- val threadPool = newFixedThreadPool(site.config.concurrency_level)
214
- implicit val executor = fromExecutor(threadPool)
215
- val pushStatus = pushSite
216
- threadPool.shutdownNow()
217
- pushStatus
210
+ val errorOrSiteDir: ErrorOrFile =
211
+ Option(cliArgs.site).fold(Ssg.findSiteDirectory(workingDirectory))(siteDirFromCli => Right(new File(siteDirFromCli)))
212
+ def errorOrSite(siteInDirectory: File): Either[ErrorReport, Site] =
213
+ loadSite(Option(cliArgs.configDir).getOrElse(workingDirectory.getPath) + "/s3_website.yml", siteInDirectory.getAbsolutePath)
214
+
215
+ val errorOrPushStatus = for {
216
+ siteInDirectory <- errorOrSiteDir.right
217
+ loadedSite <- errorOrSite(siteInDirectory).right
218
+ } yield {
219
+ implicit val site = loadedSite
220
+ val threadPool = newFixedThreadPool(site.config.concurrency_level)
221
+ implicit val executor = fromExecutor(threadPool)
222
+ val pushStatus = pushSite
223
+ threadPool.shutdownNow()
224
+ pushStatus
218
225
  }
226
+
227
+ errorOrPushStatus.left foreach (err => logger.fail(s"Could not load the site: ${err.reportMessage}"))
228
+ errorOrPushStatus fold((err: ErrorReport) => 1, pushStatus => pushStatus)
229
+ }
219
230
  }
@@ -0,0 +1,33 @@
1
+ package s3.website.model
2
+
3
+ import java.io.File
4
+ import s3.website.{ErrorOrFile, ErrorReport}
5
+
6
+ // ssg = static site generator
7
+ trait Ssg {
8
+ def outputDirectory: String
9
+ }
10
+
11
+ object Ssg {
12
+ val automaticallySupportedSiteGenerators = Jekyll :: Nanoc :: Nil
13
+
14
+ val notFoundErrorReport =
15
+ new ErrorReport {
16
+ def reportMessage =
17
+ """|Could not find a website in any of the pre-defined directories.
18
+ |Specify the website location with the --site=path argument and try again.""".stripMargin
19
+ }
20
+
21
+ def findSiteDirectory(workingDirectory: File): ErrorOrFile =
22
+ LocalFile.recursiveListFiles(workingDirectory).find { file =>
23
+ file.isDirectory && automaticallySupportedSiteGenerators.exists(_.outputDirectory == file.getName)
24
+ }.fold(Left(notFoundErrorReport): ErrorOrFile)(Right(_))
25
+ }
26
+
27
+ case object Jekyll extends Ssg {
28
+ def outputDirectory = "_site"
29
+ }
30
+
31
+ case object Nanoc extends Ssg {
32
+ def outputDirectory = "public/output"
33
+ }
@@ -6,6 +6,7 @@ import s3.website.Utils._
6
6
  import s3.website.S3.{PushSuccessReport, PushFailureReport}
7
7
  import com.amazonaws.AmazonServiceException
8
8
  import s3.website.model.{Config, Site}
9
+ import java.io.File
9
10
 
10
11
  package object website {
11
12
  trait Report {
@@ -94,4 +95,6 @@ package object website {
94
95
  }
95
96
 
96
97
  implicit def site2Config(implicit site: Site): Config = site.config
98
+
99
+ type ErrorOrFile = Either[ErrorReport, File]
97
100
  }
@@ -1,6 +1,6 @@
1
1
  package s3.website
2
2
 
3
- import org.specs2.mutable.{After, Specification}
3
+ import org.specs2.mutable.{BeforeAfter, After, Specification}
4
4
  import s3.website.model._
5
5
  import org.specs2.specification.Scope
6
6
  import org.apache.commons.io.FileUtils
@@ -10,7 +10,6 @@ import org.mockito.{Mockito, Matchers, ArgumentCaptor}
10
10
  import org.mockito.Mockito._
11
11
  import com.amazonaws.services.s3.AmazonS3
12
12
  import com.amazonaws.services.s3.model._
13
- import scala.concurrent.ExecutionContext.Implicits.global
14
13
  import scala.concurrent.duration._
15
14
  import s3.website.S3.S3Setting
16
15
  import scala.collection.JavaConversions._
@@ -22,25 +21,26 @@ import com.amazonaws.services.cloudfront.model.{CreateInvalidationResult, Create
22
21
  import org.mockito.stubbing.Answer
23
22
  import org.mockito.invocation.InvocationOnMock
24
23
  import java.util.concurrent.atomic.AtomicInteger
25
- import org.apache.commons.io.FileUtils.write
24
+ import org.apache.commons.io.FileUtils.{forceDelete, forceMkdir, write}
26
25
  import scala.collection.mutable
26
+ import s3.website.Push.CliArgs
27
27
 
28
28
  class S3WebsiteSpec extends Specification {
29
29
 
30
30
  "gzip: true" should {
31
- "update a gzipped S3 object if the contents has changed" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
31
+ "update a gzipped S3 object if the contents has changed" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
32
32
  config = "gzip: true"
33
33
  setLocalFileWithContent(("styles.css", "<h1>hi again</h1>"))
34
34
  setS3Files(S3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
35
- Push.pushSite
35
+ Push.push
36
36
  sentPutObjectRequest.getKey must equalTo("styles.css")
37
37
  }
38
38
 
39
- "not update a gzipped S3 object if the contents has not changed" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
39
+ "not update a gzipped S3 object if the contents has not changed" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
40
40
  config = "gzip: true"
41
41
  setLocalFileWithContent(("styles.css", "<h1>hi</h1>"))
42
42
  setS3Files(S3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
43
- Push.pushSite
43
+ Push.push
44
44
  noUploadsOccurred must beTrue
45
45
  }
46
46
  }
@@ -49,159 +49,159 @@ class S3WebsiteSpec extends Specification {
49
49
  gzip:
50
50
  - .xml
51
51
  """ should {
52
- "update a gzipped S3 object if the contents has changed" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
52
+ "update a gzipped S3 object if the contents has changed" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
53
53
  config = """
54
54
  |gzip:
55
55
  | - .xml
56
56
  """.stripMargin
57
57
  setLocalFileWithContent(("file.xml", "<h1>hi again</h1>"))
58
58
  setS3Files(S3File("file.xml", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
59
- Push.pushSite
59
+ Push.push
60
60
  sentPutObjectRequest.getKey must equalTo("file.xml")
61
61
  }
62
62
  }
63
63
 
64
64
  "push" should {
65
- "not upload a file if it has not changed" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
65
+ "not upload a file if it has not changed" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
66
66
  setLocalFileWithContent(("index.html", "<div>hello</div>"))
67
67
  setS3Files(S3File("index.html", md5Hex("<div>hello</div>")))
68
- Push.pushSite
68
+ Push.push
69
69
  noUploadsOccurred must beTrue
70
70
  }
71
71
 
72
- "update a file if it has changed" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
72
+ "update a file if it has changed" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
73
73
  setLocalFileWithContent(("index.html", "<h1>old text</h1>"))
74
74
  setS3Files(S3File("index.html", md5Hex("<h1>new text</h1>")))
75
- Push.pushSite
75
+ Push.push
76
76
  sentPutObjectRequest.getKey must equalTo("index.html")
77
77
  }
78
78
 
79
- "create a file if does not exist on S3" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
79
+ "create a file if does not exist on S3" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
80
80
  setLocalFile("index.html")
81
- Push.pushSite
81
+ Push.push
82
82
  sentPutObjectRequest.getKey must equalTo("index.html")
83
83
  }
84
84
 
85
- "delete files that are on S3 but not on local file system" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
85
+ "delete files that are on S3 but not on local file system" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
86
86
  setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
87
- Push.pushSite
87
+ Push.push
88
88
  sentDelete must equalTo("old.html")
89
89
  }
90
90
 
91
- "try again if the upload fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
91
+ "try again if the upload fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
92
92
  setLocalFile("index.html")
93
93
  uploadFailsAndThenSucceeds(howManyFailures = 5)
94
- Push.pushSite
94
+ Push.push
95
95
  verify(amazonS3Client, times(6)).putObject(Matchers.any(classOf[PutObjectRequest]))
96
96
  }
97
97
 
98
- "not try again if the upload fails on because of invalid credentials" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
98
+ "not try again if the upload fails on because of invalid credentials" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
99
99
  setLocalFile("index.html")
100
100
  when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow {
101
101
  val e = new AmazonServiceException("your credentials are incorrect")
102
102
  e.setStatusCode(403)
103
103
  e
104
104
  }
105
- Push.pushSite
105
+ Push.push
106
106
  verify(amazonS3Client, times(1)).putObject(Matchers.any(classOf[PutObjectRequest]))
107
107
  }
108
108
 
109
- "try again if the delete fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
109
+ "try again if the delete fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
110
110
  setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
111
111
  deleteFailsAndThenSucceeds(howManyFailures = 5)
112
- Push.pushSite
112
+ Push.push
113
113
  verify(amazonS3Client, times(6)).deleteObject(Matchers.anyString(), Matchers.anyString())
114
114
  }
115
115
 
116
- "try again if the object listing fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
116
+ "try again if the object listing fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
117
117
  setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
118
118
  objectListingFailsAndThenSucceeds(howManyFailures = 5)
119
- Push.pushSite
119
+ Push.push
120
120
  verify(amazonS3Client, times(6)).listObjects(Matchers.any(classOf[ListObjectsRequest]))
121
121
  }
122
122
  }
123
123
 
124
124
  "push with CloudFront" should {
125
- "invalidate the updated CloudFront items" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
125
+ "invalidate the updated CloudFront items" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
126
126
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
127
127
  setLocalFiles("css/test.css", "articles/index.html")
128
128
  setOutdatedS3Keys("css/test.css", "articles/index.html")
129
- Push.pushSite
129
+ Push.push
130
130
  sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/css/test.css" :: "/articles/index.html" :: Nil).sorted)
131
131
  }
132
132
 
133
- "not send CloudFront invalidation requests on new objects" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
133
+ "not send CloudFront invalidation requests on new objects" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
134
134
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
135
135
  setLocalFile("newfile.js")
136
- Push.pushSite
136
+ Push.push
137
137
  noInvalidationsOccurred must beTrue
138
138
  }
139
139
 
140
- "not send CloudFront invalidation requests on redirect objects" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
140
+ "not send CloudFront invalidation requests on redirect objects" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
141
141
  config = """
142
142
  |cloudfront_distribution_id: EGM1J2JJX9Z
143
143
  |redirects:
144
144
  | /index.php: index.html
145
145
  """.stripMargin
146
- Push.pushSite
146
+ Push.push
147
147
  noInvalidationsOccurred must beTrue
148
148
  }
149
149
 
150
- "retry CloudFront responds with TooManyInvalidationsInProgressException" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
150
+ "retry CloudFront responds with TooManyInvalidationsInProgressException" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
151
151
  setTooManyInvalidationsInProgress(4)
152
152
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
153
153
  setLocalFile("test.css")
154
154
  setOutdatedS3Keys("test.css")
155
- Push.pushSite must equalTo(0) // The retries should finally result in a success
155
+ Push.push must equalTo(0) // The retries should finally result in a success
156
156
  sentInvalidationRequests.length must equalTo(4)
157
157
  }
158
158
 
159
- "retry if CloudFront is temporarily unreachable" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
159
+ "retry if CloudFront is temporarily unreachable" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
160
160
  invalidationsFailAndThenSucceed(5)
161
161
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
162
162
  setLocalFile("test.css")
163
163
  setOutdatedS3Keys("test.css")
164
- Push.pushSite
164
+ Push.push
165
165
  sentInvalidationRequests.length must equalTo(6)
166
166
  }
167
167
 
168
- "encode unsafe characters in the keys" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
168
+ "encode unsafe characters in the keys" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
169
169
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
170
170
  setLocalFile("articles/arnold's file.html")
171
171
  setOutdatedS3Keys("articles/arnold's file.html")
172
- Push.pushSite
172
+ Push.push
173
173
  sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/arnold's%20file.html" :: Nil).sorted)
174
174
  }
175
175
 
176
- "invalidate the root object '/' if a top-level object is updated or deleted" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
176
+ "invalidate the root object '/' if a top-level object is updated or deleted" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
177
177
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
178
178
  setLocalFile("maybe-index.html")
179
179
  setOutdatedS3Keys("maybe-index.html")
180
- Push.pushSite
180
+ Push.push
181
181
  sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/" :: "/maybe-index.html" :: Nil).sorted)
182
182
  }
183
183
  }
184
184
 
185
185
  "cloudfront_invalidate_root: true" should {
186
- "convert CloudFront invalidation paths with the '/index.html' suffix into '/'" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
186
+ "convert CloudFront invalidation paths with the '/index.html' suffix into '/'" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
187
187
  config = """
188
188
  |cloudfront_distribution_id: EGM1J2JJX9Z
189
189
  |cloudfront_invalidate_root: true
190
190
  """.stripMargin
191
191
  setLocalFile("articles/index.html")
192
192
  setOutdatedS3Keys("articles/index.html")
193
- Push.pushSite
193
+ Push.push
194
194
  sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/" :: Nil).sorted)
195
195
  }
196
196
  }
197
197
 
198
198
  "a site with over 1000 items" should {
199
- "split the CloudFront invalidation requests into batches of 1000 items" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
199
+ "split the CloudFront invalidation requests into batches of 1000 items" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
200
200
  val files = (1 to 1002).map { i => s"lots-of-files/file-$i"}
201
201
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
202
202
  setLocalFiles(files:_*)
203
203
  setOutdatedS3Keys(files:_*)
204
- Push.pushSite
204
+ Push.push
205
205
  sentInvalidationRequests.length must equalTo(2)
206
206
  sentInvalidationRequests(0).getInvalidationBatch.getPaths.getItems.length must equalTo(1000)
207
207
  sentInvalidationRequests(1).getInvalidationBatch.getPaths.getItems.length must equalTo(2)
@@ -209,73 +209,73 @@ class S3WebsiteSpec extends Specification {
209
209
  }
210
210
 
211
211
  "push exit status" should {
212
- "be 0 all uploads succeed" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
212
+ "be 0 all uploads succeed" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
213
213
  setLocalFiles("file.txt")
214
- Push.pushSite must equalTo(0)
214
+ Push.push must equalTo(0)
215
215
  }
216
216
 
217
- "be 1 if any of the uploads fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
217
+ "be 1 if any of the uploads fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
218
218
  setLocalFiles("file.txt")
219
219
  when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow(new AmazonServiceException("AWS failed"))
220
- Push.pushSite must equalTo(1)
220
+ Push.push must equalTo(1)
221
221
  }
222
222
 
223
- "be 1 if any of the redirects fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
223
+ "be 1 if any of the redirects fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
224
224
  config = """
225
225
  |redirects:
226
226
  | index.php: /index.html
227
227
  """.stripMargin
228
228
  when(amazonS3Client.putObject(Matchers.any(classOf[PutObjectRequest]))).thenThrow(new AmazonServiceException("AWS failed"))
229
- Push.pushSite must equalTo(1)
229
+ Push.push must equalTo(1)
230
230
  }
231
231
 
232
- "be 0 if CloudFront invalidations and uploads succeed"in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
232
+ "be 0 if CloudFront invalidations and uploads succeed"in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
233
233
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
234
234
  setLocalFile("test.css")
235
235
  setOutdatedS3Keys("test.css")
236
- Push.pushSite must equalTo(0)
236
+ Push.push must equalTo(0)
237
237
  }
238
238
 
239
- "be 1 if CloudFront is unreachable or broken"in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
239
+ "be 1 if CloudFront is unreachable or broken"in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
240
240
  setCloudFrontAsInternallyBroken()
241
241
  config = "cloudfront_distribution_id: EGM1J2JJX9Z"
242
242
  setLocalFile("test.css")
243
243
  setOutdatedS3Keys("test.css")
244
- Push.pushSite must equalTo(1)
244
+ Push.push must equalTo(1)
245
245
  }
246
246
 
247
- "be 0 if upload retry succeeds" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
247
+ "be 0 if upload retry succeeds" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
248
248
  setLocalFile("index.html")
249
249
  uploadFailsAndThenSucceeds(howManyFailures = 1)
250
- Push.pushSite must equalTo(0)
250
+ Push.push must equalTo(0)
251
251
  }
252
252
 
253
- "be 1 if delete retry fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
253
+ "be 1 if delete retry fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
254
254
  setLocalFile("index.html")
255
255
  uploadFailsAndThenSucceeds(howManyFailures = 6)
256
- Push.pushSite must equalTo(1)
256
+ Push.push must equalTo(1)
257
257
  }
258
258
 
259
- "be 1 if an object listing fails" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
259
+ "be 1 if an object listing fails" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
260
260
  setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
261
261
  objectListingFailsAndThenSucceeds(howManyFailures = 6)
262
- Push.pushSite must equalTo(1)
262
+ Push.push must equalTo(1)
263
263
  }
264
264
  }
265
265
 
266
266
  "s3_website.yml file" should {
267
- "never be uploaded" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
267
+ "never be uploaded" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
268
268
  setLocalFile("s3_website.yml")
269
- Push.pushSite
269
+ Push.push
270
270
  noUploadsOccurred must beTrue
271
271
  }
272
272
  }
273
273
 
274
274
  "exclude_from_upload: string" should {
275
- "result in matching files not being uploaded" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
275
+ "result in matching files not being uploaded" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
276
276
  config = "exclude_from_upload: .DS_.*?"
277
277
  setLocalFile(".DS_Store")
278
- Push.pushSite
278
+ Push.push
279
279
  noUploadsOccurred must beTrue
280
280
  }
281
281
  }
@@ -285,23 +285,23 @@ class S3WebsiteSpec extends Specification {
285
285
  - regex
286
286
  - another_exclusion
287
287
  """ should {
288
- "result in matching files not being uploaded" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
288
+ "result in matching files not being uploaded" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
289
289
  config = """
290
290
  |exclude_from_upload:
291
291
  | - .DS_.*?
292
292
  | - logs
293
293
  """.stripMargin
294
294
  setLocalFiles(".DS_Store", "logs/test.log")
295
- Push.pushSite
295
+ Push.push
296
296
  noUploadsOccurred must beTrue
297
297
  }
298
298
  }
299
299
 
300
300
  "ignore_on_server: value" should {
301
- "not delete the S3 objects that match the ignore value" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
301
+ "not delete the S3 objects that match the ignore value" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
302
302
  config = "ignore_on_server: logs"
303
303
  setS3Files(S3File("logs/log.txt", ""))
304
- Push.pushSite
304
+ Push.push
305
305
  noDeletesOccurred must beTrue
306
306
  }
307
307
  }
@@ -311,224 +311,228 @@ class S3WebsiteSpec extends Specification {
311
311
  - regex
312
312
  - another_ignore
313
313
  """ should {
314
- "not delete the S3 objects that match the ignore value" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
314
+ "not delete the S3 objects that match the ignore value" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
315
315
  config = """
316
316
  |ignore_on_server:
317
317
  | - .*txt
318
318
  """.stripMargin
319
319
  setS3Files(S3File("logs/log.txt", ""))
320
- Push.pushSite
320
+ Push.push
321
321
  noDeletesOccurred must beTrue
322
322
  }
323
323
  }
324
324
 
325
325
  "max-age in config" can {
326
- "be applied to all files" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
326
+ "be applied to all files" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
327
327
  config = "max_age: 60"
328
328
  setLocalFile("index.html")
329
- Push.pushSite
329
+ Push.push
330
330
  sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=60")
331
331
  }
332
332
 
333
- "be applied to files that match the glob" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
333
+ "be applied to files that match the glob" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
334
334
  config = """
335
335
  |max_age:
336
336
  | "*.html": 90
337
337
  """.stripMargin
338
338
  setLocalFile("index.html")
339
- Push.pushSite
339
+ Push.push
340
340
  sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
341
341
  }
342
342
 
343
- "be applied to directories that match the glob" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
343
+ "be applied to directories that match the glob" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
344
344
  config = """
345
345
  |max_age:
346
346
  | "assets/**/*.js": 90
347
347
  """.stripMargin
348
348
  setLocalFile("assets/lib/jquery.js")
349
- Push.pushSite
349
+ Push.push
350
350
  sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=90")
351
351
  }
352
352
 
353
- "not be applied if the glob doesn't match" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
353
+ "not be applied if the glob doesn't match" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
354
354
  config = """
355
355
  |max_age:
356
356
  | "*.js": 90
357
357
  """.stripMargin
358
358
  setLocalFile("index.html")
359
- Push.pushSite
359
+ Push.push
360
360
  sentPutObjectRequest.getMetadata.getCacheControl must beNull
361
361
  }
362
362
 
363
- "be used to disable caching" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
363
+ "be used to disable caching" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
364
364
  config = "max_age: 0"
365
365
  setLocalFile("index.html")
366
- Push.pushSite
366
+ Push.push
367
367
  sentPutObjectRequest.getMetadata.getCacheControl must equalTo("no-cache; max-age=0")
368
368
  }
369
369
  }
370
370
 
371
371
  "max-age in config" should {
372
- "respect the more specific glob" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
372
+ "respect the more specific glob" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
373
373
  config = """
374
374
  |max_age:
375
375
  | "assets/*": 150
376
376
  | "assets/*.gif": 86400
377
377
  """.stripMargin
378
378
  setLocalFiles("assets/jquery.js", "assets/picture.gif")
379
- Push.pushSite
379
+ Push.push
380
380
  sentPutObjectRequests.find(_.getKey == "assets/jquery.js").get.getMetadata.getCacheControl must equalTo("max-age=150")
381
381
  sentPutObjectRequests.find(_.getKey == "assets/picture.gif").get.getMetadata.getCacheControl must equalTo("max-age=86400")
382
382
  }
383
383
  }
384
384
 
385
385
  "s3_reduced_redundancy: true in config" should {
386
- "result in uploads being marked with reduced redundancy" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
386
+ "result in uploads being marked with reduced redundancy" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
387
387
  config = "s3_reduced_redundancy: true"
388
388
  setLocalFile("file.exe")
389
- Push.pushSite
389
+ Push.push
390
390
  sentPutObjectRequest.getStorageClass must equalTo("REDUCED_REDUNDANCY")
391
391
  }
392
392
  }
393
393
 
394
394
  "s3_reduced_redundancy: false in config" should {
395
- "result in uploads being marked with the default storage class" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
395
+ "result in uploads being marked with the default storage class" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
396
396
  config = "s3_reduced_redundancy: false"
397
397
  setLocalFile("file.exe")
398
- Push.pushSite
398
+ Push.push
399
399
  sentPutObjectRequest.getStorageClass must beNull
400
400
  }
401
401
  }
402
402
 
403
403
  "redirect in config" should {
404
- "result in a redirect instruction that is sent to AWS" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
404
+ "result in a redirect instruction that is sent to AWS" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
405
405
  config = """
406
406
  |redirects:
407
407
  | index.php: /index.html
408
408
  """.stripMargin
409
- Push.pushSite
409
+ Push.push
410
410
  sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
411
411
  }
412
412
 
413
- "result in max-age=0 Cache-Control header on the object" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
413
+ "result in max-age=0 Cache-Control header on the object" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
414
414
  config = """
415
415
  |redirects:
416
416
  | index.php: /index.html
417
417
  """.stripMargin
418
- Push.pushSite
418
+ Push.push
419
419
  sentPutObjectRequest.getMetadata.getCacheControl must equalTo("max-age=0, no-cache")
420
420
  }
421
421
  }
422
422
 
423
423
  "redirect in config and an object on the S3 bucket" should {
424
- "not result in the S3 object being deleted" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
424
+ "not result in the S3 object being deleted" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
425
425
  config = """
426
426
  |redirects:
427
427
  | index.php: /index.html
428
428
  """.stripMargin
429
429
  setLocalFile("index.php")
430
430
  setS3Files(S3File("index.php", "md5"))
431
- Push.pushSite
431
+ Push.push
432
432
  noDeletesOccurred must beTrue
433
433
  }
434
434
  }
435
435
 
436
436
  "dotfiles" should {
437
- "be included in the pushed files" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
437
+ "be included in the pushed files" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
438
438
  setLocalFile(".vimrc")
439
- Push.pushSite
439
+ Push.push
440
440
  sentPutObjectRequest.getKey must equalTo(".vimrc")
441
441
  }
442
442
  }
443
443
 
444
444
  "content type inference" should {
445
- "add charset=utf-8 to all html documents" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
445
+ "add charset=utf-8 to all html documents" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
446
446
  setLocalFile("index.html")
447
- Push.pushSite
447
+ Push.push
448
448
  sentPutObjectRequest.getMetadata.getContentType must equalTo("text/html; charset=utf-8")
449
449
  }
450
450
 
451
- "add charset=utf-8 to all text documents" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
451
+ "add charset=utf-8 to all text documents" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
452
452
  setLocalFile("index.txt")
453
- Push.pushSite
453
+ Push.push
454
454
  sentPutObjectRequest.getMetadata.getContentType must equalTo("text/plain; charset=utf-8")
455
455
  }
456
456
 
457
- "add charset=utf-8 to all json documents" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
457
+ "add charset=utf-8 to all json documents" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
458
458
  setLocalFile("data.json")
459
- Push.pushSite
459
+ Push.push
460
460
  sentPutObjectRequest.getMetadata.getContentType must equalTo("application/json; charset=utf-8")
461
461
  }
462
462
 
463
- "resolve the content type from file contents" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
463
+ "resolve the content type from file contents" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
464
464
  setLocalFileWithContent(("index", "<html><body><h1>hi</h1></body></html>"))
465
- Push.pushSite
465
+ Push.push
466
466
  sentPutObjectRequest.getMetadata.getContentType must equalTo("text/html; charset=utf-8")
467
467
  }
468
468
  }
469
469
 
470
470
  "ERB in config file" should {
471
- "be evaluated" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
471
+ "be evaluated" in new AllInSameDirectory with EmptySite with MockAWS with DefaultRunMode {
472
472
  config = """
473
473
  |redirects:
474
474
  |<%= ('a'..'f').to_a.map do |t| ' '+t+ ': /'+t+'.html' end.join('\n')%>
475
475
  """.stripMargin
476
- Push.pushSite
476
+ Push.push
477
477
  sentPutObjectRequests.length must equalTo(6)
478
478
  sentPutObjectRequests.forall(_.getRedirectLocation != null) must beTrue
479
479
  }
480
480
  }
481
481
 
482
- "logging" should {
483
- "print the debug messages when --verbose is defined" in new EmptySite with VerboseLogger with MockAWS with DefaultRunMode {
484
- Push.pushSite
485
- logEntries must contain("[debg] Querying S3 files")
486
- }
487
-
488
- "not print the debug messages by default" in new EmptySite with NonVerboseLogger with MockAWS with DefaultRunMode {
489
- Push.pushSite
490
- logEntries.forall(_.contains("[debg]")) must beFalse
491
- }
492
- }
493
-
494
482
  "dry run" should {
495
- "not push updates" in new EmptySite with VerboseLogger with MockAWS with DryRunMode {
483
+ "not push updates" in new AllInSameDirectory with EmptySite with MockAWS with DryRunMode {
496
484
  setLocalFileWithContent(("index.html", "<div>new</div>"))
497
485
  setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
498
- Push.pushSite
486
+ Push.push
499
487
  noUploadsOccurred must beTrue
500
488
  }
501
489
 
502
- "not push redirects" in new EmptySite with VerboseLogger with MockAWS with DryRunMode {
490
+ "not push redirects" in new AllInSameDirectory with EmptySite with MockAWS with DryRunMode {
503
491
  config =
504
492
  """
505
493
  |redirects:
506
494
  | index.php: /index.html
507
495
  """.stripMargin
508
- Push.pushSite
496
+ Push.push
509
497
  noUploadsOccurred must beTrue
510
498
  }
511
499
 
512
- "not push deletes" in new EmptySite with VerboseLogger with MockAWS with DryRunMode {
500
+ "not push deletes" in new AllInSameDirectory with EmptySite with MockAWS with DryRunMode {
513
501
  setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
514
- Push.pushSite
502
+ Push.push
515
503
  noUploadsOccurred must beTrue
516
504
  }
517
505
 
518
- "not push new files" in new EmptySite with VerboseLogger with MockAWS with DryRunMode {
506
+ "not push new files" in new AllInSameDirectory with EmptySite with MockAWS with DryRunMode {
519
507
  setLocalFile("index.html")
520
- Push.pushSite
508
+ Push.push
521
509
  noUploadsOccurred must beTrue
522
510
  }
523
511
 
524
- "not invalidate files" in new EmptySite with VerboseLogger with MockAWS with DryRunMode {
512
+ "not invalidate files" in new AllInSameDirectory with EmptySite with MockAWS with DryRunMode {
525
513
  config = "cloudfront_invalidation_id: AABBCC"
526
514
  setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
527
- Push.pushSite
515
+ Push.push
528
516
  noInvalidationsOccurred must beTrue
529
517
  }
530
518
  }
531
519
 
520
+ "Jekyll site" should {
521
+ "be detected automatically" in new JekyllSite with EmptySite with MockAWS with DefaultRunMode {
522
+ setLocalFile("index.html")
523
+ Push.push
524
+ sentPutObjectRequests.length must equalTo(1)
525
+ }
526
+ }
527
+
528
+ "Nanoc site" should {
529
+ "be detected automatically" in new NanocSite with EmptySite with MockAWS with DefaultRunMode {
530
+ setLocalFile("index.html")
531
+ Push.push
532
+ sentPutObjectRequests.length must equalTo(1)
533
+ }
534
+ }
535
+
532
536
  trait DefaultRunMode {
533
537
  implicit def pushMode: PushMode = new PushMode {
534
538
  def dryRun = false
@@ -542,23 +546,6 @@ class S3WebsiteSpec extends Specification {
542
546
  }
543
547
 
544
548
  trait MockAWS extends MockS3 with MockCloudFront with Scope
545
-
546
- trait VerboseLogger extends LogCapturer {
547
- implicit val logger: Logger = new Logger(verboseOutput = true, logMessage = captureAndPrint)
548
- }
549
-
550
- trait NonVerboseLogger extends LogCapturer {
551
- implicit val logger: Logger = new Logger(verboseOutput = false, logMessage = captureAndPrint)
552
- }
553
-
554
- trait LogCapturer {
555
- val logEntries: mutable.Buffer[String] = mutable.Buffer()
556
-
557
- def captureAndPrint(msg: String) {
558
- logEntries += msg.replaceAll("\u001B\\[[;\\d]*m", "") // Remove ANSI coloring
559
- println(msg)
560
- }
561
- }
562
549
 
563
550
  trait MockCloudFront extends MockAWSHelper {
564
551
  val amazonCloudFrontClient = mock(classOf[AmazonCloudFront])
@@ -599,8 +586,6 @@ class S3WebsiteSpec extends Specification {
599
586
  }).when(amazonCloudFrontClient).createInvalidation(Matchers.anyObject())
600
587
  }
601
588
 
602
-
603
-
604
589
  def setCloudFrontAsInternallyBroken() {
605
590
  when(amazonCloudFrontClient.createInvalidation(Matchers.anyObject())).thenThrow(new AmazonServiceException("CloudFront is down"))
606
591
  }
@@ -699,16 +684,40 @@ class S3WebsiteSpec extends Specification {
699
684
  }
700
685
  }
701
686
 
702
- trait SiteDirectory extends After {
703
- val siteDir = new File(FileUtils.getTempDirectory, "site" + Random.nextLong())
704
- siteDir.mkdir()
687
+ trait Directories extends BeforeAfter {
688
+ implicit final val workingDirectory: File = new File(FileUtils.getTempDirectory, "s3_website_dir" + Random.nextLong())
689
+ val siteDirectory: File // Represents the --site=X option
690
+ val configDirectory: File = workingDirectory // Represents the --config-dir=X option
691
+
692
+ lazy val allDirectories = workingDirectory :: siteDirectory :: configDirectory :: Nil
693
+
694
+ def before {
695
+ allDirectories foreach forceMkdir
696
+ }
705
697
 
706
698
  def after {
707
- FileUtils.forceDelete(siteDir)
699
+ allDirectories foreach { dir =>
700
+ if (dir.exists) forceDelete(dir)
701
+ }
708
702
  }
709
703
  }
704
+
705
+ /**
706
+ * Represents the situation where the current working directory, site dir and config dir are in the same directory
707
+ */
708
+ trait AllInSameDirectory extends Directories {
709
+ val siteDirectory = workingDirectory
710
+ }
711
+
712
+ trait JekyllSite extends Directories {
713
+ val siteDirectory = new File(workingDirectory, "_site")
714
+ }
715
+
716
+ trait NanocSite extends Directories {
717
+ val siteDirectory = new File(workingDirectory, "public/output")
718
+ }
710
719
 
711
- trait EmptySite extends SiteDirectory {
720
+ trait EmptySite extends Directories {
712
721
  type LocalFileWithContent = (String, String)
713
722
 
714
723
  val localFilesWithContent: mutable.Buffer[LocalFileWithContent] = mutable.Buffer()
@@ -717,15 +726,15 @@ class S3WebsiteSpec extends Specification {
717
726
  def setLocalFileWithContent(fileNameAndContent: LocalFileWithContent) = localFilesWithContent += fileNameAndContent
718
727
  def setLocalFilesWithContent(fileNamesAndContent: LocalFileWithContent*) = fileNamesAndContent foreach setLocalFileWithContent
719
728
  var config = ""
720
- var baseConfig =
729
+ val baseConfig =
721
730
  """
722
731
  |s3_id: foo
723
732
  |s3_secret: bar
724
733
  |s3_bucket: bucket
725
734
  """.stripMargin
726
735
 
727
- implicit lazy val testSite: Site = siteWithFilesAndContent(config, localFilesWithContent)
728
- implicit def logger: Logger
736
+ implicit lazy val cliArgs: CliArgs = siteWithFilesAndContent(config, localFilesWithContent)
737
+ def pushMode: PushMode // Represents the --dry-run switch
729
738
 
730
739
  def buildSite(
731
740
  config: String = "",
@@ -735,24 +744,30 @@ class S3WebsiteSpec extends Specification {
735
744
  |s3_secret: bar
736
745
  |s3_bucket: bucket
737
746
  """.stripMargin
738
- ): Site = {
739
- val configFile = new File(siteDir, "s3_website.yml")
747
+ ): CliArgs = {
748
+ val configFile = new File(configDirectory, "s3_website.yml")
740
749
  write(configFile,
741
750
  s"""
742
751
  |$baseConfig
743
752
  |$config
744
753
  """.stripMargin
745
754
  )
746
- val errorOrSite: Either[ErrorReport, Site] = Site.loadSite(configFile.getAbsolutePath, siteDir.getAbsolutePath)(logger)
747
- errorOrSite.left.foreach (error => throw new RuntimeException(error.reportMessage))
748
- errorOrSite.right.get
755
+ new CliArgs {
756
+ override def verbose = true
757
+
758
+ override def dryRun = pushMode.dryRun
759
+
760
+ override def site = siteDirectory.getAbsolutePath
761
+
762
+ override def configDir = configDirectory.getAbsolutePath
763
+ }
749
764
  }
750
765
 
751
- def siteWithFilesAndContent(config: String = "", localFilesWithContent: Seq[LocalFileWithContent]): Site = {
766
+ def siteWithFilesAndContent(config: String = "", localFilesWithContent: Seq[LocalFileWithContent]): CliArgs = {
752
767
  localFilesWithContent.foreach {
753
768
  case (filePath, content) =>
754
- val file = new File(siteDir, filePath)
755
- FileUtils.forceMkdir(file.getParentFile)
769
+ val file = new File(siteDirectory, filePath)
770
+ forceMkdir(file.getParentFile)
756
771
  file.createNewFile()
757
772
  write(file, content)
758
773
  }
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.29
4
+ version: 0.0.30
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-12 00:00:00.000000000 Z
11
+ date: 2014-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -102,9 +102,6 @@ files:
102
102
  - build.sbt
103
103
  - changelog.md
104
104
  - lib/s3_website.rb
105
- - lib/s3_website/jekyll.rb
106
- - lib/s3_website/nanoc.rb
107
- - lib/s3_website/paths.rb
108
105
  - lib/s3_website/version.rb
109
106
  - project/assembly.sbt
110
107
  - project/build.properties
@@ -124,6 +121,7 @@ files:
124
121
  - src/main/scala/s3/website/model/Site.scala
125
122
  - src/main/scala/s3/website/model/errors.scala
126
123
  - src/main/scala/s3/website/model/push.scala
124
+ - src/main/scala/s3/website/model/ssg.scala
127
125
  - src/main/scala/s3/website/package.scala
128
126
  - src/test/scala/s3/website/S3WebsiteSpec.scala
129
127
  homepage: https://github.com/laurilehmijoki/s3_website/tree/s3_website_monadic
@@ -1,5 +0,0 @@
1
- module S3Website
2
- module Jekyll
3
- SITE_PATH = '_site'
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- module S3Website
2
- module Nanoc
3
- SITE_PATH = 'public/output'
4
- end
5
- end
@@ -1,40 +0,0 @@
1
- module S3Website
2
- class Paths
3
- def self.site_paths
4
- [Nanoc::SITE_PATH, Jekyll::SITE_PATH]
5
- end
6
-
7
- def self.infer_site_path(candidate_path, pwd)
8
- if candidate_path == 'infer automatically'
9
- infer_automatically pwd
10
- else
11
- candidate_path_if_exists candidate_path
12
- end
13
- end
14
-
15
- private
16
-
17
- def self.candidate_path_if_exists(candidate_path)
18
- raise NoWebsiteDirectoryFound.new(
19
- "Can't find a website in " + candidate_path
20
- ) unless File.exists? candidate_path
21
- candidate_path
22
- end
23
-
24
- def self.infer_automatically(pwd)
25
- site_path = site_paths.
26
- map do |site_path|
27
- pwd + '/' + site_path
28
- end.
29
- find do |site_path|
30
- File.exists? site_path
31
- end
32
- if site_path
33
- site_path
34
- else
35
- puts "Could not find a website directory. Specify one with the --site parameter."
36
- exit
37
- end
38
- end
39
- end
40
- end