expo-flubber 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f9ca5325df420e5023e9d2a4d0a4b39551a65521c46c29b3af02d71b3c014122
4
+ data.tar.gz: 292c84899d1be9d0aeab512e97fd571a2bfde750e6e077ac7105a5a586e7c8fb
5
+ SHA512:
6
+ metadata.gz: d63da0917757d2ff3f16448059b0fee2eddbbae66b6df84ed1c8fbe4c993897c1e160ce7cf3c1990dc19b3ce19d346141226deae12f9ac737d30eb84c8cc1718
7
+ data.tar.gz: 45b4c088837d516346dcb24daaf72d779913ae1bb91e17335ca32f57ab79b1a8b503e91463f99a579d6e303132631b1654197bf15b55bc180f38bcbebbf946bc
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/cli"
4
+
5
+ Flubber::CLI.start(ARGV)
@@ -0,0 +1,23 @@
1
+ module Flubber
2
+ module ApiToken
3
+ API_TOKEN_LOCATION = "#{ENV["HOME"]}/.flubber"
4
+
5
+ def self.set(api_token)
6
+ File.open(API_TOKEN_LOCATION, "w") do |file|
7
+ file.puts "#{api_token}"
8
+ end
9
+ end
10
+
11
+ def self.get
12
+ return unless api_token_exists?
13
+ file = File.open(API_TOKEN_LOCATION)
14
+ api_token = file.readlines[0].chomp
15
+ file.close
16
+ api_token
17
+ end
18
+
19
+ def self.api_token_exists?
20
+ File.exists?(API_TOKEN_LOCATION)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Flubber
2
+ API_URL = "https://flubber-api.herokuapp.com"
3
+ end
@@ -0,0 +1,44 @@
1
+ module Flubber
2
+ module App
3
+ S3_BUCKET_NAME = "expo-flubber"
4
+ APP_INFO_LOCATION = ".flubber"
5
+
6
+ def self.set(flubber_app)
7
+ file = File.open(APP_INFO_LOCATION, "w+")
8
+ file.puts "#{flubber_app["id"]}"
9
+ file.puts "#{flubber_app["name"].downcase.gsub(" ", "-").gsub(/\s+/, "")}"
10
+ file.puts "#{flubber_app["uuid"]}"
11
+ file.close
12
+ end
13
+
14
+ def self.set?
15
+ File.exists?(APP_INFO_LOCATION)
16
+ end
17
+
18
+ def self.id
19
+ file = File.open(APP_INFO_LOCATION)
20
+ lines = file.readlines.map(&:chomp)
21
+ file.close
22
+ lines[0]
23
+ end
24
+
25
+ def self.name
26
+ file = File.open(APP_INFO_LOCATION)
27
+ lines = file.readlines.map(&:chomp)
28
+ file.close
29
+ lines[1]
30
+ end
31
+
32
+ def self.uuid
33
+ file = File.open(APP_INFO_LOCATION)
34
+ lines = file.readlines.map(&:chomp)
35
+ file.close
36
+ lines[2]
37
+ end
38
+
39
+ def self.bundles_public_url
40
+ @bundles_public_url ||= \
41
+ "https://#{S3_BUCKET_NAME}.s3.amazonaws.com/#{uuid}/#{name}"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,71 @@
1
+ require "faraday"
2
+ require "JSON"
3
+ require "mimemagic"
4
+
5
+ require "app"
6
+ require "api_url"
7
+
8
+ module Flubber
9
+ class AwsFileUpload
10
+ def self.upload(file_path:, aws_destination:)
11
+ new(file_path, aws_destination).upload
12
+ end
13
+
14
+ def initialize(file_path, aws_destination)
15
+ @file_path = file_path
16
+ @aws_destination = aws_destination
17
+ end
18
+
19
+ def upload
20
+ connection.post("/", payload)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :file_path, :aws_destination
26
+
27
+ def connection
28
+ @connection ||= Faraday.new(presigned_post["url"]) do |f|
29
+ f.request :multipart
30
+ f.request :url_encoded
31
+ f.adapter :net_http
32
+ end
33
+ end
34
+
35
+ def payload
36
+ @payload ||= presigned_post["fields"].merge(
37
+ :file => Faraday::UploadIO.new(file_path, content_type),
38
+ )
39
+ end
40
+
41
+ def presigned_post
42
+ @presigned_post ||= JSON.parse(Faraday.post(
43
+ "#{API_URL}/presigned_posts",
44
+ {
45
+ "app_uuid": app_uuid,
46
+ "aws_directory": aws_destination,
47
+ "content_type": content_type,
48
+ },
49
+ {
50
+ "Authorization" => "Bearer #{api_token}"
51
+ }
52
+ ).body)
53
+ end
54
+
55
+ def api_token
56
+ @api_token ||= ApiToken.get
57
+ end
58
+
59
+ def app_uuid
60
+ @app_uuid ||= App.uuid
61
+ end
62
+
63
+ def content_type
64
+ @content_type ||= begin
65
+ MimeMagic.by_path(file_path).type
66
+ rescue
67
+ "binary/octet-stream"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,37 @@
1
+ require "aws_file_upload"
2
+
3
+ module Flubber
4
+ class AwsFolderUpload
5
+ IGNORED_FILES = [".DS_Store"]
6
+
7
+ def self.upload(folder_name:, aws_destination:)
8
+ new(folder_name, aws_destination).upload
9
+ end
10
+
11
+ def initialize(folder_name, aws_destination)
12
+ @folder_name = folder_name
13
+ @aws_destination = aws_destination
14
+ end
15
+
16
+ def upload
17
+ file_paths.each do |file_path|
18
+ AwsFileUpload.upload(
19
+ file_path: file_path,
20
+ aws_destination: aws_destination,
21
+ )
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :folder_name, :aws_destination
28
+
29
+ def file_paths
30
+ @file_paths ||= Dir
31
+ .children("./#{folder_name}")
32
+ .filter { |file| !IGNORED_FILES.include?(file) }
33
+ .map { |file| File.expand_path("#{folder_name}/#{file}") }
34
+ .filter { |file| !File.directory?(file) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ require "thor"
2
+
3
+ require "login"
4
+ require "deploy"
5
+
6
+ module Flubber
7
+ class CLI < Thor
8
+
9
+ desc "login", "login to flubber"
10
+ def login
11
+ Login.login
12
+ end
13
+
14
+ desc "deploy", "deploy an over the air update"
15
+ def deploy
16
+ Login.login
17
+ Deploy.deploy
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,165 @@
1
+ require "git"
2
+ require "tty-spinner"
3
+ require "active_support"
4
+ require "active_support/core_ext"
5
+
6
+ require "app"
7
+ require "api_url"
8
+ require "api_token"
9
+ require "aws_folder_upload"
10
+
11
+ module Flubber
12
+ class Deploy
13
+ FILE_ALL_EXPO_APPS_HAVE = ".expo"
14
+ FOLDERS_TO_UPLOAD = [{
15
+ name: "dist",
16
+ aws_destination: "/",
17
+ }, {
18
+ name: "dist/assets",
19
+ aws_destination: "assets",
20
+ }, {
21
+ name: "dist/bundles",
22
+ aws_destination: "bundles",
23
+ }]
24
+
25
+ def self.deploy
26
+ new.deploy
27
+ end
28
+
29
+ def deploy
30
+ unless working_directory_is_an_expo_project?
31
+ puts "Failed. Run me in an expo project."
32
+ return
33
+ end
34
+ remove_bundle
35
+ set_flubber_app
36
+ puts "Using app #{App.name}"
37
+ generate_bundle
38
+ upload_bundle
39
+ remove_bundle
40
+ log_deploy
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :selection
46
+
47
+ def working_directory_is_an_expo_project?
48
+ @working_directory_is_an_expo_project ||= \
49
+ File.exist?(FILE_ALL_EXPO_APPS_HAVE)
50
+ end
51
+
52
+ def set_flubber_app
53
+ return if flubber_app_is_set?
54
+ ask_for_which_flubber_app_to_use
55
+ end
56
+
57
+ def flubber_app_is_set?
58
+ App.set?
59
+ end
60
+
61
+ def flubber_apps
62
+ @flubber_apps ||= JSON.parse(Faraday.get(
63
+ "#{API_URL}/apps",
64
+ {},
65
+ { "Authorization" => "Bearer #{api_token}" },
66
+ ).body)
67
+ end
68
+
69
+ def print_existing_app_options
70
+ flubber_apps.each_with_index do |app, index|
71
+ puts "#{index + 1}. #{app["name"]}"
72
+ end
73
+ end
74
+
75
+ def print_new_app_option
76
+ puts "#{flubber_apps.count + 1}. Create A New Flubber App"
77
+ end
78
+
79
+ def existing_app_selected?
80
+ selection >= 1 && selection <= flubber_apps.count
81
+ end
82
+
83
+ def new_app_selected?
84
+ selection == flubber_apps.count + 1
85
+ end
86
+
87
+ def use_existing_app_selection
88
+ flubber_app_index = selection - 1
89
+ App.set(flubber_apps[flubber_app_index])
90
+ end
91
+
92
+ def create_new_app
93
+ print "Name your app: "
94
+ name = STDIN.gets.chomp
95
+ app = JSON.parse(Faraday.post(
96
+ "#{API_URL}/apps",
97
+ { "name": name },
98
+ { "Authorization" => "Bearer #{api_token}" },
99
+ ).body)
100
+ App.set(app)
101
+ end
102
+
103
+ def ask_for_which_flubber_app_to_use
104
+ print_existing_app_options
105
+ print_new_app_option
106
+ loop do
107
+ print "Which app? "
108
+ @selection = STDIN.gets.chomp.to_i
109
+ if existing_app_selected?
110
+ use_existing_app_selection
111
+ break
112
+ elsif new_app_selected?
113
+ create_new_app
114
+ break
115
+ end
116
+ puts "Not an option"
117
+ end
118
+ end
119
+
120
+ def generate_bundle
121
+ bundling_spinner = TTY::Spinner.new(
122
+ "[:spinner] Bundling ...",
123
+ format: :dots,
124
+ )
125
+ bundling_spinner.auto_spin
126
+ `expo export --public-url #{App.bundles_public_url}`
127
+ bundling_spinner.stop('Done!')
128
+ end
129
+
130
+ def upload_bundle
131
+ uploading_spinner = TTY::Spinner.new(
132
+ "[:spinner] Uploading ...",
133
+ format: :dots,
134
+ )
135
+ uploading_spinner.auto_spin
136
+ FOLDERS_TO_UPLOAD.each do |folder|
137
+ AwsFolderUpload.upload(
138
+ folder_name: folder[:name],
139
+ aws_destination: folder[:aws_destination],
140
+ )
141
+ end
142
+ uploading_spinner.stop('Done!')
143
+ end
144
+
145
+ def log_deploy
146
+ Faraday.post(
147
+ "#{API_URL}/apps/#{App.id}/updates",
148
+ { "commit_sha": sha },
149
+ { "Authorization" => "Bearer #{api_token}" },
150
+ )
151
+ end
152
+
153
+ def sha
154
+ @sha ||= Git.open(".").log.first.sha
155
+ end
156
+
157
+ def api_token
158
+ @api_token ||= ApiToken.get
159
+ end
160
+
161
+ def remove_bundle
162
+ `rm -rf dist`
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,66 @@
1
+ require "io/console"
2
+ require "faraday"
3
+ require "JSON"
4
+ require "active_support"
5
+ require "active_support/core_ext"
6
+
7
+ require "api_url"
8
+ require "api_token"
9
+
10
+ module Flubber
11
+ class Login
12
+ def self.login
13
+ new.login
14
+ end
15
+
16
+ def login
17
+ if logged_in?
18
+ puts "Already logged in."
19
+ else
20
+ get_credentials
21
+ get_api_token
22
+ save_api_token
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :phone_number, :password, :api_token
29
+
30
+ def get_credentials
31
+ print "Phone Number: "
32
+ @phone_number = STDIN.gets.chomp
33
+ print "Password: "
34
+ @password = IO::console.getpass
35
+ end
36
+
37
+ def get_api_token
38
+ if api_token_response.status == 201
39
+ @api_token = JSON.parse(api_token_response.body)["api_token"]
40
+ end
41
+ end
42
+
43
+ def save_api_token
44
+ if api_token.present?
45
+ ApiToken.set(api_token)
46
+ puts "Success"
47
+ else
48
+ puts "Incorrect credentials"
49
+ end
50
+ end
51
+
52
+ def api_token_response
53
+ @api_token_response ||= Faraday.post(
54
+ "#{API_URL}/user_token",
55
+ {
56
+ "phone_number": phone_number,
57
+ "password": password,
58
+ },
59
+ )
60
+ end
61
+
62
+ def logged_in?
63
+ @logged_in ||= ApiToken.get.present?
64
+ end
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: expo-flubber
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Pachulski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: git
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.7.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.7.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.3.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.3.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: tty-spinner
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.0.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: json
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.3.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.3.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: mimemagic
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.3.5
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.3.5
111
+ - !ruby/object:Gem::Dependency
112
+ name: io
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.0.1
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.0.1
125
+ description: Update your react native expo apps Over-The-Air without size restriction
126
+ email: nick@pachulski.me
127
+ executables:
128
+ - flubber
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - bin/flubber
133
+ - lib/api_token.rb
134
+ - lib/api_url.rb
135
+ - lib/app.rb
136
+ - lib/aws_file_upload.rb
137
+ - lib/aws_folder_upload.rb
138
+ - lib/cli.rb
139
+ - lib/deploy.rb
140
+ - lib/login.rb
141
+ homepage: http://pachulski.me
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubygems_version: 3.0.3
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: OTA Updates Without Size Restriction
164
+ test_files: []