amazon_static_site 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4eb7bca91606801e7d0f5119a155b8eb88cbb07b167e669a45eb4f2ffc7800a9
4
+ data.tar.gz: 782abe744733815240cd87da5dc4af08fafe78237828987fa2e03a8502af9dae
5
+ SHA512:
6
+ metadata.gz: 896f57be534e465b6d87b27cdd7a97e0b8f0e93145b5cb726da681dd46c7c5753109cb17e5c80e0a5c6d15c97673aac2ba8ec1d13658f872f04e3dcd7350a12b
7
+ data.tar.gz: d328337e0852467e00d8bccdccc75b82ed45c15488fbd3586d927fb462eee029c35c39f84be9cca760e4349642df698d8becafaec87226f601660d68dfee4e70
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in amazon_static_site.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,131 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ amazon_static_site (0.1.2)
5
+ activesupport
6
+ aws-sdk-s3
7
+ better_tempfile
8
+ cloudflare
9
+ mime-types
10
+ nokogiri
11
+ sinatra
12
+ terminal-table
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activesupport (7.0.2.2)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 1.6, < 2)
20
+ minitest (>= 5.1)
21
+ tzinfo (~> 2.0)
22
+ async (1.30.1)
23
+ console (~> 1.10)
24
+ nio4r (~> 2.3)
25
+ timers (~> 4.1)
26
+ async-http (0.56.5)
27
+ async (>= 1.25)
28
+ async-io (>= 1.28)
29
+ async-pool (>= 0.2)
30
+ protocol-http (~> 0.22.0)
31
+ protocol-http1 (~> 0.14.0)
32
+ protocol-http2 (~> 0.14.0)
33
+ async-io (1.32.2)
34
+ async
35
+ async-pool (0.3.9)
36
+ async (>= 1.25)
37
+ async-rest (0.12.4)
38
+ async-http (~> 0.42)
39
+ protocol-http (~> 0.7)
40
+ aws-eventstream (1.2.0)
41
+ aws-partitions (1.554.0)
42
+ aws-sdk-core (3.126.2)
43
+ aws-eventstream (~> 1, >= 1.0.2)
44
+ aws-partitions (~> 1, >= 1.525.0)
45
+ aws-sigv4 (~> 1.1)
46
+ jmespath (~> 1.0)
47
+ aws-sdk-kms (1.54.0)
48
+ aws-sdk-core (~> 3, >= 3.126.0)
49
+ aws-sigv4 (~> 1.1)
50
+ aws-sdk-s3 (1.112.0)
51
+ aws-sdk-core (~> 3, >= 3.126.0)
52
+ aws-sdk-kms (~> 1)
53
+ aws-sigv4 (~> 1.4)
54
+ aws-sigv4 (1.4.0)
55
+ aws-eventstream (~> 1, >= 1.0.2)
56
+ better_tempfile (0.1.0)
57
+ cloudflare (4.3.0)
58
+ async-rest (~> 0.12.3)
59
+ coderay (1.1.3)
60
+ concurrent-ruby (1.1.9)
61
+ console (1.14.0)
62
+ fiber-local
63
+ diff-lcs (1.5.0)
64
+ fiber-local (1.0.0)
65
+ i18n (1.10.0)
66
+ concurrent-ruby (~> 1.0)
67
+ jmespath (1.6.0)
68
+ method_source (1.0.0)
69
+ mime-types (3.4.1)
70
+ mime-types-data (~> 3.2015)
71
+ mime-types-data (3.2022.0105)
72
+ minitest (5.15.0)
73
+ mustermann (1.1.1)
74
+ ruby2_keywords (~> 0.0.1)
75
+ nio4r (2.5.8)
76
+ nokogiri (1.13.1-x86_64-linux)
77
+ racc (~> 1.4)
78
+ protocol-hpack (1.4.2)
79
+ protocol-http (0.22.5)
80
+ protocol-http1 (0.14.2)
81
+ protocol-http (~> 0.22)
82
+ protocol-http2 (0.14.2)
83
+ protocol-hpack (~> 1.4)
84
+ protocol-http (~> 0.18)
85
+ pry (0.14.1)
86
+ coderay (~> 1.1)
87
+ method_source (~> 1.0)
88
+ racc (1.6.0)
89
+ rack (2.2.3)
90
+ rack-protection (2.2.0)
91
+ rack
92
+ rake (10.5.0)
93
+ rspec (3.11.0)
94
+ rspec-core (~> 3.11.0)
95
+ rspec-expectations (~> 3.11.0)
96
+ rspec-mocks (~> 3.11.0)
97
+ rspec-core (3.11.0)
98
+ rspec-support (~> 3.11.0)
99
+ rspec-expectations (3.11.0)
100
+ diff-lcs (>= 1.2.0, < 2.0)
101
+ rspec-support (~> 3.11.0)
102
+ rspec-mocks (3.11.0)
103
+ diff-lcs (>= 1.2.0, < 2.0)
104
+ rspec-support (~> 3.11.0)
105
+ rspec-support (3.11.0)
106
+ ruby2_keywords (0.0.5)
107
+ sinatra (2.2.0)
108
+ mustermann (~> 1.0)
109
+ rack (~> 2.2)
110
+ rack-protection (= 2.2.0)
111
+ tilt (~> 2.0)
112
+ terminal-table (3.0.2)
113
+ unicode-display_width (>= 1.1.1, < 3)
114
+ tilt (2.0.10)
115
+ timers (4.3.3)
116
+ tzinfo (2.0.4)
117
+ concurrent-ruby (~> 1.0)
118
+ unicode-display_width (2.1.0)
119
+
120
+ PLATFORMS
121
+ ruby
122
+
123
+ DEPENDENCIES
124
+ amazon_static_site!
125
+ bundler
126
+ pry
127
+ rake (~> 10.0)
128
+ rspec (~> 3.0)
129
+
130
+ BUNDLED WITH
131
+ 2.3.7
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../lib/amazon_static_site.rb"
3
+ begin
4
+ require "pry"
5
+ rescue LoadError
6
+ end
7
+
8
+ case ARGV[0].to_s
9
+ when /^(serve|s|start|server)$/i
10
+ AmazonStaticSite::Server.new.start
11
+ when /^(generate|g|new|n)$/i
12
+ AmazonStaticSite::Generator.new.start
13
+ when /^(deploy|d|push|p|production)$/i
14
+ AmazonStaticSite::Worker.new(file: ARGV[1], folder: ARGV[2])
15
+ else
16
+ puts "amazon_static_site wrong parameters".red
17
+ puts "How to use:".yellow
18
+ puts "amazon_static_site generate <NAME> # create new application using template in the gem"
19
+ puts "amazon_static_site server # to start server and develop your HTML/CSS/JS locally in browser"
20
+ puts "amazon_static_site deploy <CONFIG> <PATH-TO-PUBLIC> # to start server and develop your HTML/CSS/JS locally in browser"
21
+ puts "example:"
22
+ puts "amazon_static_site deploy site-folder/config.yml site-folder/public"
23
+ end
@@ -0,0 +1,13 @@
1
+ module AmazonStaticSite
2
+ module Client
3
+ class Base
4
+ attr_reader :service
5
+ delegate :config, to: :service
6
+
7
+ def initialize(service)
8
+ @service = service
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,76 @@
1
+ module AmazonStaticSite
2
+ module Client
3
+ class Cloudflare < Base
4
+ def configure_dns
5
+ in_cloudflare do |connection|
6
+ account_id = cloudflare_account_id(connection)
7
+
8
+ puts " find or create #{domain_for_cloudflare} in account: #{account_id}"
9
+
10
+ zone = fetch_zone(connection)
11
+ unless zone
12
+ connection.zones.create(domain_for_cloudflare, { id: account_id })
13
+ zone = fetch_zone(connection)
14
+ end
15
+
16
+ puts "Creating DNS records:".yellow
17
+ puts " - #{service.s3.s3_primary}"
18
+ puts " - #{service.s3.s3_secondary}"
19
+
20
+ puts "Generating ...".yellow
21
+ zone.dns_records.to_a.each do |record|
22
+ if record.type == 'CNAME' && (record.name == config.domain.secondary || record.name == config.domain.primary)
23
+ record.delete
24
+ end
25
+ end
26
+ www = zone.dns_records.create('CNAME', 'www', service.s3.s3_primary, proxied: true)
27
+ non_www = zone.dns_records.create('CNAME', '.', service.s3.s3_secondary, proxied: true)
28
+
29
+ zone_info = []
30
+ zone_info << [
31
+ zone.value[:name],
32
+ zone.value[:name_servers].join('; '),
33
+ zone.value[:status]
34
+ ]
35
+ puts
36
+ puts "Domain settings (you need to chang nameservers for your domain)".green
37
+ puts Terminal::Table.new(headings: ["Name", "Name Servers", "Status"], rows: zone_info)
38
+ puts
39
+ puts "DNS settings".green
40
+ summary_info = []
41
+ [www, non_www].each do |e|
42
+ summary_info << [ e.type, e.name, ' => ', e.content, e.proxied ]
43
+ end
44
+ puts Terminal::Table.new(headings: ['Type', 'Name', nil, 'Destination', 'Proxied'], rows: summary_info)
45
+ puts "=> Done!".green
46
+ end
47
+ end
48
+
49
+ def primary_www?
50
+ config.domain.primary =~ /^www\./
51
+ end
52
+
53
+ def domain_for_cloudflare
54
+ primary_www? ? config.domain.secondary : config.domain.primary
55
+ end
56
+
57
+ private
58
+
59
+ def fetch_zone(connection)
60
+ connection.zones.find_by_name(domain_for_cloudflare)
61
+ end
62
+
63
+ def in_cloudflare
64
+ # use Cloadflare API client
65
+ ::Cloudflare.connect(key: config.cloudflare.api_key, email: config.cloudflare.email) do |connection|
66
+ yield(connection)
67
+ end
68
+ end
69
+
70
+ def cloudflare_account_id(connection)
71
+ connection.accounts.first.value[:id]
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,78 @@
1
+ module AmazonStaticSite
2
+ module Client
3
+ class S3 < Base
4
+ def create_buckets
5
+ primary
6
+ secondary
7
+ end
8
+
9
+ def primary
10
+ @primary ||= get_bucket(config.domain.primary)
11
+ end
12
+
13
+ def secondary
14
+ @secondary ||= get_bucket(config.domain.secondary)
15
+ end
16
+
17
+ def publish_static_website_on_s3
18
+ puts "Publish static web site:".yellow
19
+ s3_client.put_bucket_website(
20
+ bucket: config.domain.primary,
21
+ website_configuration: {
22
+ index_document: {
23
+ suffix: "index.html"
24
+ },
25
+ error_document: {
26
+ key: "error.html"
27
+ }
28
+ }
29
+ )
30
+ puts " Primary #{config.domain.primary} DONE"
31
+ s3_client.put_bucket_website(
32
+ bucket: config.domain.secondary,
33
+ website_configuration: {
34
+ redirect_all_requests_to: {
35
+ host_name: config.domain.primary,
36
+ protocol: "https",
37
+ },
38
+ }
39
+ )
40
+ puts " Secondary #{config.domain.secondary} DONE"
41
+ end
42
+
43
+ def s3_primary; s3_domain(config.domain.primary); end
44
+ def s3_secondary; s3_domain(config.domain.secondary); end
45
+
46
+ private
47
+
48
+ def get_bucket(name)
49
+ s3_resource.bucket(name).exists? ?
50
+ s3_resource.bucket(name) : s3_client.create_bucket(bucket: name, acl: 'public-read')
51
+ end
52
+
53
+ def s3_client
54
+ @s3_client ||= Aws::S3::Client.new(aws_credentials)
55
+ end
56
+
57
+ def s3_resource
58
+ @s3_resource ||= Aws::S3::Resource.new(aws_credentials)
59
+ end
60
+
61
+ def aws_credentials
62
+ {
63
+ region: config.s3.region,
64
+ access_key_id: config.s3.access_key_id,
65
+ secret_access_key: config.s3.secret_access_key
66
+ }
67
+ end
68
+
69
+ def s3_domain(domain)
70
+ [
71
+ domain,
72
+ ".s3-website-#{config.s3.region}.amazonaws.com"
73
+ ].join
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,74 @@
1
+ # http://www.railsjazz.com.s3-website-us-west-1.amazonaws.com/
2
+
3
+ module AmazonStaticSite
4
+ module Client
5
+ class Upload < Base
6
+ GZIP_FILES = /\.(js|css)$/
7
+
8
+ def upload_to(destination)
9
+ result = FileList.new(config.folder + "/**/*.*")
10
+ result -= [config.file] # don not upload config file
11
+
12
+ puts "Uploading:"
13
+
14
+ total_uploaded = 0
15
+
16
+ result.each do |file|
17
+ pathname = Pathname.new(file)
18
+ basename = pathname.basename
19
+ upload_to = pathname.to_s.gsub(config.folder, "").gsub(/^\//, "")
20
+
21
+ size = File.size(file)
22
+ total_uploaded += size
23
+
24
+ puts " #{file}...#{filesize(size)}".green
25
+ upload_file(file: file, upload_to: upload_to, destination: destination)
26
+
27
+ if file.downcase =~ GZIP_FILES
28
+ temp_filename = "#{basename}.gz"
29
+ temp = BetterTempfile.new(temp_filename)
30
+ # gzip file in temp folder
31
+ Zlib::GzipWriter.open(temp.path) do |gz|
32
+ gz.mtime = File.mtime(file)
33
+ gz.orig_name = temp_filename
34
+ gz.write IO.binread(file)
35
+ end
36
+
37
+ size = File.size(temp.path)
38
+ total_uploaded += size
39
+
40
+ # print progress
41
+ print " GZip: #{temp_filename}".dark_green
42
+ print "...#{filesize(size)}\n".green
43
+
44
+ upload_file(file: temp.path, upload_to: upload_to + ".gz", destination: destination)
45
+ # delete temp file
46
+ temp.unlink
47
+ end
48
+ end
49
+ puts "Total uploaded: #{filesize(total_uploaded)}"
50
+ result
51
+ end
52
+
53
+ def upload_file(file:, upload_to:, destination:)
54
+ aws_options = {
55
+ acl: "public-read",
56
+ content_type: MIME::Types.type_for(file).first&.content_type
57
+ }
58
+ destination.object(upload_to).upload_file(file, aws_options)
59
+ end
60
+
61
+ def filesize(size)
62
+ units = ['B', 'KB', 'MB', 'GB', 'TB', 'Pb', 'EB']
63
+
64
+ return '0.0 B' if size == 0
65
+ exp = (Math.log(size) / Math.log(1024)).to_i
66
+ exp += 1 if (size.to_f / 1024 ** exp >= 1024 - 0.05)
67
+ exp = 6 if exp > 6
68
+
69
+ '%.1f %s' % [size.to_f / 1024 ** exp, units[exp]]
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,14 @@
1
+ module AmazonStaticSite
2
+ class Config
3
+ attr_reader :worker, :options, :file, :folder
4
+
5
+ delegate_missing_to :options
6
+
7
+ def initialize(file: "./config.yml", folder: "./public")
8
+ @file = file
9
+ @folder = folder.gsub(/\/$/, '')
10
+ @worker = worker
11
+ @options = JSON.parse(YAML.load_file(file).to_json, object_class: OpenStruct)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ module AmazonStaticSite
2
+ class Generator
3
+ attr_reader :folder
4
+
5
+ def initialize(folder = ARGV[1] || '.')
6
+ require 'fileutils'
7
+ @folder = folder.gsub(/\/$/, '')
8
+ end
9
+
10
+ def start
11
+ print "Creating folder #{folder} ...".green
12
+ FileUtils.mkdir_p(folder)
13
+ print "OK\n".yellow
14
+
15
+ print "Copying #{template} to #{destination} ...".green
16
+ FileUtils.copy_entry template, destination
17
+ print "OK\n".yellow
18
+
19
+ print "Generating config #{destination}/config/site.yml ...".green
20
+ generate_config
21
+ print "OK\n".yellow
22
+ end
23
+
24
+ private
25
+
26
+ def template
27
+ File.dirname(File.expand_path('..', __dir__)) + "/template"
28
+ end
29
+
30
+ def destination
31
+ Dir.pwd + "/" + folder
32
+ end
33
+
34
+ def generate_config
35
+ FileUtils.mv "#{destination}/config.yml.sample", "#{destination}/config.yml"
36
+ end
37
+
38
+ end
39
+ end
40
+
41
+
42
+
43
+
@@ -0,0 +1,5 @@
1
+ class LocalStaticSite < Sinatra::Base
2
+ get "/" do
3
+ redirect '/index.html'
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module AmazonStaticSite
2
+ class Server
3
+ attr_reader :folder
4
+
5
+ def initialize(folder = ARGV[1] || './public')
6
+ require 'rack'
7
+ @folder = folder.gsub(/\/$/, '')
8
+ end
9
+
10
+ def start(host: '0.0.0.0', port: 8080)
11
+ handler = Rack::Handler::WEBrick
12
+ app = LocalStaticSite.new
13
+ app.settings.public_folder = folder
14
+ puts "Starting server on folder: #{app.settings.public_folder} on http://#{host}:#{port}".green
15
+ handler.run app, Host: host, Port: port
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+
22
+
23
+
@@ -0,0 +1,26 @@
1
+ module AmazonStaticSite
2
+ class Service
3
+ attr_reader :config, :s3, :cloudflare, :uploader
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ @s3 = Client::S3.new(self)
8
+ @cloudflare = Client::Cloudflare.new(self)
9
+ @uploader = Client::Upload.new(self)
10
+ end
11
+
12
+ def run_s3
13
+ s3.create_buckets
14
+ s3.publish_static_website_on_s3
15
+ end
16
+
17
+ def run_upload
18
+ uploader.upload_to(s3.primary)
19
+ end
20
+
21
+ def run_cloudflare
22
+ cloudflare.configure_dns
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ class String
2
+ def red; colorize(self, "\e[1m\e[31m"); end
3
+ def green; colorize(self, "\e[1m\e[32m"); end
4
+ def dark_green; colorize(self, "\e[32m"); end
5
+ def yellow; colorize(self, "\e[1m\e[33m"); end
6
+ def blue; colorize(self, "\e[1m\e[34m"); end
7
+ def dark_blue; colorize(self, "\e[34m"); end
8
+ def pur; colorize(self, "\e[1m\e[35m"); end
9
+ def colorize(text, color_code) "#{color_code}#{text}\e[0m" end
10
+ end
@@ -0,0 +1,3 @@
1
+ module AmazonStaticSite
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,23 @@
1
+ module AmazonStaticSite
2
+ class Worker
3
+ attr_reader :config, :service
4
+
5
+ def initialize(file:, folder:)
6
+ @config = Config.new(file: file, folder: folder)
7
+ @service = Service.new(config)
8
+
9
+ puts "Starting...".yellow
10
+ puts " Config file: #{config.file}"
11
+ puts " Folder to upload: #{config.folder}"
12
+
13
+ puts "Connecting to Amazon:".yellow
14
+ service.run_s3
15
+
16
+ puts "Uploading to Amazon:".yellow
17
+ service.run_upload
18
+
19
+ puts "Configure Cloudflare:".yellow
20
+ service.run_cloudflare
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ require 'active_support/all'
2
+ require 'yaml'
3
+ require 'rake'
4
+ require 'nokogiri'
5
+ require 'aws-sdk-s3'
6
+ require 'json'
7
+ require 'cloudflare'
8
+ require 'sinatra/base'
9
+ require 'zlib'
10
+ require 'better_tempfile'
11
+ require 'mime/types'
12
+ require 'terminal-table'
13
+
14
+ require_relative './amazon_static_site/version'
15
+
16
+ module AmazonStaticSite
17
+ class Error < StandardError; end
18
+ end
19
+
20
+ require_relative './amazon_static_site/utils/string_ext.rb'
21
+ require_relative './amazon_static_site/server.rb'
22
+ require_relative './amazon_static_site/generator.rb'
23
+ require_relative './amazon_static_site/client/base.rb'
24
+ require_relative './amazon_static_site/client/s3.rb'
25
+ require_relative './amazon_static_site/client/upload.rb'
26
+ require_relative './amazon_static_site/client/cloudflare.rb'
27
+ require_relative './amazon_static_site/config.rb'
28
+ require_relative './amazon_static_site/service.rb'
29
+ require_relative './amazon_static_site/worker.rb'
30
+ require_relative './amazon_static_site/local_static_site.rb'
@@ -0,0 +1,5 @@
1
+ RSpec.describe AmazonStaticSite do
2
+ it "has a version number" do
3
+ expect(AmazonStaticSite::VERSION).not_to be nil
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ require "bundler/setup"
2
+ require "amazon_static_site"
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = ".rspec_status"
7
+
8
+ # Disable RSpec exposing methods globally on `Module` and `main`
9
+ config.disable_monkey_patching!
10
+
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ domain:
2
+ primary: "www.site.com"
3
+ secondary: "site.com"
4
+ s3:
5
+ region: us-west-1
6
+ access_key_id: "KEY"
7
+ secret_access_key: "SECRET"
8
+ cloudflare:
9
+ email: email@site.org
10
+ api_key: "API"