Class: Match::Storage::GoogleCloudStorage

Inherits:
Interface
  • Object
show all
Defined in:
match/lib/match/storage/google_cloud_storage.rb

Overview

Store the code signing identities in on Google Cloud Storage

Constant Summary collapse

DEFAULT_KEYS_FILE_NAME =
"gc_keys.json"

Constants inherited from Interface

Interface::MATCH_VERSION_FILE_NAME

Instance Attribute Summary collapse

Attributes inherited from Interface

#working_directory

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Interface

#clear_changes, #configure, #save_changes!

Constructor Details

#initialize(type: nil, platform: nil, google_cloud_bucket_name: nil, google_cloud_keys_file: nil, google_cloud_project_id: nil, readonly: nil, username: nil, team_id: nil, team_name: nil, api_key_path: nil, api_key: nil, skip_google_cloud_account_confirmation: nil) ⇒ GoogleCloudStorage

Returns a new instance of GoogleCloudStorage.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'match/lib/match/storage/google_cloud_storage.rb', line 56

def initialize(type: nil,
               platform: nil,
               google_cloud_bucket_name: nil,
               google_cloud_keys_file: nil,
               google_cloud_project_id: nil,
               readonly: nil,
               username: nil,
               team_id: nil,
               team_name: nil,
               api_key_path: nil,
               api_key: nil,
               skip_google_cloud_account_confirmation: nil)
  @type = type if type
  @platform = platform if platform
  @google_cloud_project_id = google_cloud_project_id if google_cloud_project_id
  @bucket_name = google_cloud_bucket_name

  @readonly = readonly
  @username = username
  @team_id = team_id
  @team_name = team_name

  @api_key_path = api_key_path
  @api_key = api_key

  @google_cloud_keys_file = ensure_keys_file_exists(google_cloud_keys_file, google_cloud_project_id, )
  if self.google_cloud_keys_file.to_s.length > 0
    # Extract the Project ID from the `JSON` file
    # so the user doesn't have to provide it manually
    begin
      keys_file_content = JSON.parse(File.read(self.google_cloud_keys_file))
    rescue JSON::ParserError
      # Don't dump secret into logs in case a secret (p8) is passed. See fastlane/fastlane#21017
      UI.user_error!("Provided keys file on path #{File.expand_path(self.google_cloud_keys_file)} is not a valid JSON file")
    end
    if google_cloud_project_id.to_s.length > 0 && google_cloud_project_id != keys_file_content["project_id"]
      UI.important("The google_cloud_keys_file's project ID ('#{keys_file_content['project_id']}') doesn't match the google_cloud_project_id ('#{google_cloud_project_id}'). This may be the wrong keys file.")
    end
    @google_cloud_project_id = keys_file_content["project_id"]
    if self.google_cloud_project_id.to_s.length == 0
      UI.user_error!("Provided keys file on path #{File.expand_path(self.google_cloud_keys_file)} doesn't include required value for `project_id`")
    end
  end

  # Create the Google Cloud Storage client
  # If the JSON auth file is invalid, this line will
  # raise an exception
  begin
    self.gc_storage = Google::Cloud::Storage.new(
      credentials: self.google_cloud_keys_file,
      project_id: self.google_cloud_project_id
    )
  rescue => ex
    UI.error(ex)
    UI.verbose(ex.backtrace.join("\n"))
    UI.user_error!("Couldn't log into your Google Cloud account using the provided JSON file at path '#{File.expand_path(self.google_cloud_keys_file)}'")
  end

  ensure_bucket_is_selected
  check_bucket_permissions
end

Instance Attribute Details

#api_keyObject (readonly)

Returns the value of attribute api_key.



27
28
29
# File 'match/lib/match/storage/google_cloud_storage.rb', line 27

def api_key
  @api_key
end

#api_key_pathObject (readonly)

Returns the value of attribute api_key_path.



26
27
28
# File 'match/lib/match/storage/google_cloud_storage.rb', line 26

def api_key_path
  @api_key_path
end

#bucket_nameObject (readonly)

