fastlane-plugin-flint 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,211 @@
1
+ require 'fastlane_core/command_executor'
2
+
3
+ require_relative 'change_password'
4
+ require_relative 'encrypt'
5
+
6
+ module Fastlane
7
+ module Flint
8
+ class GitHelper
9
+ FLINT_VERSION_FILE_NAME = "flint_version.txt"
10
+
11
+ def self.clone(git_url,
12
+ shallow_clone,
13
+ manual_password: nil,
14
+ skip_docs: false,
15
+ branch: "master",
16
+ git_full_name: nil,
17
+ git_user_email: nil,
18
+ clone_branch_directly: false,
19
+ encrypt: nil)
20
+ # Note: if you modify the parameters above, don't forget to also update the method call in
21
+ # - flint_action.rb
22
+ # - nuke.rb
23
+ # - change_password.rb
24
+ # - commands_generator.rb
25
+ #
26
+ return @dir if @dir
27
+
28
+ @dir = Dir.mktmpdir
29
+
30
+ command = "git clone '#{git_url}' '#{@dir}'"
31
+ if shallow_clone
32
+ command << " --depth 1 --no-single-branch"
33
+ elsif clone_branch_directly
34
+ command += " -b #{branch.shellescape} --single-branch"
35
+ end
36
+
37
+ UI.message("Cloning remote git repo...")
38
+
39
+ if branch && !clone_branch_directly
40
+ UI.message("If cloning the repo takes too long, you can use the `clone_branch_directly` option in flint.")
41
+ end
42
+
43
+ begin
44
+ # GIT_TERMINAL_PROMPT will fail the `git clone` command if user credentials are missing
45
+ FastlaneCore::CommandExecutor.execute(command: "GIT_TERMINAL_PROMPT=0 #{command}",
46
+ print_all: FastlaneCore::Globals.verbose?,
47
+ print_command: FastlaneCore::Globals.verbose?)
48
+ rescue
49
+ UI.error("Error cloning keystores repo, please make sure you have read access to the repository you want to use")
50
+ if branch && clone_branch_directly
51
+ UI.error("You passed '#{branch}' as branch in combination with the `clone_branch_directly` flag. Please remove `clone_branch_directly` flag on the first run for _flint_ to create the branch.")
52
+ end
53
+ UI.error("Run the following command manually to make sure you're properly authenticated:")
54
+ UI.command(command)
55
+ UI.user_error!("Error cloning keystores git repo, please make sure you have access to the repository - see instructions above")
56
+ end
57
+
58
+ add_user_config(git_full_name, git_user_email)
59
+
60
+ UI.user_error!("Error cloning repo, make sure you have access to it '#{git_url}'") unless File.directory?(@dir)
61
+
62
+ checkout_branch(branch) unless branch == "master"
63
+
64
+ if !Helper.test? && GitHelper.flint_version(@dir).nil? && manual_password.nil? && File.exist?(File.join(@dir, "README.md"))
65
+ UI.important("Migrating to new flint...")
66
+ ChangePassword.update(params: { git_url: git_url,
67
+ git_branch: branch,
68
+ shallow_clone: shallow_clone },
69
+ from: "",
70
+ to: encrypt.password(git_url))
71
+ return self.clone(git_url, shallow_clone)
72
+ end
73
+
74
+ encrypt.decrypt_repo(path: @dir, git_url: git_url, manual_password: manual_password)
75
+
76
+ return @dir
77
+ end
78
+
79
+ def self.generate_commit_message(params)
80
+ # 'Automatic commit via fastlane'
81
+ [
82
+ "[fastlane]",
83
+ "Updated",
84
+ params[:type].to_s,
85
+ "and platform android"
86
+ ].join(" ")
87
+ end
88
+
89
+ def self.flint_version(workspace)
90
+ path = File.join(workspace, FLINT_VERSION_FILE_NAME)
91
+ if File.exist?(path)
92
+ Gem::Version.new(File.read(path))
93
+ end
94
+ end
95
+
96
+ def self.commit_changes(path, message, git_url, branch = "master", files_to_commmit = nil, encrypt = nil)
97
+ files_to_commmit ||= []
98
+ Dir.chdir(path) do
99
+ return if `git status`.include?("nothing to commit")
100
+
101
+ encrypt.encrypt_repo(path: path, git_url: git_url)
102
+ commands = []
103
+
104
+ if files_to_commmit.count > 0 # e.g. for nuke this is treated differently
105
+ if !File.exist?(FLINT_VERSION_FILE_NAME) || File.read(FLINT_VERSION_FILE_NAME) != Fastlane::VERSION.to_s
106
+ files_to_commmit << FLINT_VERSION_FILE_NAME
107
+ File.write(FLINT_VERSION_FILE_NAME, Fastlane::VERSION) # stored unencrypted
108
+ end
109
+
110
+ template = File.read("#{Flint::ROOT}/assets/READMETemplate.md")
111
+ readme_path = "README.md"
112
+ if !File.exist?(readme_path) || File.read(readme_path) != template
113
+ files_to_commmit << readme_path
114
+ File.write(readme_path, template)
115
+ end
116
+
117
+ # `git add` each file we want to commit
118
+ # - Fixes https://github.com/fastlane/fastlane/issues/8917
119
+ # - Fixes https://github.com/fastlane/fastlane/issues/8793
120
+ # - Replaces, closes and fixes https://github.com/fastlane/fastlane/pull/8919
121
+ commands += files_to_commmit.map do |current_file|
122
+ "git add #{current_file.shellescape}"
123
+ end
124
+ else
125
+ # No specific list given, e.g. this happens on `fastlane flint nuke`
126
+ # We just want to run `git add -A` to commit everything
127
+ commands << "git add -A"
128
+ end
129
+ commands << "git commit -m #{message.shellescape}"
130
+ commands << "GIT_TERMINAL_PROMPT=0 git push origin #{branch.shellescape}"
131
+
132
+ UI.message("Pushing changes to remote git repo...")
133
+
134
+ commands.each do |command|
135
+ FastlaneCore::CommandExecutor.execute(command: command,
136
+ print_all: FastlaneCore::Globals.verbose?,
137
+ print_command: FastlaneCore::Globals.verbose?)
138
+ end
139
+ end
140
+ FileUtils.rm_rf(path)
141
+ @dir = nil
142
+ rescue => ex
143
+ UI.error("Couldn't commit or push changes back to git...")
144
+ UI.error(ex)
145
+ end
146
+
147
+ def self.clear_changes
148
+ return unless @dir
149
+
150
+ FileUtils.rm_rf(@dir)
151
+ @dir = nil
152
+ end
153
+
154
+ # Create and checkout an specific branch in the git repo
155
+ def self.checkout_branch(branch)
156
+ return unless @dir
157
+
158
+ commands = []
159
+ if branch_exists?(branch)
160
+ # Checkout the branch if it already exists
161
+ commands << "git checkout #{branch.shellescape}"
162
+ else
163
+ # If a new branch is being created, we create it as an 'orphan' to not inherit changes from the master branch.
164
+ commands << "git checkout --orphan #{branch.shellescape}"
165
+ # We also need to reset the working directory to not transfer any uncommitted changes to the new branch.
166
+ commands << "git reset --hard"
167
+ end
168
+
169
+ UI.message("Checking out branch #{branch}...")
170
+
171
+ Dir.chdir(@dir) do
172
+ commands.each do |command|
173
+ FastlaneCore::CommandExecutor.execute(command: command,
174
+ print_all: FastlaneCore::Globals.verbose?,
175
+ print_command: FastlaneCore::Globals.verbose?)
176
+ end
177
+ end
178
+ end
179
+
180
+ # Checks if a specific branch exists in the git repo
181
+ def self.branch_exists?(branch)
182
+ return unless @dir
183
+
184
+ result = Dir.chdir(@dir) do
185
+ FastlaneCore::CommandExecutor.execute(command: "git --no-pager branch --list origin/#{branch.shellescape} --no-color -r",
186
+ print_all: FastlaneCore::Globals.verbose?,
187
+ print_command: FastlaneCore::Globals.verbose?)
188
+ end
189
+ return !result.empty?
190
+ end
191
+
192
+ def self.add_user_config(user_name, user_email)
193
+ # Add git config if needed
194
+ commands = []
195
+ commands << "git config user.name \"#{user_name}\"" unless user_name.nil?
196
+ commands << "git config user.email \"#{user_email}\"" unless user_email.nil?
197
+
198
+ return if commands.empty?
199
+
200
+ UI.message("Add git user config to local git repo...")
201
+ Dir.chdir(@dir) do
202
+ commands.each do |command|
203
+ FastlaneCore::CommandExecutor.execute(command: command,
204
+ print_all: FastlaneCore::Globals.verbose?,
205
+ print_command: FastlaneCore::Globals.verbose?)
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,119 @@
1
+ require 'terminal-table'
2
+
3
+ require 'fastlane_core/print_table'
4
+
5
+ require_relative 'git_helper'
6
+ require_relative 'encrypt'
7
+
8
+ module Fastlane
9
+ module Flint
10
+ class Nuke
11
+ attr_accessor :params
12
+ attr_accessor :type
13
+
14
+ attr_accessor :files
15
+
16
+ def run(params, type: nil)
17
+ self.params = params
18
+ self.type = type
19
+
20
+ params[:workspace] = GitHelper.clone(params[:git_url],
21
+ params[:shallow_clone],
22
+ skip_docs: params[:skip_docs],
23
+ branch: params[:git_branch],
24
+ git_full_name: params[:git_full_name],
25
+ git_user_email: params[:git_user_email],
26
+ clone_branch_directly: params[:clone_branch_directly],
27
+ encrypt: Encrypt.new)
28
+
29
+ had_app_identifier = self.params.fetch(:app_identifier, ask: false)
30
+ self.params[:app_identifier] = '' # we don't really need a value here
31
+ FastlaneCore::PrintTable.print_values(config: params,
32
+ hide_keys: [:app_identifier, :workspace],
33
+ title: "Summary for flint nuke #{Fastlane::VERSION}")
34
+
35
+ prepare_list
36
+ print_tables
37
+
38
+ if params[:readonly]
39
+ UI.user_error!("`fastlane flint nuke` doesn't delete anything when running with --readonly enabled")
40
+ end
41
+
42
+ if (self.files).count > 0
43
+ unless params[:skip_confirmation]
44
+ if type == "release"
45
+ UI.confirm(
46
+ "DANGER: By nuking release keys you might not " +
47
+ "be able to update your app in the play store. Are you sure?"
48
+ )
49
+ end
50
+ UI.error("---")
51
+ UI.error("Are you sure you want to completely delete and revoke all the")
52
+ UI.error("keystores listed above? (y/n)")
53
+ UI.error("---")
54
+ end
55
+ if params[:skip_confirmation] || UI.confirm("Do you really want to nuke everything listed above?")
56
+ nuke_it_now!
57
+ UI.success("Successfully cleaned up ♻️")
58
+ else
59
+ UI.success("Cancelled nuking #thanks 🏠 👨 ‍👩 ‍👧")
60
+ end
61
+ else
62
+ UI.success("No relevant keystores found, nothing to nuke here :)")
63
+ end
64
+ end
65
+
66
+ # Collect all the keystores
67
+ def prepare_list
68
+ UI.message("Fetching keystores...")
69
+ cert_type = Flint.cert_type_sym(type)
70
+
71
+ certs = Dir[File.join(params[:workspace], "**", "*-#{cert_type.to_s}.keystore")]
72
+
73
+ self.files = certs
74
+ end
75
+
76
+ # Print tables to ask the user
77
+ def print_tables
78
+ puts("")
79
+ if self.files.count > 0
80
+ rows = self.files.collect do |f|
81
+ components = f.split(File::SEPARATOR)[-3..-1]
82
+ file_type = components[0..1].reverse.join(" ")[0..-2]
83
+
84
+ [file_type, components[2]]
85
+ end
86
+ puts(Terminal::Table.new({
87
+ title: "Files that are going to be deleted".green,
88
+ headings: ["Type", "File Name"],
89
+ rows: rows
90
+ }))
91
+ puts("")
92
+ end
93
+ end
94
+
95
+ def nuke_it_now!
96
+ if self.files.count > 0
97
+ delete_files!
98
+ end
99
+
100
+ # Now we need to commit and push all this too
101
+ message = ["[fastlane]", "Nuked", "files", "for", type.to_s].join(" ")
102
+ GitHelper.commit_changes(params[:workspace], message, self.params[:git_url], params[:git_branch], nil, Encrypt.new)
103
+ end
104
+
105
+ private
106
+
107
+ def delete_files!
108
+ UI.header("Deleting #{self.files.count} files from the git repo...")
109
+
110
+ self.files.each do |file|
111
+ UI.message("Deleting file '#{File.basename(file)}'...")
112
+
113
+ File.delete(file)
114
+ UI.success("Successfully deleted file")
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,31 @@
1
+ require 'terminal-table'
2
+
3
+ require 'fastlane_core/print_table'
4
+
5
+ require_relative 'utils'
6
+
7
+ module Fastlane
8
+ module Flint
9
+ class TablePrinter
10
+ def self.print_summary(app_identifier: nil, type: nil, platform: :android, keystore_name: nil)
11
+ rows = []
12
+
13
+ type = type.to_sym
14
+
15
+ rows << ["App Identifier", "", app_identifier]
16
+ rows << ["Type", "", type]
17
+ rows << ["Platform", "", platform.to_s]
18
+ rows << ["Keystore", "", keystore_name]
19
+
20
+ params = {}
21
+ params[:rows] = FastlaneCore::PrintTable.transform_output(rows)
22
+ params[:title] = "Installed Keystores".green
23
+ params[:headings] = ['Parameter', 'Environment Variable', 'Value']
24
+
25
+ puts("")
26
+ puts(Terminal::Table.new(params))
27
+ puts("")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ require 'digest'
2
+ require 'fileutils'
3
+ require 'openssl'
4
+
5
+
6
+ module Fastlane
7
+ module Flint
8
+ class Utils
9
+ def self.import(item_path, target_path, keystore_name, alias_name, password)
10
+ FileUtils.cp(item_path, target_path)
11
+ end
12
+
13
+ def self.activate(keystore_name, alias_name, password, keystore_properties_path)
14
+ template = File.read("#{Flint::ROOT}/assets/KeystorePropertiesTemplate")
15
+ template.gsub!("[[STORE_FILE]]", keystore_name)
16
+ template.gsub!("[[KEY_ALIAS]]", alias_name)
17
+ template.gsub!("[[PASSWORD]]", password)
18
+ File.write(keystore_properties_path, template)
19
+
20
+ # We could test that the required lines are added to build.gradle
21
+ end
22
+
23
+ def self.installed?(item_path, target_path)
24
+ if File.exist?(target_path)
25
+ installed_digest = Digest::MD5.hexdigest File.read target_path
26
+ item_digest = Digest::MD5.hexdigest File.read item_path
27
+
28
+ return installed_digest == item_digest
29
+ end
30
+
31
+ return false
32
+ end
33
+
34
+ def self.get_keystore_info(item_path, password)
35
+ cmd =
36
+ begin
37
+ output = IO.popen("keytool -list -v -keystore '#{item_path}' -storepass '#{password}'")
38
+ lines = output.readlines
39
+ output.close
40
+ raise lines.join unless $?.exitstatus == 0
41
+ rescue => ex
42
+ raise ex
43
+ end
44
+
45
+ return lines
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Flint
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-flint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jyrno Ader
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec_junit_formatter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.49.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.49.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-require_tools
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: fastlane
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 2.109.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 2.109.1
139
+ description:
140
+ email: jyrno42@gmail.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/assets/FlintfileTemplate
148
+ - lib/assets/KeystorePropertiesTemplate
149
+ - lib/assets/READMETemplate.md
150
+ - lib/fastlane/plugin/flint.rb
151
+ - lib/fastlane/plugin/flint/actions/flint_action.rb
152
+ - lib/fastlane/plugin/flint/actions/flint_change_password.rb
153
+ - lib/fastlane/plugin/flint/actions/flint_nuke.rb
154
+ - lib/fastlane/plugin/flint/actions/flint_setup.rb
155
+ - lib/fastlane/plugin/flint/helper/change_password.rb
156
+ - lib/fastlane/plugin/flint/helper/commands_generator.rb
157
+ - lib/fastlane/plugin/flint/helper/encrypt.rb
158
+ - lib/fastlane/plugin/flint/helper/flint_helper.rb
159
+ - lib/fastlane/plugin/flint/helper/generator.rb
160
+ - lib/fastlane/plugin/flint/helper/git_helper.rb
161
+ - lib/fastlane/plugin/flint/helper/nuke.rb
162
+ - lib/fastlane/plugin/flint/helper/table_printer.rb
163
+ - lib/fastlane/plugin/flint/helper/utils.rb
164
+ - lib/fastlane/plugin/flint/version.rb
165
+ homepage: https://github.com/jyrno42/fastlane-plugin-flint
166
+ licenses:
167
+ - MIT
168
+ metadata: {}
169
+ post_install_message:
170
+ rdoc_options: []
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 2.7.6
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: Easily sync your keystores across your team using git
189
+ test_files: []