fastlane-plugin-apprepo 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +2 -0
- data/.gitignore +0 -0
- data/.rspec +3 -0
- data/.rubocop.yml +4 -0
- data/Gemfile +7 -1
- data/README.md +9 -2
- data/assets/RepofileDefault +34 -0
- data/assets/apprepo.png +0 -0
- data/assets/circle.key +27 -0
- data/assets/fastlane.png +0 -0
- data/assets/manifest.png +0 -0
- data/circle.yml +11 -9
- data/coverage/.last_run.json +5 -0
- data/coverage/.resultset.json +1244 -0
- data/coverage/.resultset.json.lock +0 -0
- data/fastlane-plugin-apprepo.gemspec +6 -2
- data/fastlane/Appfile +1 -0
- data/fastlane/Fastfile +42 -0
- data/fastlane/Pluginfile +1 -0
- data/fastlane/Repofile +33 -0
- data/greeter.rb +6 -0
- data/lib/fastlane/plugin/apprepo.rb +26 -7
- data/lib/fastlane/plugin/apprepo/actions/apprepo_action.rb +106 -92
- data/lib/fastlane/plugin/apprepo/actions/download_manifest.rb +40 -0
- data/lib/fastlane/plugin/apprepo/actions/init.rb +46 -0
- data/lib/fastlane/plugin/apprepo/actions/rubocop.rb +29 -0
- data/lib/fastlane/plugin/apprepo/actions/run.rb +69 -0
- data/lib/fastlane/plugin/apprepo/actions/submit.rb +41 -0
- data/lib/fastlane/plugin/apprepo/analyser.rb +35 -0
- data/lib/fastlane/plugin/apprepo/command.rb +17 -0
- data/lib/fastlane/plugin/apprepo/commands_generator.rb +121 -0
- data/lib/fastlane/plugin/apprepo/detect_values.rb +29 -0
- data/lib/fastlane/plugin/apprepo/helper/analyser.rb +35 -0
- data/lib/fastlane/plugin/apprepo/helper/commands_generator.rb +121 -0
- data/lib/fastlane/plugin/apprepo/helper/detect_values.rb +29 -0
- data/lib/fastlane/plugin/apprepo/helper/loader.rb +15 -0
- data/lib/fastlane/plugin/apprepo/helper/manifest.rb +44 -0
- data/lib/fastlane/plugin/apprepo/helper/options.rb +120 -0
- data/lib/fastlane/plugin/apprepo/helper/runner.rb +68 -0
- data/lib/fastlane/plugin/apprepo/helper/setup.rb +49 -0
- data/lib/fastlane/plugin/apprepo/helper/uploader.rb +301 -0
- data/lib/fastlane/plugin/apprepo/loader.rb +15 -0
- data/lib/fastlane/plugin/apprepo/manifest.rb +44 -0
- data/lib/fastlane/plugin/apprepo/options.rb +120 -0
- data/lib/fastlane/plugin/apprepo/runner.rb +68 -0
- data/lib/fastlane/plugin/apprepo/setup.rb +49 -0
- data/lib/fastlane/plugin/apprepo/uploader.rb +301 -0
- data/lib/fastlane/plugin/apprepo/version.rb +4 -3
- data/manifest.json +10 -0
- data/sampleapp.ipa +1 -0
- data/test +0 -0
- metadata +46 -2
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module Fastlane
|
3
|
+
module Apprepo
|
4
|
+
# This class is responsible for detecting values from IPA.
|
5
|
+
class DetectValues
|
6
|
+
def run!(options)
|
7
|
+
find_app_identifier(options)
|
8
|
+
find_version(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_app_identifier(options)
|
12
|
+
return if options[:app_identifier]
|
13
|
+
if options[:ipa]
|
14
|
+
# identifier = Apprepo::Analyser.fetch_app_identifier(options[:ipa])
|
15
|
+
end
|
16
|
+
options[:app_identifier] = identifier unless identifier.to_s.empty?
|
17
|
+
input_message = 'The Bundle Identifier of your App: '
|
18
|
+
options[:app_identifier] ||= UI.input(input_message)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_version(options)
|
22
|
+
unless options[:ipa].nil?
|
23
|
+
opt = Apprepo::Analyser.new(options)
|
24
|
+
options[:app_version] ||= opt.fetch_app_version(options[:ipa])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fastlane_core/languages'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Apprepo
|
5
|
+
# Responsible for loading language folders
|
6
|
+
module Loader
|
7
|
+
def self.language_folders(root)
|
8
|
+
Dir.glob(File.join(root, '*')).select do |path|
|
9
|
+
all = ALL_LANGUAGES.include?(File.basename(path).downcase)
|
10
|
+
File.directory?(path) && all
|
11
|
+
end.sort
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Apprepo
|
5
|
+
# Responsible for parsing and providing manifest.json
|
6
|
+
class Manifest
|
7
|
+
#
|
8
|
+
# Translated internal key names from Fastlane to Apprepo
|
9
|
+
#
|
10
|
+
|
11
|
+
attr_accessor :appcode # Apprepo Internal Code
|
12
|
+
attr_accessor :filename # IPA file name
|
13
|
+
attr_accessor :bundle_identifier # app_identifier
|
14
|
+
attr_accessor :bundle_version # app_version
|
15
|
+
attr_accessor :title # app_name
|
16
|
+
attr_accessor :subtitle # app_description
|
17
|
+
attr_accessor :notify # will send push notification / slack
|
18
|
+
|
19
|
+
def initialize(appcode)
|
20
|
+
self.appcode = appcode
|
21
|
+
UI.message('Initializing requires APPCODE. ' + self.appcode)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_json(json)
|
25
|
+
# TODO: load values from JSON
|
26
|
+
end
|
27
|
+
|
28
|
+
# Provide JSON serialized data
|
29
|
+
def manifest_as_json
|
30
|
+
structure = {
|
31
|
+
appcode: appcode,
|
32
|
+
filename: filename,
|
33
|
+
bundle_identifier: bundle_identifier,
|
34
|
+
bundle_version: bundle_version,
|
35
|
+
title: title,
|
36
|
+
subtitle: subtitle,
|
37
|
+
notify: notify
|
38
|
+
}
|
39
|
+
|
40
|
+
fputs structure
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'fastlane_core'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Apprepo
|
5
|
+
# Provides available options for the commands generator
|
6
|
+
class Options
|
7
|
+
# rubocop:disable Metrics/AbcSize
|
8
|
+
def self.available_options
|
9
|
+
[
|
10
|
+
FastlaneCore::ConfigItem.new(key: :ipa,
|
11
|
+
short_option: '-i',
|
12
|
+
optional: true,
|
13
|
+
env_name: 'APPREPO_IPA_PATH',
|
14
|
+
description: 'Path to your ipa file',
|
15
|
+
default_value: Dir['*.ipa'].first,
|
16
|
+
verify_block: proc do |value|
|
17
|
+
UI.user_error!("Could not find ipa file at path '#{value}'") unless File.exist?(value)
|
18
|
+
UI.user_error!("'#{value}' doesn't seem to be an ipa file") unless value.end_with?('.ipa')
|
19
|
+
end),
|
20
|
+
FastlaneCore::ConfigItem.new(key: :app_identifier,
|
21
|
+
short_option: '-b',
|
22
|
+
optional: false,
|
23
|
+
env_name: 'APPREPO_APP_ID',
|
24
|
+
description: 'Your bundle identifier',
|
25
|
+
default_value: ''),
|
26
|
+
FastlaneCore::ConfigItem.new(key: :app_code,
|
27
|
+
short_option: '-c',
|
28
|
+
optional: true,
|
29
|
+
env_name: 'APPREPO_APPCODE',
|
30
|
+
description: 'APPCODE value for apprepo'),
|
31
|
+
FastlaneCore::ConfigItem.new(key: :repo_url,
|
32
|
+
short_option: '-r',
|
33
|
+
optional: false,
|
34
|
+
env_name: 'APPREPO_URL',
|
35
|
+
description: 'URL of your Apprepo server'),
|
36
|
+
FastlaneCore::ConfigItem.new(key: :repo_user,
|
37
|
+
short_option: '-u',
|
38
|
+
optional: false,
|
39
|
+
env_name: 'APPREPO_USER',
|
40
|
+
description: 'USER of your Apprepo server'),
|
41
|
+
FastlaneCore::ConfigItem.new(key: :repo_password,
|
42
|
+
short_option: '-p',
|
43
|
+
optional: true,
|
44
|
+
env_name: 'APPREPO_PASSWORD',
|
45
|
+
description: 'PASSWORD for your Apprepo server (not for production)',
|
46
|
+
conflicting_options: [:repo_key],
|
47
|
+
conflict_block: proc do |value|
|
48
|
+
UI.user_error!("You can't use 'password' and '#{value.key}' options in one run.")
|
49
|
+
end),
|
50
|
+
FastlaneCore::ConfigItem.new(key: :repo_key,
|
51
|
+
short_option: '-k',
|
52
|
+
optional: false,
|
53
|
+
env_name: 'APPREPO_KEY',
|
54
|
+
description: 'RSA key for your Apprepo server',
|
55
|
+
conflicting_options: [:repo_password],
|
56
|
+
conflict_block: proc do |value|
|
57
|
+
UI.user_error!("You can't use 'repo_key' and '#{value.key}' options in one run.")
|
58
|
+
end),
|
59
|
+
FastlaneCore::ConfigItem.new(key: :repo_description,
|
60
|
+
short_option: '-d',
|
61
|
+
optional: true,
|
62
|
+
description: 'Long detailed description for your Apprepo server',
|
63
|
+
default_value: ''),
|
64
|
+
FastlaneCore::ConfigItem.new(key: :repo_summary,
|
65
|
+
short_option: '-s',
|
66
|
+
optional: true,
|
67
|
+
description: 'Short description for your Apprepo server',
|
68
|
+
default_value: ''),
|
69
|
+
FastlaneCore::ConfigItem.new(key: :manifest_path,
|
70
|
+
short_option: '-m',
|
71
|
+
description: 'Path to the folder containing the metadata files',
|
72
|
+
optional: true),
|
73
|
+
FastlaneCore::ConfigItem.new(key: :repo_title,
|
74
|
+
short_option: '-a',
|
75
|
+
description: 'Title of the app',
|
76
|
+
optional: false),
|
77
|
+
FastlaneCore::ConfigItem.new(key: :appcode,
|
78
|
+
short_option: '-o',
|
79
|
+
description: 'Name of the app on Apprepo',
|
80
|
+
optional: false),
|
81
|
+
FastlaneCore::ConfigItem.new(key: :skip_binary_upload,
|
82
|
+
description: 'Skip uploading an ipa or to Apprepo',
|
83
|
+
is_string: false,
|
84
|
+
default_value: false),
|
85
|
+
FastlaneCore::ConfigItem.new(key: :app_version,
|
86
|
+
short_option: '-z',
|
87
|
+
description: 'The version that should be edited or created',
|
88
|
+
optional: true),
|
89
|
+
FastlaneCore::ConfigItem.new(key: :skip_manifest,
|
90
|
+
description: "Don't upload the metadata (e.g. title, description), this will still upload screenshots",
|
91
|
+
is_string: false,
|
92
|
+
default_value: false),
|
93
|
+
FastlaneCore::ConfigItem.new(key: :notify,
|
94
|
+
description: 'Notify Apprepo users on update',
|
95
|
+
is_string: false,
|
96
|
+
default_value: false),
|
97
|
+
FastlaneCore::ConfigItem.new(key: :build_number,
|
98
|
+
short_option: '-n',
|
99
|
+
description: 'If set the given build number (already uploaded to iTC) will be used instead of the current built one',
|
100
|
+
optional: true,
|
101
|
+
conflicting_options: [:ipa],
|
102
|
+
conflict_block: proc do |value|
|
103
|
+
UI.user_error!("You can't use 'build_number' and '#{value.key}' options in one run.")
|
104
|
+
end),
|
105
|
+
|
106
|
+
# App Metadata
|
107
|
+
# Non Localised
|
108
|
+
FastlaneCore::ConfigItem.new(key: :app_icon,
|
109
|
+
description: 'Metadata: The path to the app icon',
|
110
|
+
optional: true,
|
111
|
+
short_option: '-l',
|
112
|
+
verify_block: proc do |value|
|
113
|
+
UI.user_error!("Could not find png file at path '#{value}'") unless File.exist?(value)
|
114
|
+
UI.user_error!("'#{value}' doesn't seem to be a png file") unless value.end_with?('.png')
|
115
|
+
end)
|
116
|
+
]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'uploader'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Apprepo
|
5
|
+
# Responsible for running
|
6
|
+
class Runner
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
self.options = options
|
11
|
+
Apprepo::DetectValues.new.run!(self.options)
|
12
|
+
FastlaneCore::PrintTable.print_values(config: options, hide_keys: [:repo_password], mask_keys: [:repo_key], title: "apprepo-sftp #{Apprepo::VERSION} Summary")
|
13
|
+
end
|
14
|
+
|
15
|
+
# rubocop:disable Metrics/AbcSize
|
16
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
17
|
+
def run
|
18
|
+
UI.success('Apprepo SFTP Uploader running...')
|
19
|
+
verify_version unless options[:app_version].to_s.empty?
|
20
|
+
has_binary = options[:ipa]
|
21
|
+
if !options[:skip_binary_upload] && !options[:build_number] && has_binary
|
22
|
+
upload_binary
|
23
|
+
end
|
24
|
+
UI.success('Finished the upload to Apprepo.')
|
25
|
+
notify unless options[:notify].nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make sure the version on Apprepo matches the one in the ipa
|
29
|
+
# If not, the new version will automatically be created
|
30
|
+
def verify_version
|
31
|
+
app_version = options[:app_version]
|
32
|
+
msg = "TODO: Test if Apprepo matches '#{app_version}' from the IPA..."
|
33
|
+
UI.message(msg)
|
34
|
+
|
35
|
+
# changed = options[:app].ensure_version!(app_version)
|
36
|
+
# if changed
|
37
|
+
# UI.success("Successfully set the version to '#{app_version}'")
|
38
|
+
# else
|
39
|
+
# UI.success("'#{app_version}' is the latest version on Apprepo")
|
40
|
+
# end
|
41
|
+
end
|
42
|
+
|
43
|
+
def download_manifest
|
44
|
+
if options[:manifest_path]
|
45
|
+
uploader = Apprepo::Uploader.new(options)
|
46
|
+
result = uploader.download_manifest_only
|
47
|
+
msg = 'Metadata download failed. Check out the error above.'
|
48
|
+
UI.user_error!(msg) unless result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Upload the binary to Apprepo
|
53
|
+
def upload_binary
|
54
|
+
if options[:ipa]
|
55
|
+
uploader = Apprepo::Uploader.new(options)
|
56
|
+
result = uploader.upload
|
57
|
+
msg = 'Binary upload failed. Check out the error above.'
|
58
|
+
UI.user_error!(msg) unless result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def notify
|
63
|
+
# should be in metadata
|
64
|
+
UI.command_output('TODO: Missing implementation for Apprepo Push Notifier')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Apprepo
|
3
|
+
# Responsible for setting up the Repofile configuration
|
4
|
+
class Setup
|
5
|
+
def setup_apprepo(file_path, data, _apprepo_path, _options)
|
6
|
+
UI.message('[Apprepo:Setup] Setting up...')
|
7
|
+
File.write(file_path, data)
|
8
|
+
|
9
|
+
# TODO: implement later
|
10
|
+
download_manifest(apprepo_path, options)
|
11
|
+
|
12
|
+
UI.success("NOT! created new Repofile at path '#{file_path}'")
|
13
|
+
end
|
14
|
+
|
15
|
+
# This method takes care of creating a new 'apprepo' folder with metadata
|
16
|
+
# and screenshots folders
|
17
|
+
def generate_apprepo_file(_apprepo_path, options)
|
18
|
+
# v = options[:app].latest_version
|
19
|
+
# generate_apprepo_file(v, File.join(apprepo_path, 'manifest.json'))
|
20
|
+
|
21
|
+
# Generate the final Repofile here
|
22
|
+
gem_path = Helper.gem_path('apprepo')
|
23
|
+
apprepo = File.read("#{gem_path}/../assets/RepofileDefault")
|
24
|
+
apprepo.gsub!('[[APP_IDENTIFIER]]', options[:app].bundle_id)
|
25
|
+
apprepo.gsub!('[[APPREPO_IPA_PATH]]', options[:app].file_path)
|
26
|
+
apprepo.gsub!('[[APP_VERSION]]', options[:app].version)
|
27
|
+
apprepo.gsub!('[[APP_NAME]]', options[:app].name)
|
28
|
+
# apprepo (was deliver)
|
29
|
+
end
|
30
|
+
|
31
|
+
def download_manifest(apprepo_path, _options)
|
32
|
+
path = File.join(apprepo_path, 'metadata')
|
33
|
+
FileUtils.mkdir_p(path)
|
34
|
+
UI.success("TODO: DOWNLOAD MANIFEST'")
|
35
|
+
Apprepo::Uploader.new(options).download_manifest_only
|
36
|
+
end
|
37
|
+
|
38
|
+
def run(options)
|
39
|
+
UI.message('[Apprepo:Setup] Running...')
|
40
|
+
containing = (File.directory?('fastlane') ? 'fastlane' : '.')
|
41
|
+
file_path = File.join(containing, 'Repofile')
|
42
|
+
data = generate_apprepo_file(containing, options)
|
43
|
+
setup_apprepo(file_path, data, containing, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @setup = new Apprepo::Setup
|
49
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'json'
|
5
|
+
require 'net/ssh'
|
6
|
+
require 'net/sftp'
|
7
|
+
require 'fastlane_core'
|
8
|
+
require 'fastlane_core/languages'
|
9
|
+
|
10
|
+
require_relative 'options'
|
11
|
+
|
12
|
+
module Fastlane
|
13
|
+
module Apprepo
|
14
|
+
# rubocop:disable Metrics/ClassLength
|
15
|
+
|
16
|
+
# Responsible for performing the SFTP operation
|
17
|
+
class Uploader
|
18
|
+
attr_accessor :options
|
19
|
+
|
20
|
+
#
|
21
|
+
# These want to be an input parameters:
|
22
|
+
#
|
23
|
+
|
24
|
+
attr_accessor :host
|
25
|
+
attr_accessor :user
|
26
|
+
attr_accessor :password
|
27
|
+
attr_accessor :rsa_keypath
|
28
|
+
attr_accessor :ipa_path
|
29
|
+
attr_accessor :manifest_path
|
30
|
+
attr_accessor :appcode
|
31
|
+
|
32
|
+
# rubocop:disable Metrics/AbcSize
|
33
|
+
# rubocop:disable Metrics/MethodLength
|
34
|
+
def initialize(options)
|
35
|
+
self.options = options unless options.nil?
|
36
|
+
self.host = options[:repo_url] # 'repo.teacloud.net'
|
37
|
+
self.user = options[:repo_user]
|
38
|
+
self.password = options[:repo_password]
|
39
|
+
self.rsa_keypath = options[:repo_key] # '../assets/circle.key'
|
40
|
+
self.ipa_path = options[:ipa] # '../sampleapp.ipa'
|
41
|
+
self.manifest_path = options[:manifest_path] # '../assets/example_manifest.json'
|
42
|
+
self.appcode = options[:appcode]
|
43
|
+
|
44
|
+
# Apprepo::Uploader.new.run!
|
45
|
+
# FastlaneCore::PrintTable.print_values(config: nil , hide_keys: [:app],
|
46
|
+
# mask_keys: ['app_review_information.demo_password'],
|
47
|
+
# title: "deliver #{Apprepo::VERSION} Summary") # options
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Upload IPA & manifest
|
52
|
+
#
|
53
|
+
|
54
|
+
# rubocop:disable Metrics/AbcSize
|
55
|
+
# rubocop:disable Metrics/MethodLength
|
56
|
+
def upload
|
57
|
+
# Login & Upload IPA with metadata using RSA key or username/password
|
58
|
+
FastlaneCore::UI.message('upload...')
|
59
|
+
|
60
|
+
if host.nil? || user.nil?
|
61
|
+
FastlaneCore::UI.user_error('repo_url, repo_user and repo_pasdword or repo_key must be set on upload')
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
|
65
|
+
if rsa_keypath
|
66
|
+
rsa_key = load_rsa_key(rsa_keypath)
|
67
|
+
if rsa_key.nil?
|
68
|
+
FastlaneCore::UI.user_error('Failed to load RSA key... ' + options[:rsa_keypath])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
success = false
|
73
|
+
if !rsa_key.nil?
|
74
|
+
FastlaneCore::UI.message('Logging in with RSA key...')
|
75
|
+
Net::SSH.start(host, user, key_data: rsa_key, keys_only: true) do |ssh|
|
76
|
+
FastlaneCore::UI.message('Uploading IPA & Manifest...')
|
77
|
+
success = ssh_sftp_upload(ssh, ipa_path, manifest_path)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
FastlaneCore::UI.message('Logging in with username/password...')
|
81
|
+
Net::SSH.start(host, user, password: password) do |ssh|
|
82
|
+
FastlaneCore::UI.message('Uploading IPA & Manifest...')
|
83
|
+
success = ssh_sftp_upload(ssh, ipa_path, manifest_path)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
success
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Download metadata only
|
91
|
+
#
|
92
|
+
|
93
|
+
# rubocop:disable Metrics/AbcSize
|
94
|
+
# rubocop:disable Metrics/MethodLength
|
95
|
+
def download_manifest_only
|
96
|
+
FastlaneCore::UI.message('download_manifest_only...')
|
97
|
+
rsa_key = load_rsa_key(rsa_keypath)
|
98
|
+
success = true
|
99
|
+
if !rsa_key.nil?
|
100
|
+
FastlaneCore::UI.message('Logging in with RSA key for download...')
|
101
|
+
Net::SSH.start(host, user, key_data: rsa_key, keys_only: true) do |ssh|
|
102
|
+
FastlaneCore::UI.message('Uploading UPA & Manifest...')
|
103
|
+
success = ssh_sftp_download(ssh, manifest_path)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
FastlaneCore::UI.message('Logging in for download...')
|
107
|
+
Net::SSH.start(host, user, password: password) do |ssh|
|
108
|
+
FastlaneCore::UI.message('Uploading UPA & Manifest...')
|
109
|
+
success = ssh_sftp_download(ssh, manifest_path)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
success
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def ssh_sftp_download(ssh, _manifest_path)
|
118
|
+
ssh.sftp.connect do |sftp|
|
119
|
+
FastlaneCore::UI.message('Fetching remote manifest...')
|
120
|
+
manifest = download_manifest(sftp)
|
121
|
+
puts '********************************************************'
|
122
|
+
puts JSON.pretty_generate(manifest)
|
123
|
+
puts '********************************************************'
|
124
|
+
FastlaneCore::UI.success('Successfully fetched manifest')
|
125
|
+
FastlaneCore::UI.command_output('TODO: Processing manifest not implemented.')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def ssh_sftp_upload(ssh, local_ipa_path, manifest_path)
|
130
|
+
ssh.sftp.connect do |sftp|
|
131
|
+
break unless check_ipa(local_ipa_path)
|
132
|
+
check_appcode(sftp, appcode)
|
133
|
+
path = remote_path(appcode)
|
134
|
+
manifest = download_manifest(sftp)
|
135
|
+
puts JSON.pretty_generate(manifest) unless manifest.nil?
|
136
|
+
bump_ipa(sftp, local_ipa_path, appcode)
|
137
|
+
remote_ipa_path = get_remote_ipa_path(local_ipa_path, appcode)
|
138
|
+
upload_ipa(sftp, local_ipa_path, remote_ipa_path)
|
139
|
+
upload_manifest(sftp, manifest_path, remote_manifest_path(appcode))
|
140
|
+
# Lists the entries in a directory for verification
|
141
|
+
sftp.dir.foreach(path) do |entry|
|
142
|
+
FastlaneCore::UI.message(entry.longname)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check IPA existence locally
|
148
|
+
#
|
149
|
+
# @param local_ipa_path
|
150
|
+
def check_ipa(local_ipa_path)
|
151
|
+
if File.exist?(local_ipa_path)
|
152
|
+
FastlaneCore::UI.important('IPA found at ' + local_ipa_path)
|
153
|
+
return true
|
154
|
+
else
|
155
|
+
FastlaneCore::UI.verbose('IPA at given path does not exist yet.')
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Private methods - Remote Operations
|
161
|
+
|
162
|
+
# Checks/creates remote APPCODE directory
|
163
|
+
# @param sftp
|
164
|
+
# @param [String] appcode
|
165
|
+
def check_appcode(sftp, appcode)
|
166
|
+
path = remote_path(appcode)
|
167
|
+
FastlaneCore::UI.message('Checking APPCODE')
|
168
|
+
remote_mkdir(sftp, path)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Checks/renames remote IPA
|
172
|
+
#
|
173
|
+
# @params sftp
|
174
|
+
# @params [String] local_ipa_path
|
175
|
+
def bump_ipa(sftp, local, appcode)
|
176
|
+
remote = get_remote_ipa_path(local, appcode)
|
177
|
+
FastlaneCore::UI.message('Checking remote IPA')
|
178
|
+
begin
|
179
|
+
sftp.stat!(remote) do |response|
|
180
|
+
if response.ok?
|
181
|
+
begin
|
182
|
+
sftp.rename!(remote, remote + '.bak')
|
183
|
+
rescue
|
184
|
+
begin
|
185
|
+
sftp.remove(remote + '.bak') # may fail if not existent
|
186
|
+
FastlaneCore::UI.message('Removed ' + remote + '.bak')
|
187
|
+
rescue
|
188
|
+
sftp.rename!(remote, remote + '.bak')
|
189
|
+
FastlaneCore::UI.message('Bumped to ' + remote + '.bak')
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
rescue
|
195
|
+
FastlaneCore::UI.message('No previous IPA found.')
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Downloads remote manifest, self.appcode required by options.
|
200
|
+
#
|
201
|
+
# @param sftp
|
202
|
+
# @param [String] remote_path
|
203
|
+
# @returns [JSON] json or nil
|
204
|
+
def download_manifest(sftp)
|
205
|
+
FastlaneCore::UI.message('Checking remote Manifest')
|
206
|
+
json = nil
|
207
|
+
remote_manifest_path = remote_manifest_path(appcode)
|
208
|
+
begin
|
209
|
+
sftp.stat!(remote_manifest_path) do |response|
|
210
|
+
if response.ok?
|
211
|
+
FastlaneCore::UI.success('Loading remote manifest:')
|
212
|
+
manifest = sftp.download!(remote_manifest_path)
|
213
|
+
json = JSON.parse(manifest)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
rescue
|
217
|
+
FastlaneCore::UI.message('No previous Manifest found')
|
218
|
+
end
|
219
|
+
json
|
220
|
+
end
|
221
|
+
|
222
|
+
# Upload current IPA
|
223
|
+
#
|
224
|
+
# @param sftp
|
225
|
+
# @param [String] local_ipa_path
|
226
|
+
# @param [String] remote_ipa_path
|
227
|
+
def upload_ipa(sftp, local_ipa_path, remote_ipa_path)
|
228
|
+
msg = "[Uploading IPA] #{local_ipa_path} to #{remote_ipa_path}"
|
229
|
+
FastlaneCore::UI.message(msg)
|
230
|
+
result = sftp.upload!(local_ipa_path, remote_ipa_path) do |event, _uploader, *_args|
|
231
|
+
case event
|
232
|
+
when :open then
|
233
|
+
putc '.'
|
234
|
+
when :put then
|
235
|
+
putc '.'
|
236
|
+
$stdout.flush
|
237
|
+
when :close then
|
238
|
+
puts "\n"
|
239
|
+
when :finish then
|
240
|
+
FastlaneCore::UI.success('IPA upload successful')
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Upload current manifest.json
|
246
|
+
#
|
247
|
+
# @param sftp
|
248
|
+
# @param [String] manifest_path
|
249
|
+
# @param [String] remote_manifest_path
|
250
|
+
def upload_manifest(sftp, local_path, remote_path)
|
251
|
+
msg = '[Uploading Manifest] ' + local_path + ' to ' + remote_path
|
252
|
+
FastlaneCore::UI.message(msg)
|
253
|
+
result = sftp.upload!(local_path, remote_path) do |event, _uploader, *_args|
|
254
|
+
case event
|
255
|
+
when :finish then
|
256
|
+
FastlaneCore::UI.success('Manifest upload successful')
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def get_remote_ipa_path(local_ipa_path, appcode)
|
262
|
+
remote_path(appcode) + File.basename(local_ipa_path)
|
263
|
+
end
|
264
|
+
|
265
|
+
def remote_path(appcode)
|
266
|
+
generate_remote_path + appcode + '/'
|
267
|
+
end
|
268
|
+
|
269
|
+
def remote_manifest_path(appcode)
|
270
|
+
remote_manifest_path = remote_path(appcode) + 'manifest.json'
|
271
|
+
end
|
272
|
+
|
273
|
+
def generate_remote_path
|
274
|
+
'/home/' + user + '/repo/apps/'
|
275
|
+
end
|
276
|
+
|
277
|
+
def remote_mkdir(sftp, remote_path)
|
278
|
+
sftp.mkdir remote_path
|
279
|
+
rescue Net::SFTP::StatusException => e
|
280
|
+
raise if e.code != 11
|
281
|
+
msg = "Remote dir #{remote_path} exists."
|
282
|
+
FastlaneCore::UI.message(msg)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Private methods - Local Operations
|
286
|
+
|
287
|
+
def load_rsa_key(rsa_keypath)
|
288
|
+
File.open(rsa_keypath, 'r') do |file|
|
289
|
+
rsa_key = nil
|
290
|
+
rsa_key = [file.read]
|
291
|
+
if !rsa_key.nil?
|
292
|
+
FastlaneCore::UI.success('Successfully loaded RSA key...')
|
293
|
+
else
|
294
|
+
FastlaneCore::UI.user_error!('Failed to load RSA key...')
|
295
|
+
end
|
296
|
+
rsa_key
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|