Returns the value of attribute bucket_name.



19
20
21
# File 'match/lib/match/storage/google_cloud_storage.rb', line 19

def bucket_name
  @bucket_name
end

#gc_storageObject

Managed values



30
31
32
# File 'match/lib/match/storage/google_cloud_storage.rb', line 30

def gc_storage
  @gc_storage
end

#google_cloud_keys_fileObject (readonly)

Returns the value of attribute google_cloud_keys_file.



20
21
22
# File 'match/lib/match/storage/google_cloud_storage.rb', line 20

def google_cloud_keys_file
  @google_cloud_keys_file
end

#google_cloud_project_idObject (readonly)

Returns the value of attribute google_cloud_project_id.



21
22
23
# File 'match/lib/match/storage/google_cloud_storage.rb', line 21

def google_cloud_project_id
  @google_cloud_project_id
end

#platformObject (readonly)

Returns the value of attribute platform.



18
19
20
# File 'match/lib/match/storage/google_cloud_storage.rb', line 18

def platform
  @platform
end

#readonlyObject (readonly)

Returns the value of attribute readonly.



22
23
24
# File 'match/lib/match/storage/google_cloud_storage.rb', line 22

def readonly
  @readonly
end

#team_idObject (readonly)

Returns the value of attribute team_id.



24
25
26
# File 'match/lib/match/storage/google_cloud_storage.rb', line 24

def team_id
  @team_id
end

#team_nameObject (readonly)

Returns the value of attribute team_name.



25
26
27
# File 'match/lib/match/storage/google_cloud_storage.rb', line 25

def team_name
  @team_name
end

#typeObject (readonly)

User provided values



17
18
19
# File 'match/lib/match/storage/google_cloud_storage.rb', line 17

def type
  @type
end

#usernameObject (readonly)

Returns the value of attribute username.



23
24
25
# File 'match/lib/match/storage/google_cloud_storage.rb', line 23

def username
  @username
end

Class Method Details

.configure(params) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'match/lib/match/storage/google_cloud_storage.rb', line 32

def self.configure(params)
  if params[:git_url].to_s.length > 0
    UI.important("Looks like you still define a `git_url` somewhere, even though")
    UI.important("you use Google Cloud Storage. You can remove the `git_url`")
    UI.important("from your Matchfile and Fastfile")
    UI.message("The above is just a warning, fastlane will continue as usual now...")
  end

  return self.new(
    type: params[:type].to_s,
    platform: params[:platform].to_s,
    google_cloud_bucket_name: params[:google_cloud_bucket_name],
    google_cloud_keys_file: params[:google_cloud_keys_file],
    google_cloud_project_id: params[:google_cloud_project_id],
    readonly: params[:readonly],
    username: params[:username],
    team_id: params[:team_id],
    team_name: params[:team_name],
    api_key_path: params[:api_key_path],
    api_key: params[:api_key],
    skip_google_cloud_account_confirmation: params[:skip_google_cloud_account_confirmation]
  )
end

Instance Method Details

#api_tokenObject



131
132
133
134
135
# File 'match/lib/match/storage/google_cloud_storage.rb', line 131

def api_token
  api_token = Spaceship::ConnectAPI::Token.from(hash: self.api_key, filepath: self.api_key_path)
  api_token ||= Spaceship::ConnectAPI.token
  return api_token
end

#currently_used_team_idObject



118
119
120
121
122
123
124
125
126
127
128
129
# File 'match/lib/match/storage/google_cloud_storage.rb', line 118

def currently_used_team_id
  if self.readonly
    # In readonly mode, we still want to see if the user provided a team_id
    # see `prefixed_working_directory` comments for more details
    return self.team_id
  else
    UI.user_error!("The `team_id` option is required. fastlane cannot automatically determine portal team id via the App Store Connect API (yet)") if self.team_id.to_s.empty?

    spaceship = SpaceshipEnsure.new(self.username, self.team_id, self.team_name, self.api_token)
    return spaceship.team_id
  end
end

#delete_files(files_to_delete: [], custom_message: nil) ⇒ Object



