beam_up 0.5.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +124 -0
- data/lib/beam_up/cli.rb +187 -0
- data/lib/beam_up/configuration.rb +48 -0
- data/lib/beam_up/core.rb +68 -0
- data/lib/beam_up/errors.rb +7 -0
- data/lib/beam_up/providers/aws_s3.rb +44 -0
- data/lib/beam_up/providers/base.rb +25 -0
- data/lib/beam_up/providers/bunny.rb +78 -0
- data/lib/beam_up/providers/digital_ocean_spaces.rb +46 -0
- data/lib/beam_up/providers/hetzner.rb +42 -0
- data/lib/beam_up/providers/neocities.rb +72 -0
- data/lib/beam_up/providers/netlify.rb +125 -0
- data/lib/beam_up/providers/s3_compatible.rb +63 -0
- data/lib/beam_up/providers/sftp.rb +81 -0
- data/lib/beam_up/providers/statichost.rb +87 -0
- data/lib/beam_up/providers/transporter.rb +54 -0
- data/lib/beam_up/providers.rb +17 -0
- data/lib/beam_up/result.rb +26 -0
- data/lib/beam_up/version.rb +3 -0
- data/lib/beam_up.rb +31 -0
- metadata +108 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "beam_up/providers/s3_compatible"
|
|
4
|
+
|
|
5
|
+
module BeamUp
|
|
6
|
+
module Providers
|
|
7
|
+
class Hetzner < S3Compatible
|
|
8
|
+
class Config
|
|
9
|
+
def self.config_keys = %w[access_key secret_key region bucket]
|
|
10
|
+
|
|
11
|
+
attr_accessor :access_key, :secret_key, :region, :bucket
|
|
12
|
+
|
|
13
|
+
def with(options)
|
|
14
|
+
self.access_key = options[:access_key]
|
|
15
|
+
self.secret_key = options[:secret_key]
|
|
16
|
+
self.region = options[:region] || "fsn1"
|
|
17
|
+
self.bucket = options[:bucket]
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def validate!
|
|
22
|
+
raise ConfigurationError, "Access key must be set" unless access_key
|
|
23
|
+
raise ConfigurationError, "Secret key must be set" unless secret_key
|
|
24
|
+
raise ConfigurationError, "Bucket must be set" unless bucket
|
|
25
|
+
raise ConfigurationError, "Invalid region: #{region}. Valid regions: fsn1, nbg1, hel1, ash, hil, sin" unless VALID_REGIONS.include?(region)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
VALID_REGIONS = %w[ash fsn1 hel1 hil nbg1 sin]
|
|
32
|
+
|
|
33
|
+
def bucket_name = @configuration.bucket
|
|
34
|
+
|
|
35
|
+
def endpoint = "https://#{@configuration.region}.your-objectstorage.com"
|
|
36
|
+
|
|
37
|
+
def public_url = "https://#{bucket_name}.#{@configuration.region}.your-objectstorage.com"
|
|
38
|
+
|
|
39
|
+
def provider_name = "Hetzner"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
|
|
6
|
+
module BeamUp
|
|
7
|
+
module Providers
|
|
8
|
+
class Neocities < Base
|
|
9
|
+
API_HOST = "https://neocities.org"
|
|
10
|
+
|
|
11
|
+
class Config
|
|
12
|
+
def self.config_keys = %w[api_key site_name]
|
|
13
|
+
|
|
14
|
+
attr_accessor :api_key, :site_name
|
|
15
|
+
|
|
16
|
+
def with(options)
|
|
17
|
+
self.api_key = options[:api_key]
|
|
18
|
+
self.site_name = options[:site_name]
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate!
|
|
23
|
+
raise ConfigurationError, "API key must be set" unless api_key
|
|
24
|
+
raise ConfigurationError, "Site name must be set" unless site_name
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def deploy!(path)
|
|
29
|
+
@path = path
|
|
30
|
+
|
|
31
|
+
upload_files
|
|
32
|
+
|
|
33
|
+
Result.new(
|
|
34
|
+
provider: "Neocities",
|
|
35
|
+
deploy_id: Time.now.to_i.to_s,
|
|
36
|
+
url: "https://#{@configuration.site_name}.neocities.org"
|
|
37
|
+
)
|
|
38
|
+
rescue => error
|
|
39
|
+
Result.new(provider: "Neocities", error: error.message)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def upload_files
|
|
45
|
+
uri = URI("#{API_HOST}/api/upload")
|
|
46
|
+
|
|
47
|
+
request = Net::HTTP::Post.new(uri)
|
|
48
|
+
request.basic_auth(@configuration.site_name, @configuration.api_key)
|
|
49
|
+
|
|
50
|
+
form_data = files_to_deploy.map do |file|
|
|
51
|
+
relative_path = file.delete_prefix("#{@path}/")
|
|
52
|
+
|
|
53
|
+
[relative_path, File.read(file)]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
request.set_form(form_data, "multipart/form-data")
|
|
57
|
+
|
|
58
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
59
|
+
http.request(request)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
case response.code.to_i
|
|
63
|
+
when 200..299
|
|
64
|
+
json_response = JSON.parse(response.body)
|
|
65
|
+
raise DeploymentError, json_response["message"] if json_response["result"] == "error"
|
|
66
|
+
else
|
|
67
|
+
raise DeploymentError, "Neocities API error: #{response.code} #{response.body}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "cgi/escape"
|
|
5
|
+
require "net/http"
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
8
|
+
module BeamUp
|
|
9
|
+
module Providers
|
|
10
|
+
class Netlify < Base
|
|
11
|
+
class Config
|
|
12
|
+
def self.config_keys = %w[api_token project_id]
|
|
13
|
+
|
|
14
|
+
attr_accessor :api_token, :project_id
|
|
15
|
+
|
|
16
|
+
def with(options)
|
|
17
|
+
self.api_token = options[:api_token]
|
|
18
|
+
self.project_id = options[:project_id]
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate!
|
|
23
|
+
raise ConfigurationError, "API token must be set" unless api_token
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def deploy!(path)
|
|
28
|
+
@path = path
|
|
29
|
+
digested_files = digested files_to_deploy
|
|
30
|
+
response = post "/sites/#{project_id}/deploys", files: digested_files
|
|
31
|
+
|
|
32
|
+
upload(files_to_deploy, response)
|
|
33
|
+
|
|
34
|
+
Result.new(
|
|
35
|
+
provider: "Netlify",
|
|
36
|
+
deploy_id: response["id"],
|
|
37
|
+
url: response["deploy_ssl_url"] || response["ssl_url"]
|
|
38
|
+
)
|
|
39
|
+
rescue => error
|
|
40
|
+
Result.new(provider: "Netlify", error: error.message)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def digested(files)
|
|
46
|
+
files.to_h do |file|
|
|
47
|
+
relative_path = "/#{file.delete_prefix("#{@path}/")}"
|
|
48
|
+
|
|
49
|
+
[relative_path, Digest::SHA1.hexdigest(File.read(file))]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def project_id
|
|
54
|
+
return @configuration.project_id if @configuration.project_id.to_s != ""
|
|
55
|
+
|
|
56
|
+
site_name = "#{File.basename(Dir.pwd)}-#{Time.now.to_i}"
|
|
57
|
+
site = post("/sites", {name: [site_name, SecureRandom.hex(4)].join("-")})
|
|
58
|
+
@created_project_id = site["id"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def upload(files, response)
|
|
62
|
+
required_shas = response["required"] || []
|
|
63
|
+
return if required_shas.empty?
|
|
64
|
+
|
|
65
|
+
required_shas.each.with_index(1) do |sha, index|
|
|
66
|
+
file_path = file_map_from(files)[sha]
|
|
67
|
+
next if file_path.nil? || file_path.empty?
|
|
68
|
+
|
|
69
|
+
relative_path = "/" + file_path.delete_prefix("#{@path}/")
|
|
70
|
+
escaped_path = CGI.escape(relative_path.delete_prefix("/"))
|
|
71
|
+
|
|
72
|
+
put("/deploys/#{response["id"]}/files/#{escaped_path}", File.read(file_path))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def message
|
|
77
|
+
if @configuration.project_id.to_s.empty?
|
|
78
|
+
<<~MSG
|
|
79
|
+
Successfully deployed to Netlify.
|
|
80
|
+
|
|
81
|
+
New site created with ID: #{@created_project_id}
|
|
82
|
+
Add this to your .beam_up.yml to skip site creation in future deploys:
|
|
83
|
+
project_id: #{@created_project_id}
|
|
84
|
+
MSG
|
|
85
|
+
else
|
|
86
|
+
"Successfully deployed to Netlify"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def file_map_from(files)
|
|
91
|
+
files.to_h { [Digest::SHA1.hexdigest(File.read(it)), it] }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def post(path, data)
|
|
95
|
+
request(:post, path, data.to_json, "application/json")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def put(path, data)
|
|
99
|
+
request(:put, path, data, "application/octet-stream")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def request(method, path, body = nil, content_type = nil)
|
|
103
|
+
uri = URI("https://api.netlify.com/api/v1#{path}")
|
|
104
|
+
|
|
105
|
+
request = case method
|
|
106
|
+
when :post then Net::HTTP::Post.new(uri)
|
|
107
|
+
when :put then Net::HTTP::Put.new(uri)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
request["Authorization"] = "Bearer #{@configuration.api_token}"
|
|
111
|
+
request["Content-Type"] = content_type if content_type
|
|
112
|
+
request.body = body if body
|
|
113
|
+
|
|
114
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
|
|
115
|
+
|
|
116
|
+
case response.code.to_i
|
|
117
|
+
when 200..299
|
|
118
|
+
response.body.empty? ? {} : JSON.parse(response.body)
|
|
119
|
+
else
|
|
120
|
+
raise DeploymentError, "Netlify API error: #{response.code} #{response.body}"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "aws-sdk-s3"
|
|
4
|
+
|
|
5
|
+
module BeamUp
|
|
6
|
+
module Providers
|
|
7
|
+
class S3Compatible < Base
|
|
8
|
+
def deploy!(path)
|
|
9
|
+
@path = path
|
|
10
|
+
|
|
11
|
+
files_to_deploy.each do |file|
|
|
12
|
+
upload file.delete_prefix("#{@path}/"), file
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Result.new(
|
|
16
|
+
provider: provider_name,
|
|
17
|
+
deploy_id: Time.now.to_i.to_s,
|
|
18
|
+
url: public_url
|
|
19
|
+
)
|
|
20
|
+
rescue => error
|
|
21
|
+
Result.new(provider: provider_name, error: error.message)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def upload(key, file)
|
|
27
|
+
File.open(file, "rb") do |opened_file|
|
|
28
|
+
s3_client.put_object(
|
|
29
|
+
bucket: bucket_name,
|
|
30
|
+
key: key,
|
|
31
|
+
body: opened_file,
|
|
32
|
+
acl: "public-read"
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def s3_client
|
|
38
|
+
@s3_client ||= Aws::S3::Client.new(
|
|
39
|
+
access_key_id: @configuration.access_key,
|
|
40
|
+
secret_access_key: @configuration.secret_key,
|
|
41
|
+
region: @configuration.region,
|
|
42
|
+
endpoint: endpoint
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def bucket_name
|
|
47
|
+
raise NotImplementedError, "Subclasses must implement #bucket_name"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def endpoint
|
|
51
|
+
raise NotImplementedError, "Subclasses must implement #endpoint"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def public_url
|
|
55
|
+
raise NotImplementedError, "Subclasses must implement #public_url"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def provider_name
|
|
59
|
+
raise NotImplementedError, "Subclasses must implement #provider_name"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BeamUp
|
|
4
|
+
module Providers
|
|
5
|
+
class SFTP < Base
|
|
6
|
+
class Config
|
|
7
|
+
def self.config_keys = %w[host port username password key remote_path]
|
|
8
|
+
|
|
9
|
+
attr_accessor :host, :port, :username, :password, :key, :remote_path
|
|
10
|
+
|
|
11
|
+
def with(options)
|
|
12
|
+
self.host = options[:host]
|
|
13
|
+
self.port = options[:port] || 22
|
|
14
|
+
self.username = options[:username]
|
|
15
|
+
self.password = options[:password]
|
|
16
|
+
self.key = options[:key]
|
|
17
|
+
self.remote_path = options[:remote_path]
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def validate!
|
|
22
|
+
raise ConfigurationError, "Host must be set" unless host
|
|
23
|
+
raise ConfigurationError, "Username must be set" unless username
|
|
24
|
+
raise ConfigurationError, "Remote path must be set" unless remote_path
|
|
25
|
+
raise ConfigurationError, "Password or key must be set" unless password || key
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def deploy!(path)
|
|
30
|
+
@path = path
|
|
31
|
+
|
|
32
|
+
require "net/ssh"
|
|
33
|
+
require "net/sftp"
|
|
34
|
+
|
|
35
|
+
options = {
|
|
36
|
+
password: @configuration.password,
|
|
37
|
+
encryption: ["aes256-gcm@openssh.com", "aes256-ctr", "aes192-ctr", "aes128-ctr", "twofish256-ctr", "twofish192-ctr", "twofish128-ctr"]
|
|
38
|
+
}
|
|
39
|
+
options[:keys] = [@configuration.key] if @configuration.key
|
|
40
|
+
|
|
41
|
+
Net::SFTP.start(@configuration.host, @configuration.username, options) do |sftp|
|
|
42
|
+
files_to_deploy.each do |file|
|
|
43
|
+
upload sftp, file
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Result.new(
|
|
48
|
+
provider: "SFTP",
|
|
49
|
+
deploy_id: Time.now.to_i.to_s,
|
|
50
|
+
url: "sftp://#{@configuration.host}#{@configuration.remote_path}"
|
|
51
|
+
)
|
|
52
|
+
rescue LoadError
|
|
53
|
+
raise ConfigurationError, "SFTP requires net-sftp gem. Install with: gem install net-sftp (or add to Gemfile)"
|
|
54
|
+
rescue => error
|
|
55
|
+
Result.new(provider: "SFTP", error: error.message)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def upload(sftp, file)
|
|
61
|
+
remote_path = File.join(@configuration.remote_path, file.delete_prefix("#{@path}/"))
|
|
62
|
+
|
|
63
|
+
return if unchanged?(sftp, file, remote_path)
|
|
64
|
+
|
|
65
|
+
sftp.upload(file, remote_path)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def unchanged?(sftp, file, remote_path)
|
|
69
|
+
size = File.size(file)
|
|
70
|
+
|
|
71
|
+
begin
|
|
72
|
+
remote_stat = sftp.stat!(remote_path)
|
|
73
|
+
|
|
74
|
+
remote_stat.size == size
|
|
75
|
+
rescue Net::SFTP::StatusException
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "zip"
|
|
5
|
+
require "tempfile"
|
|
6
|
+
|
|
7
|
+
module BeamUp
|
|
8
|
+
module Providers
|
|
9
|
+
class Statichost < Base
|
|
10
|
+
BUILDER_HOST = "https://builder.statichost.eu"
|
|
11
|
+
|
|
12
|
+
class Config
|
|
13
|
+
def self.config_keys = %w[api_key site_name builder_host]
|
|
14
|
+
|
|
15
|
+
attr_accessor :api_key, :site_name, :builder_host
|
|
16
|
+
|
|
17
|
+
def with(options)
|
|
18
|
+
self.api_key = options[:api_key]
|
|
19
|
+
self.site_name = options[:site_name]
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate!
|
|
24
|
+
raise ConfigurationError, "API key must be set" unless api_key
|
|
25
|
+
raise ConfigurationError, "Site name must be set" unless site_name
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def deploy!(path)
|
|
30
|
+
@path = path
|
|
31
|
+
|
|
32
|
+
zipped_file = create_zip path
|
|
33
|
+
response = upload zipped_file
|
|
34
|
+
|
|
35
|
+
Result.new(
|
|
36
|
+
provider: "Statichost",
|
|
37
|
+
deploy_id: response["id"] || Time.now.to_i.to_s,
|
|
38
|
+
url: "https://#{@configuration.site_name}.statichost.eu"
|
|
39
|
+
)
|
|
40
|
+
rescue => error
|
|
41
|
+
Result.new(provider: "Statichost", error: error.message)
|
|
42
|
+
ensure
|
|
43
|
+
zipped_file&.close!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def create_zip(path)
|
|
49
|
+
temp = Tempfile.new(["statichost", ".zip"], binmode: true)
|
|
50
|
+
|
|
51
|
+
Zip::OutputStream.open(temp) do |zip|
|
|
52
|
+
Dir.glob("#{path}/**/*").each do |file|
|
|
53
|
+
next unless File.file?(file)
|
|
54
|
+
|
|
55
|
+
relative_path = file.delete_prefix("#{path}/")
|
|
56
|
+
zip.put_next_entry(relative_path)
|
|
57
|
+
zip.write(File.read(file))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
temp.rewind
|
|
62
|
+
temp
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def upload(zipped_file)
|
|
66
|
+
uri = URI("https://builder.statichost.eu/#{@configuration.site_name}/drop")
|
|
67
|
+
|
|
68
|
+
request = Net::HTTP::Post.new(uri)
|
|
69
|
+
request["Authorization"] = "Bearer #{@configuration.api_key}"
|
|
70
|
+
request["Content-Type"] = "application/zip"
|
|
71
|
+
request["Accept"] = "application/json"
|
|
72
|
+
request.body = zipped_file.read
|
|
73
|
+
|
|
74
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
75
|
+
http.request(request)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
case response.code.to_i
|
|
79
|
+
when 200..299
|
|
80
|
+
response.body.empty? ? {} : JSON.parse(response.body)
|
|
81
|
+
else
|
|
82
|
+
raise DeploymentError, "Statichost API error: #{response.code} #{response.body}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module BeamUp
|
|
6
|
+
module Providers
|
|
7
|
+
class Transporter < Base
|
|
8
|
+
class Config
|
|
9
|
+
def self.config_keys = %w[target_directory]
|
|
10
|
+
|
|
11
|
+
attr_accessor :target_directory
|
|
12
|
+
|
|
13
|
+
def with(options)
|
|
14
|
+
self.target_directory = options[:target_directory]
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def validate!
|
|
19
|
+
raise ConfigurationError, "Target directory must be set" unless target_directory
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def deploy!(path)
|
|
24
|
+
@path = path
|
|
25
|
+
files = files_to_deploy
|
|
26
|
+
|
|
27
|
+
puts "Energizing… 🚀"
|
|
28
|
+
puts "Matter stream detected: #{files.length} files"
|
|
29
|
+
|
|
30
|
+
FileUtils.mkdir_p(@configuration.target_directory)
|
|
31
|
+
|
|
32
|
+
files.each do |file|
|
|
33
|
+
relative_path = file.sub("#{@path}/", "")
|
|
34
|
+
target_path = File.join(@configuration.target_directory, relative_path)
|
|
35
|
+
|
|
36
|
+
FileUtils.mkdir_p(File.dirname(target_path))
|
|
37
|
+
FileUtils.cp(file, target_path)
|
|
38
|
+
|
|
39
|
+
puts " Beaming: #{relative_path}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
puts "Transport complete. Files materialized at: #{@configuration.target_directory}"
|
|
43
|
+
|
|
44
|
+
Result.new(
|
|
45
|
+
provider: "Transporter",
|
|
46
|
+
deploy_id: files.length.to_s,
|
|
47
|
+
url: @configuration.target_directory
|
|
48
|
+
)
|
|
49
|
+
rescue => error
|
|
50
|
+
Result.new(provider: "Transporter", error: error.message)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "beam_up/providers/base"
|
|
4
|
+
require "beam_up/providers/aws_s3"
|
|
5
|
+
require "beam_up/providers/bunny"
|
|
6
|
+
require "beam_up/providers/digital_ocean_spaces"
|
|
7
|
+
require "beam_up/providers/hetzner"
|
|
8
|
+
require "beam_up/providers/neocities"
|
|
9
|
+
require "beam_up/providers/netlify"
|
|
10
|
+
require "beam_up/providers/sftp"
|
|
11
|
+
require "beam_up/providers/statichost"
|
|
12
|
+
require "beam_up/providers/transporter"
|
|
13
|
+
|
|
14
|
+
module BeamUp
|
|
15
|
+
module Providers
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BeamUp
|
|
4
|
+
class Result
|
|
5
|
+
attr_reader :deploy_id, :error, :provider
|
|
6
|
+
|
|
7
|
+
def initialize(provider:, deploy_id: nil, url: nil, error: nil)
|
|
8
|
+
@provider = provider
|
|
9
|
+
@deploy_id = deploy_id
|
|
10
|
+
@url = url
|
|
11
|
+
@error = error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success? = @error.nil?
|
|
15
|
+
|
|
16
|
+
def failure? = !success?
|
|
17
|
+
|
|
18
|
+
def message
|
|
19
|
+
if success?
|
|
20
|
+
"Successfully deployed to #{@provider}#{" at #{@url}" if @url}"
|
|
21
|
+
else
|
|
22
|
+
"Deployment to #{@provider} failed: #{@error}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/beam_up.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "beam_up/version"
|
|
4
|
+
require "beam_up/errors"
|
|
5
|
+
require "beam_up/providers"
|
|
6
|
+
require "beam_up/configuration"
|
|
7
|
+
require "beam_up/result"
|
|
8
|
+
require "beam_up/core"
|
|
9
|
+
require "beam_up/cli"
|
|
10
|
+
|
|
11
|
+
module BeamUp
|
|
12
|
+
PROVIDERS = {
|
|
13
|
+
"aws_s3" => Providers::AwsS3,
|
|
14
|
+
"bunny" => Providers::Bunny,
|
|
15
|
+
"digital_ocean_spaces" => Providers::DigitalOceanSpaces,
|
|
16
|
+
"hetzner" => Providers::Hetzner,
|
|
17
|
+
"neocities" => Providers::Neocities,
|
|
18
|
+
"netlify" => Providers::Netlify,
|
|
19
|
+
"sftp" => Providers::SFTP,
|
|
20
|
+
"statichost" => Providers::Statichost,
|
|
21
|
+
"transporter" => Providers::Transporter
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
def configure(&block) = Core.configure(&block)
|
|
26
|
+
|
|
27
|
+
def configuration = Core.configuration
|
|
28
|
+
|
|
29
|
+
def deploy!(path = nil, provider: nil, to: nil) = Core.deploy!(path, provider: (to || provider)&.to_s)
|
|
30
|
+
end
|
|
31
|
+
end
|