fastlane-plugin-firebase 0.1.1
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 +21 -0
- data/README.md +101 -0
- data/lib/fastlane/plugin/firebase.rb +16 -0
- data/lib/fastlane/plugin/firebase/actions/firebase_add_client_action.rb +107 -0
- data/lib/fastlane/plugin/firebase/actions/firebase_delete_client_action.rb +75 -0
- data/lib/fastlane/plugin/firebase/actions/firebase_download_config_action.rb +79 -0
- data/lib/fastlane/plugin/firebase/actions/firebase_list_action.rb +60 -0
- data/lib/fastlane/plugin/firebase/actions/firebase_upload_certificate_action.rb +104 -0
- data/lib/fastlane/plugin/firebase/helper/firebase_helper.rb +12 -0
- data/lib/fastlane/plugin/firebase/lib/api.rb +186 -0
- data/lib/fastlane/plugin/firebase/lib/commands_generator.rb +41 -0
- data/lib/fastlane/plugin/firebase/lib/manager.rb +61 -0
- data/lib/fastlane/plugin/firebase/lib/options.rb +14 -0
- data/lib/fastlane/plugin/firebase/version.rb +5 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9361331c59d149a7f719386d1c08c25c82a403f8
|
4
|
+
data.tar.gz: 2016b089727e3a4ee85587b44eeada1b4b174893
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7081d6fc6f890e1d0ae24d906113655e98e0f22b6a07aa061338923a688f789afb67d15397d36612e8297b7959cd20639eb84696a974463a7ffc9a0dbf7cfcae
|
7
|
+
data.tar.gz: 135af776a81c32012398322b829f7d810d28114ce2490e8da0dc7d42237255d09d43165eed4e2bed65886feaf0f8fa1f3017078d0ea3f59ba65ac3cb5ca4622b
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Tomas Kohout <email@tomaskohout.cz>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# firebase plugin
|
2
|
+
|
3
|
+
[](https://rubygems.org/gems/fastlane-plugin-firebase)
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
This project is a [fastlane](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-firebase`, add it to your project by running:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
fastlane add_plugin firebase
|
11
|
+
```
|
12
|
+
|
13
|
+
## About firebase
|
14
|
+
|
15
|
+
An unofficial tool to access Firebase project settings. It allows you to create new clients (apps) and delete existing ones. It also allows to download config files (GoogleInfo.plist for ios and google-services.json for android) and upload push notification certificates (ios).
|
16
|
+
|
17
|
+
## Disclaimer
|
18
|
+
**!! Important !!**
|
19
|
+
|
20
|
+
This tool uses internal firebase api and logs-in through google login using web scrapper tool. Until firebase provides official api, this is all we got. The use of web-based google login might result in email warnings from google about new login from unknown device. We recommend creating an extra account for the use of the firebase plugin and set it with limited permissions. We give no warranties whatsoever.
|
21
|
+
|
22
|
+
|
23
|
+
### Actions
|
24
|
+
|
25
|
+
|
26
|
+
List all projects and clients
|
27
|
+
|
28
|
+
```
|
29
|
+
firebase_list
|
30
|
+
```
|
31
|
+
|
32
|
+
|
33
|
+
Add client to a project and download config file
|
34
|
+
|
35
|
+
```
|
36
|
+
firebase_add_client
|
37
|
+
```
|
38
|
+
|
39
|
+
|
40
|
+
Remove existing client from a project
|
41
|
+
|
42
|
+
```
|
43
|
+
firebase_delete_client
|
44
|
+
```
|
45
|
+
|
46
|
+
Upload push certificate to a client
|
47
|
+
|
48
|
+
```
|
49
|
+
firebase_upload_certificate
|
50
|
+
```
|
51
|
+
|
52
|
+
Download config file for a client
|
53
|
+
|
54
|
+
```
|
55
|
+
firebase_download_config
|
56
|
+
```
|
57
|
+
|
58
|
+
|
59
|
+
## Example
|
60
|
+
|
61
|
+
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
|
62
|
+
|
63
|
+
|
64
|
+
## Run tests for this plugin
|
65
|
+
|
66
|
+
To run both the tests, and code style validation, run
|
67
|
+
|
68
|
+
```
|
69
|
+
rake
|
70
|
+
```
|
71
|
+
|
72
|
+
To automatically fix many of the styling issues, use
|
73
|
+
```
|
74
|
+
rubocop -a
|
75
|
+
```
|
76
|
+
|
77
|
+
## Issues and Feedback
|
78
|
+
|
79
|
+
For any other issues and feedback about this plugin, please submit it to this repository.
|
80
|
+
|
81
|
+
## Troubleshooting
|
82
|
+
|
83
|
+
If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
|
84
|
+
|
85
|
+
## Using `fastlane` Plugins
|
86
|
+
|
87
|
+
For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
|
88
|
+
|
89
|
+
## About `fastlane`
|
90
|
+
|
91
|
+
`fastlane` is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
|
92
|
+
|
93
|
+
## Warning
|
94
|
+
|
95
|
+
DISCLAIMER OF WARRANTIES AND LIMITATION OF LIABILITY.
|
96
|
+
|
97
|
+
UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
98
|
+
|
99
|
+
TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
100
|
+
|
101
|
+
THE DISCLAIMER OF WARRANTIES AND LIMITATION OF LIABILITY PROVIDED ABOVE SHALL BE INTERPRETED IN A MANNER THAT, TO THE EXTENT POSSIBLE, MOST CLOSELY APPROXIMATES AN ABSOLUTE DISCLAIMER AND WAIVER OF ALL LIABILITY.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'fastlane/plugin/firebase/version'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Firebase
|
5
|
+
# Return all .rb files inside the "actions" and "helper" directory
|
6
|
+
def self.all_classes
|
7
|
+
Dir[File.expand_path('**/{actions,helper,lib}/*.rb', File.dirname(__FILE__))]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# By default we want to import all available actions and helpers
|
13
|
+
# A plugin can contain any number of actions and plugins
|
14
|
+
Fastlane::Firebase.all_classes.each do |current|
|
15
|
+
require current
|
16
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class FirebaseAddClientAction < Action
|
4
|
+
|
5
|
+
def self.run(params)
|
6
|
+
manager = Firebase::Manager.new
|
7
|
+
#Login
|
8
|
+
api = manager.login(params[:username])
|
9
|
+
|
10
|
+
#Select project
|
11
|
+
project = manager.select_project(params[:project_number])
|
12
|
+
|
13
|
+
# Client input
|
14
|
+
type = params[:type].to_sym
|
15
|
+
|
16
|
+
bundle_id = params[:bundle_id]
|
17
|
+
name = params[:name]
|
18
|
+
appstore_id = params[:appstore_id]
|
19
|
+
|
20
|
+
# Add client
|
21
|
+
client = api.add_client(project["projectNumber"], type, bundle_id, name, appstore_id)
|
22
|
+
|
23
|
+
if params[:download_config] then
|
24
|
+
#Download config
|
25
|
+
config = api.download_config_file(project["projectNumber"], client["clientId"])
|
26
|
+
path = File.join(params[:output_path], params[:output_name] || config.filename)
|
27
|
+
config.save(path)
|
28
|
+
|
29
|
+
UI.success "Successfuly saved config at #{path}"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.description
|
35
|
+
"An unofficial tool to access Firebase"
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.authors
|
39
|
+
["Tomas Kohout"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.return_value
|
43
|
+
# If your method provides a return value, you can describe here what it does
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.details
|
47
|
+
# Optional:
|
48
|
+
"Firebase helps you list your projects, create applications, download configuration files and more..."
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.available_options
|
52
|
+
[
|
53
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
54
|
+
env_name: "FIREBASE_USERNAME",
|
55
|
+
description: "Username for your google account",
|
56
|
+
optional: false),
|
57
|
+
FastlaneCore::ConfigItem.new(key: :project_number,
|
58
|
+
env_name: "FIREBASE_PROJECT_NUMBER",
|
59
|
+
description: "Project number",
|
60
|
+
optional: true),
|
61
|
+
FastlaneCore::ConfigItem.new(key: :download_config,
|
62
|
+
env_name: "FIREBASE_DOWNLOAD_CONFIG",
|
63
|
+
description: "Should download config for created client",
|
64
|
+
optional: false,
|
65
|
+
default_value: true),
|
66
|
+
FastlaneCore::ConfigItem.new(key: :type,
|
67
|
+
env_name: "FIREBASE_TYPE",
|
68
|
+
description: "Type of client (ios, android)",
|
69
|
+
verify_block: proc do |value|
|
70
|
+
types = [:ios, :android]
|
71
|
+
UI.user_error!("Type must be in #{types}") unless types.include?(value.to_sym)
|
72
|
+
end
|
73
|
+
),
|
74
|
+
FastlaneCore::ConfigItem.new(key: :bundle_id,
|
75
|
+
env_name: "FIREBASE_BUNDLE_ID",
|
76
|
+
description: "Bundle ID (package name)",
|
77
|
+
optional: false),
|
78
|
+
FastlaneCore::ConfigItem.new(key: :name,
|
79
|
+
env_name: "FIREBASE_BUNDLE_ID",
|
80
|
+
description: "Display name",
|
81
|
+
optional: true),
|
82
|
+
FastlaneCore::ConfigItem.new(key: :appstore_id,
|
83
|
+
env_name: "FIREBASE_APPSTORE_ID",
|
84
|
+
description: "AppStore ID",
|
85
|
+
optional: true),
|
86
|
+
FastlaneCore::ConfigItem.new(key: :output_path,
|
87
|
+
env_name: "FIREBASE_OUTPUT_PATH",
|
88
|
+
description: "Path for the downloaded config",
|
89
|
+
optional: false,
|
90
|
+
default_value: "./"),
|
91
|
+
FastlaneCore::ConfigItem.new(key: :output_name,
|
92
|
+
env_name: "FIREBASE_OUTPUT_NAME",
|
93
|
+
description: "Name of the downloaded file",
|
94
|
+
optional: true)
|
95
|
+
]
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.is_supported?(platform)
|
99
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
100
|
+
# See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
|
101
|
+
#
|
102
|
+
# [:ios, :mac, :android].include?(platform)
|
103
|
+
true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class FirebaseDeleteClientAction < Action
|
4
|
+
|
5
|
+
def self.run(params)
|
6
|
+
manager = Firebase::Manager.new
|
7
|
+
#Login
|
8
|
+
api = manager.login(params[:username])
|
9
|
+
|
10
|
+
#Select project
|
11
|
+
project = manager.select_project(params[:project_number])
|
12
|
+
|
13
|
+
#Select project
|
14
|
+
client = manager.select_client(project, params[:client_id])
|
15
|
+
|
16
|
+
#Confirm
|
17
|
+
if !params[:force] then
|
18
|
+
UI.error "Caution, this is a permanent action. Deleting your app will delete the corresponding Analytics data, but not your app's API keys or OAuth clients."
|
19
|
+
UI.confirm "Are you sure to delete #{client["clientId"]} (#{client["displayName"]})?"
|
20
|
+
end
|
21
|
+
|
22
|
+
#Delete
|
23
|
+
api.delete_client(project["projectNumber"], client["clientId"])
|
24
|
+
|
25
|
+
UI.success "Successfuly deleted #{client["clientId"]}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.description
|
29
|
+
"An unofficial tool to access Firebase"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.authors
|
33
|
+
["Tomas Kohout"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.return_value
|
37
|
+
# If your method provides a return value, you can describe here what it does
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.details
|
41
|
+
# Optional:
|
42
|
+
"Firebase helps you list your projects, create applications, download configuration files and more..."
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.available_options
|
46
|
+
[
|
47
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
48
|
+
env_name: "FIREBASE_USERNAME",
|
49
|
+
description: "Username for your google account",
|
50
|
+
optional: false),
|
51
|
+
FastlaneCore::ConfigItem.new(key: :project_number,
|
52
|
+
env_name: "FIREBASE_PROJECT_NUMBER",
|
53
|
+
description: "Project number",
|
54
|
+
optional: true),
|
55
|
+
FastlaneCore::ConfigItem.new(key: :client_id,
|
56
|
+
env_name: "FIREBASE_CLIENT_ID",
|
57
|
+
description: "Client ID to be deleted",
|
58
|
+
optional: true),
|
59
|
+
FastlaneCore::ConfigItem.new(key: :force,
|
60
|
+
env_name: "FIREBASE_FORCE",
|
61
|
+
description: "Force delete",
|
62
|
+
default_value: false)
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.is_supported?(platform)
|
67
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
68
|
+
# See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
|
69
|
+
#
|
70
|
+
# [:ios, :mac, :android].include?(platform)
|
71
|
+
true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class FirebaseDownloadConfigAction < Action
|
4
|
+
|
5
|
+
def self.run(params)
|
6
|
+
manager = Firebase::Manager.new
|
7
|
+
|
8
|
+
# Login
|
9
|
+
api = manager.login(params[:username])
|
10
|
+
|
11
|
+
#Select project
|
12
|
+
project = manager.select_project(params[:project_number])
|
13
|
+
project_number = project["projectNumber"]
|
14
|
+
|
15
|
+
#Select client
|
16
|
+
client = manager.select_client(project, params[:client_id])
|
17
|
+
client_id = client["clientId"]
|
18
|
+
|
19
|
+
#Download
|
20
|
+
config = api.download_config_file(project_number, client_id)
|
21
|
+
path = File.join(params[:output_path], params[:output_name] || config.filename)
|
22
|
+
config.save(path)
|
23
|
+
|
24
|
+
UI.success "Successfuly saved config at #{path}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.description
|
28
|
+
"An unofficial tool to access Firebase"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.authors
|
32
|
+
["Tomas Kohout"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.return_value
|
36
|
+
# If your method provides a return value, you can describe here what it does
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.details
|
40
|
+
# Optional:
|
41
|
+
"Firebase helps you list your projects, create applications, download configuration files and more..."
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.available_options
|
45
|
+
[
|
46
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
47
|
+
env_name: "FIREBASE_USERNAME",
|
48
|
+
description: "Username for your google account",
|
49
|
+
optional: false),
|
50
|
+
FastlaneCore::ConfigItem.new(key: :project_number,
|
51
|
+
env_name: "FIREBASE_PROJECT_NUMBER",
|
52
|
+
description: "Project number",
|
53
|
+
optional: true),
|
54
|
+
FastlaneCore::ConfigItem.new(key: :client_id,
|
55
|
+
env_name: "FIREBASE_CLIENT_ID",
|
56
|
+
description: "Project client id",
|
57
|
+
optional: true),
|
58
|
+
FastlaneCore::ConfigItem.new(key: :output_path,
|
59
|
+
env_name: "FIREBASE_OUTPUT_PATH",
|
60
|
+
description: "Path for the downloaded config",
|
61
|
+
optional: false,
|
62
|
+
default_value: "./"),
|
63
|
+
FastlaneCore::ConfigItem.new(key: :output_name,
|
64
|
+
env_name: "FIREBASE_OUTPUT_NAME",
|
65
|
+
description: "Name of the downloaded file",
|
66
|
+
optional: true)
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.is_supported?(platform)
|
71
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
72
|
+
# See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
|
73
|
+
#
|
74
|
+
# [:ios, :mac, :android].include?(platform)
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class FirebaseListAction < Action
|
4
|
+
require 'security'
|
5
|
+
|
6
|
+
def self.run(params)
|
7
|
+
manager = Firebase::Manager.new
|
8
|
+
# Login
|
9
|
+
api = manager.login(params[:username])
|
10
|
+
|
11
|
+
# List projects
|
12
|
+
projects = api.project_list()
|
13
|
+
projects.each_with_index { |p, i|
|
14
|
+
UI.message "#{i+1}. #{p["displayName"]}"
|
15
|
+
p["clientSummary"].sort {|left, right| left["clientId"] <=> right["clientId"] }.each_with_index { |client, j|
|
16
|
+
UI.message " - #{client["clientId"]} (#{client["displayName"]})"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.description
|
22
|
+
"An unofficial tool to access Firebase"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.authors
|
26
|
+
["Tomas Kohout"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.return_value
|
30
|
+
# If your method provides a return value, you can describe here what it does
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.details
|
34
|
+
# Optional:
|
35
|
+
"Firebase helps you list your projects, create applications, download configuration files and more..."
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.available_options
|
39
|
+
[
|
40
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
41
|
+
env_name: "FIREBASE_USERNAME",
|
42
|
+
description: "Username for your google account",
|
43
|
+
optional: false),
|
44
|
+
FastlaneCore::ConfigItem.new(key: :project_number,
|
45
|
+
env_name: "FIREBASE_PROJECT_NUMBER",
|
46
|
+
description: "Project number",
|
47
|
+
optional: true)
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.is_supported?(platform)
|
52
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
53
|
+
# See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
|
54
|
+
#
|
55
|
+
# [:ios, :mac, :android].include?(platform)
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class FirebaseUploadCertificateAction < Action
|
4
|
+
require 'security'
|
5
|
+
|
6
|
+
def self.server_name
|
7
|
+
"firebase.google.com"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.run(params)
|
11
|
+
manager = Firebase::Manager.new
|
12
|
+
|
13
|
+
# Login
|
14
|
+
api = manager.login(params[:username])
|
15
|
+
|
16
|
+
p12_path = params[:p12_path]
|
17
|
+
|
18
|
+
#Select project
|
19
|
+
project = manager.select_project(params[:project_number])
|
20
|
+
project_number = project["projectNumber"]
|
21
|
+
|
22
|
+
#Select client
|
23
|
+
client = manager.select_client(project, params[:client_id])
|
24
|
+
client_id = client["clientId"]
|
25
|
+
|
26
|
+
#Select certificate type
|
27
|
+
type = UI.select("Select type:", [:development, :production])
|
28
|
+
|
29
|
+
#Base64 certificate
|
30
|
+
certificate_value = Base64.encode64(File.open(p12_path, "rb").read).delete!("\n")
|
31
|
+
|
32
|
+
UI.message "Uploading certificate ..."
|
33
|
+
cert_password = params[:p12_password]
|
34
|
+
|
35
|
+
begin
|
36
|
+
api.upload_certificate(project_number, client_id, type, certificate_value, cert_password)
|
37
|
+
rescue Firebase::Api::BadRequestError => e
|
38
|
+
if e.message.start_with? "3000" then
|
39
|
+
UI.error e.message
|
40
|
+
cert_password = UI.password("Password for certificate")
|
41
|
+
retry
|
42
|
+
elsif e.message.start_with? "3003" then
|
43
|
+
UI.user_error! "The Bundle ID in the certificate does not match the Bundle ID you entered."
|
44
|
+
return
|
45
|
+
end
|
46
|
+
UI.crash! e.message
|
47
|
+
end
|
48
|
+
|
49
|
+
UI.success "Successfully uploaded certificate"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def self.description
|
54
|
+
"An unofficial tool to access Firebase"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.authors
|
58
|
+
["Tomas Kohout"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.return_value
|
62
|
+
# If your method provides a return value, you can describe here what it does
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.details
|
66
|
+
# Optional:
|
67
|
+
"Firebase helps you list your projects, create applications, download configuration files and more..."
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.available_options
|
71
|
+
[
|
72
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
73
|
+
env_name: "FIREBASE_USERNAME",
|
74
|
+
description: "Username for your google account",
|
75
|
+
optional: false),
|
76
|
+
FastlaneCore::ConfigItem.new(key: :p12_path,
|
77
|
+
env_name: "FIREBASE_P12PATH",
|
78
|
+
description: "Path to certificate",
|
79
|
+
optional: false),
|
80
|
+
FastlaneCore::ConfigItem.new(key: :p12_password,
|
81
|
+
env_name: "FIREBASE_PASSWORD",
|
82
|
+
description: "Password to the certicate",
|
83
|
+
optional: true),
|
84
|
+
FastlaneCore::ConfigItem.new(key: :project_number,
|
85
|
+
env_name: "FIREBASE_PROJECT_NUMBER",
|
86
|
+
description: "Project number",
|
87
|
+
optional: true),
|
88
|
+
FastlaneCore::ConfigItem.new(key: :client_id,
|
89
|
+
env_name: "FIREBASE_CLIENT_ID",
|
90
|
+
description: "Project client id",
|
91
|
+
optional: true)
|
92
|
+
]
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.is_supported?(platform)
|
96
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
97
|
+
# See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
|
98
|
+
#
|
99
|
+
# [:ios, :mac, :android].include?(platform)
|
100
|
+
true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Helper
|
3
|
+
class FirebaseHelper
|
4
|
+
# class methods that you define here become available in your action
|
5
|
+
# as `Helper::FirebaseHelper.your_method`
|
6
|
+
#
|
7
|
+
def self.show_message
|
8
|
+
UI.message("Hello from the firebase plugin helper!")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Firebase
|
3
|
+
|
4
|
+
class Api
|
5
|
+
class LoginError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class BadRequestError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'mechanize'
|
12
|
+
require 'digest/sha1'
|
13
|
+
require 'json'
|
14
|
+
require 'cgi'
|
15
|
+
|
16
|
+
def initialize(email, password)
|
17
|
+
@agent = Mechanize.new
|
18
|
+
@base_url = "https://console.firebase.google.com"
|
19
|
+
@sdk_url = "https://mobilesdk-pa.clients6.google.com/"
|
20
|
+
@login_url = "https://accounts.google.com/ServiceLogin"
|
21
|
+
|
22
|
+
login(email, password)
|
23
|
+
end
|
24
|
+
|
25
|
+
def login(email, password)
|
26
|
+
UI.message "Logging in to Google account #{email}"
|
27
|
+
|
28
|
+
page = @agent.get("#{@login_url}?passive=1209600&osid=1&continue=#{@base_url}/&followup=#{@base_url}/")
|
29
|
+
|
30
|
+
#First step - email
|
31
|
+
google_form = page.form()
|
32
|
+
google_form.Email = email
|
33
|
+
|
34
|
+
#Send
|
35
|
+
page = @agent.submit(google_form, google_form.buttons.first)
|
36
|
+
|
37
|
+
#Second step - password
|
38
|
+
google_form = page.form()
|
39
|
+
google_form.Passwd = password
|
40
|
+
|
41
|
+
#Send
|
42
|
+
page = @agent.submit(google_form, google_form.buttons.first)
|
43
|
+
match = page.search("script").text.scan(/\\x22api-key\\x22:\\x22(.*?)\\x22/)
|
44
|
+
|
45
|
+
if match.count == 1 then
|
46
|
+
@api_key = match[0][0]
|
47
|
+
@authorization_headers = create_authorization_headers()
|
48
|
+
UI.success "Successfuly logged in"
|
49
|
+
true
|
50
|
+
else
|
51
|
+
if error = page.at("#errormsg_0_Passwd") then
|
52
|
+
message = error.text.strip
|
53
|
+
else
|
54
|
+
message = "Unknown error"
|
55
|
+
end
|
56
|
+
raise LoginError, "Login failed: #{message}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def generate_sapisid_hash(time, sapisid, origin)
|
61
|
+
to_hash = time.to_s + " " + sapisid + " " + origin.to_s
|
62
|
+
|
63
|
+
hash = Digest::SHA1.hexdigest(to_hash)
|
64
|
+
sapisid_hash = time.to_s + "_" + hash
|
65
|
+
|
66
|
+
sapisid_hash
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_authorization_headers
|
70
|
+
cookie = @agent.cookie_jar.jar["google.com"]["/"]["SAPISID"]
|
71
|
+
sapisid = cookie.value
|
72
|
+
origin = @base_url
|
73
|
+
time = Time.now.to_i
|
74
|
+
|
75
|
+
sapisid_hash = generate_sapisid_hash(time, sapisid, origin)
|
76
|
+
|
77
|
+
cookies = @agent.cookie_jar.jar["google.com"]["/"].merge(@agent.cookie_jar.jar["console.firebase.google.com"]["/"])
|
78
|
+
cookie_header = cookies.map { |el, cookie| "#{el}=#{cookie.value}" }.join(";")
|
79
|
+
|
80
|
+
sapisid_hash = generate_sapisid_hash(time, sapisid, origin)
|
81
|
+
sapisid_header = "SAPISIDHASH #{sapisid_hash}"
|
82
|
+
|
83
|
+
json_headers = {
|
84
|
+
'Authorization' => sapisid_header,
|
85
|
+
'Cookie' => cookie_header,
|
86
|
+
'X-Origin' => origin
|
87
|
+
}
|
88
|
+
|
89
|
+
json_headers
|
90
|
+
end
|
91
|
+
|
92
|
+
def request_json(path, method = :get, parameters = Hash.new, headers = Hash.new)
|
93
|
+
begin
|
94
|
+
if method == :get then
|
95
|
+
parameters["key"] = @api_key
|
96
|
+
page = @agent.get("#{@sdk_url}#{path}", parameters, nil, headers.merge(@authorization_headers))
|
97
|
+
elsif method == :post then
|
98
|
+
headers['Content-Type'] = 'application/json'
|
99
|
+
page = @agent.post("#{@sdk_url}#{path}?key=#{@api_key}", parameters.to_json, headers.merge(@authorization_headers))
|
100
|
+
elsif method == :delete then
|
101
|
+
page = @agent.delete("#{@sdk_url}#{path}?key=#{@api_key}", parameters, headers.merge(@authorization_headers))
|
102
|
+
end
|
103
|
+
|
104
|
+
JSON.parse(page.body)
|
105
|
+
|
106
|
+
rescue Mechanize::ResponseCodeError => e
|
107
|
+
code = e.response_code.to_i
|
108
|
+
if code >= 400 && code < 500 then
|
109
|
+
if body = JSON.parse(e.page.body) then
|
110
|
+
raise BadRequestError, body["error"]["message"]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
UI.crash! e.page.body
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def project_list
|
118
|
+
UI.message "Retrieving project list"
|
119
|
+
json = request_json("v1/projects")
|
120
|
+
projects = json["project"]
|
121
|
+
UI.success "Found #{projects.count} projects"
|
122
|
+
projects
|
123
|
+
end
|
124
|
+
|
125
|
+
def add_client(project_number, type, bundle_id, app_name, ios_appstore_id )
|
126
|
+
parameters = {
|
127
|
+
"requestHeader" => { "clientVersion" => "FIREBASE" },
|
128
|
+
"displayName" => app_name || ""
|
129
|
+
}
|
130
|
+
|
131
|
+
case type
|
132
|
+
when :ios
|
133
|
+
parameters["iosData"] = {
|
134
|
+
"bundleId" => bundle_id,
|
135
|
+
"iosAppStoreId" => ios_appstore_id || ""
|
136
|
+
}
|
137
|
+
when :android
|
138
|
+
parameters["androidData"] = {
|
139
|
+
"packageName" => bundle_id
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
json = request_json("v1/projects/#{project_number}/clients", :post, parameters)
|
144
|
+
if client = json["client"] then
|
145
|
+
UI.success "Successfuly added client #{bundle_id}"
|
146
|
+
client
|
147
|
+
else
|
148
|
+
UI.error "Client could not be added"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def delete_client(project_number, client_id)
|
153
|
+
json = request_json("v1/projects/#{project_number}/clients/#{client_id}", :delete)
|
154
|
+
end
|
155
|
+
|
156
|
+
def upload_certificate(project_number, client_id, type, certificate_value, certificate_password)
|
157
|
+
|
158
|
+
prefix = type == :development ? "debug" : "prod"
|
159
|
+
|
160
|
+
parameters = {
|
161
|
+
"#{prefix}ApnsCertificate" => {
|
162
|
+
"certificateValue" => certificate_value,
|
163
|
+
"apnsPassword" => certificate_password
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
json = request_json("v1/projects/#{project_number}/clients/#{client_id}:setApnsCertificate", :post, parameters)
|
168
|
+
end
|
169
|
+
|
170
|
+
def download_config_file(project_number, client_id)
|
171
|
+
|
172
|
+
request = "[\"getArtifactRequest\",null,\"#{client_id}\",\"1\",\"#{project_number}\"]"
|
173
|
+
code = (client_id.start_with? "ios") ? "1" : "2"
|
174
|
+
url = @base_url + "/m/mobilesdk/projects/" + project_number + "/clients/" + CGI.escape(client_id) + "/artifacts/#{code}?param=" + CGI.escape(request)
|
175
|
+
UI.message "Downloading config file"
|
176
|
+
begin
|
177
|
+
config = @agent.get url
|
178
|
+
UI.success "Successfuly downloaded #{config.filename}"
|
179
|
+
config
|
180
|
+
rescue Mechanize::ResponseCodeError => e
|
181
|
+
UI.crash! e.page.body
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'commander'
|
2
|
+
require 'fastlane/version'
|
3
|
+
|
4
|
+
|
5
|
+
module Firebase
|
6
|
+
class CommandsGenerator
|
7
|
+
include Commander::Methods
|
8
|
+
|
9
|
+
def self.start
|
10
|
+
self.new.run
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
program :name, 'firebase'
|
15
|
+
program :version, Fastlane::VERSION
|
16
|
+
program :description, 'CLI for \'PEM\' - Automatically generate and renew your push notification profiles'
|
17
|
+
program :help, 'Author', 'Felix Krause <pem@krausefx.com>'
|
18
|
+
program :help, 'Website', 'https://fastlane.tools'
|
19
|
+
program :help, 'GitHub', 'https://github.com/fastlane/PEM'
|
20
|
+
program :help_formatter, :compact
|
21
|
+
|
22
|
+
global_option('--verbose') { FastlaneCore::Globals.verbose = true }
|
23
|
+
|
24
|
+
command :list do |c|
|
25
|
+
c.syntax = 'fastlane firebase list'
|
26
|
+
c.description = 'Lists all projects in your firebase'
|
27
|
+
|
28
|
+
FastlaneCore::CommanderGenerator.new.generate(Firebase::Options.available_options, command: c)
|
29
|
+
|
30
|
+
c.action do |args, options|
|
31
|
+
Firebase.config = FastlaneCore::Configuration.create(Firebase::Options.available_options, options.__hash__)
|
32
|
+
#Firebase::Manager.start
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
default_command :list
|
37
|
+
|
38
|
+
run!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Firebase
|
3
|
+
|
4
|
+
class Manager
|
5
|
+
def server_name
|
6
|
+
"firebase.google.com"
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
def login(username)
|
12
|
+
item = Security::InternetPassword.find(server: server_name(), account: username)
|
13
|
+
password = item.password if item
|
14
|
+
|
15
|
+
begin
|
16
|
+
password = UI.input("Password for #{username}") unless password
|
17
|
+
|
18
|
+
#Api instance
|
19
|
+
@api = Firebase::Api.new(username, password)
|
20
|
+
|
21
|
+
#Store password
|
22
|
+
Security::InternetPassword.add(server_name(), username, password) unless item.password == password
|
23
|
+
|
24
|
+
@api
|
25
|
+
rescue Firebase::Api::LoginError => e
|
26
|
+
password = nil
|
27
|
+
UI.error e.message
|
28
|
+
retry
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def select_project(project_number)
|
33
|
+
projects = @api.project_list()
|
34
|
+
if project = projects.select {|p| p["projectNumber"] == project_number }.first then
|
35
|
+
project
|
36
|
+
else
|
37
|
+
options = projects.map { |p| p["displayName"] }
|
38
|
+
index = select_index("Select project:", options)
|
39
|
+
projects[index]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def select_client(project, client_id)
|
44
|
+
clients = project["clientSummary"].sort {|left, right| left["clientId"] <=> right["clientId"] }
|
45
|
+
|
46
|
+
if client = clients.select {|c| c["clientId"] == client_id }.first then
|
47
|
+
client
|
48
|
+
else
|
49
|
+
options = clients.map { |p| "#{p["clientId"]} (#{p["displayName"]})" }
|
50
|
+
index = select_index("Select client:", options)
|
51
|
+
clients[index]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def select_index(text, options)
|
56
|
+
selected = UI.select(text, options)
|
57
|
+
return options.index(selected)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'fastlane_core'
|
2
|
+
require 'credentials_manager'
|
3
|
+
|
4
|
+
module Firebase
|
5
|
+
class Options
|
6
|
+
def self.available_options
|
7
|
+
[
|
8
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
9
|
+
env_name: "FIREBASE_USERNAME",
|
10
|
+
description: "Username for the google account"),
|
11
|
+
]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fastlane-plugin-firebase
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomas Kohout
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
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: rspec
|
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: rake
|
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: rubocop
|
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: fastlane
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.5.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.5.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mechanize
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email: email@tomaskohout.cz
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- LICENSE
|
104
|
+
- README.md
|
105
|
+
- lib/fastlane/plugin/firebase.rb
|
106
|
+
- lib/fastlane/plugin/firebase/actions/firebase_add_client_action.rb
|
107
|
+
- lib/fastlane/plugin/firebase/actions/firebase_delete_client_action.rb
|
108
|
+
- lib/fastlane/plugin/firebase/actions/firebase_download_config_action.rb
|
109
|
+
- lib/fastlane/plugin/firebase/actions/firebase_list_action.rb
|
110
|
+
- lib/fastlane/plugin/firebase/actions/firebase_upload_certificate_action.rb
|
111
|
+
- lib/fastlane/plugin/firebase/helper/firebase_helper.rb
|
112
|
+
- lib/fastlane/plugin/firebase/lib/api.rb
|
113
|
+
- lib/fastlane/plugin/firebase/lib/commands_generator.rb
|
114
|
+
- lib/fastlane/plugin/firebase/lib/manager.rb
|
115
|
+
- lib/fastlane/plugin/firebase/lib/options.rb
|
116
|
+
- lib/fastlane/plugin/firebase/version.rb
|
117
|
+
homepage: https://github.com/tkohout/fastlane-firebase-plugin
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.6.11
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Unofficial tool to access Firebase project settings
|
141
|
+
test_files: []
|