170
171
172
173
174
175
176
177
# File 'match/lib/match/storage/google_cloud_storage.rb', line 170

def delete_files(files_to_delete: [], custom_message: nil)
  files_to_delete.each do |current_file|
    target_path = current_file.gsub(self.working_directory + "/", "")
    file = bucket.file(target_path)
    UI.message("Deleting '#{target_path}' from Google Cloud Storage bucket '#{self.bucket_name}'...")
    file.delete
  end
end

#downloadObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'match/lib/match/storage/google_cloud_storage.rb', line 152

def download
  # Check if we already have a functional working_directory
  return if @working_directory

  # No existing working directory, creating a new one now
  self.working_directory = Dir.mktmpdir

  bucket.files.each do |current_file|
    file_path = current_file.name # e.g. "N8X438SEU2/certs/distribution/XD9G7QCACF.cer"
    download_path = File.join(self.working_directory, file_path)

    FileUtils.mkdir_p(File.expand_path("..", download_path))
    UI.verbose("Downloading file from Google Cloud Storage '#{file_path}' on bucket #{self.bucket_name}")
    current_file.download(download_path)
  end
  UI.verbose("Successfully downloaded files from GCS to #{self.working_directory}")
end

#generate_matchfile_contentObject



210
211
212
# File 'match/lib/match/storage/google_cloud_storage.rb', line 210

def generate_matchfile_content
  return "google_cloud_bucket_name(\"#{self.bucket_name}\")"
end

#human_readable_descriptionObject



179
180
181
# File 'match/lib/match/storage/google_cloud_storage.rb', line 179

def human_readable_description
  "Google Cloud Bucket [#{self.google_cloud_project_id}/#{self.bucket_name}]"
end

#list_files(file_name: "", file_ext: "") ⇒ Object



206
207
208
# File 'match/lib/match/storage/google_cloud_storage.rb', line 206

def list_files(file_name: "", file_ext: "")
  Dir[File.join(working_directory, self.team_id, "**", file_name, "*.#{file_ext}")]
end

#prefixed_working_directoryObject



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'match/lib/match/storage/google_cloud_storage.rb', line 137

def prefixed_working_directory
  # We fall back to "*", which means certificates and profiles
  # from all teams that use this bucket would be installed. This is not ideal, but
  # unless the user provides a `team_id`, we can't know which one to use
  # This only happens if `readonly` is activated, and no `team_id` was provided
  @_folder_prefix ||= currently_used_team_id
  if @_folder_prefix.nil?
    # We use a `@_folder_prefix` variable, to keep state between multiple calls of this
    # method, as the value won't change. This way the warning is only printed once
    UI.important("Looks like you run `match` in `readonly` mode, and didn't provide a `team_id`. This will still work, however it is recommended to provide a `team_id` in your Appfile or Matchfile")
    @_folder_prefix = "*"
  end
  return File.join(working_directory, @_folder_prefix)
end

#skip_docsObject



202
203
204
# File 'match/lib/match/storage/google_cloud_storage.rb', line 202

def skip_docs
  false
end

#upload_files(files_to_upload: [], custom_message: nil) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'match/lib/match/storage/google_cloud_storage.rb', line 183

def upload_files(files_to_upload: [], custom_message: nil)
  # `files_to_upload` is an array of files that need to be uploaded to Google Cloud
  # Those doesn't mean they're new, it might just be they're changed
  # Either way, we'll upload them using the same technique

  files_to_upload.each do |current_file|
    # Go from
    #   "/var/folders/px/bz2kts9n69g8crgv4jpjh6b40000gn/T/d20181026-96528-1av4gge/profiles/development/Development_me.mobileprovision"
    # to
    #   "profiles/development/Development_me.mobileprovision"
    #

    # We also have to remove the trailing `/` as Google Cloud doesn't handle it nicely
    target_path = current_file.gsub(self.working_directory + "/", "")
    UI.verbose("Uploading '#{target_path}' to Google Cloud Storage...")
    bucket.create_file(current_file, target_path)
  end
end