├── .gitignore ├── .rspec ├── .rubocop.yml ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── art ├── firebase-test-lab_cover.jpg ├── firebase_test_lab_logo.png ├── screenshot_github.png └── screenshot_slack.png ├── fastlane-plugin-firebase_test_lab_android.gemspec ├── fastlane ├── Fastfile ├── Pluginfile └── client-secret.json ├── lib └── fastlane │ └── plugin │ ├── firebase_test_lab_android.rb │ └── firebase_test_lab_android │ ├── actions │ └── firebase_test_lab_android_action.rb │ ├── helper │ ├── Helper.rb │ ├── github_notifier.rb │ └── slack_notifier.rb │ └── version.rb └── spec └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS 2 | .DS_store 3 | 4 | # Gem 5 | *.gem 6 | Gemfile.lock 7 | vendor/bundle 8 | .bundle/config 9 | 10 | ## Documentation cache and generated files: 11 | /.yardoc/ 12 | /_yardoc/ 13 | /doc/ 14 | /rdoc/ 15 | fastlane/README.md 16 | fastlane/report.xml 17 | coverage 18 | test-results 19 | client-secret.json 20 | 21 | 22 | ## Intellij 23 | .idea/ 24 | 25 | *.apk 26 | console_output.log 27 | .results/ 28 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --color 3 | --format d 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Style/MultipleComparison: 3 | Enabled: false 4 | Style/PercentLiteralDelimiters: 5 | Enabled: false 6 | Style/ClassCheck: 7 | EnforcedStyle: kind_of? 8 | Style/FrozenStringLiteralComment: 9 | Enabled: false 10 | Style/SafeNavigation: 11 | Enabled: false 12 | Performance/RegexpMatch: 13 | Enabled: false 14 | Performance/StringReplacement: 15 | Enabled: false 16 | Style/NumericPredicate: 17 | Enabled: false 18 | Metrics/BlockLength: 19 | Enabled: false 20 | Metrics/ModuleLength: 21 | Enabled: false 22 | Style/VariableNumber: 23 | Enabled: false 24 | Style/MethodMissing: 25 | Enabled: false 26 | MultilineBlockChain: 27 | Enabled: false 28 | Style/NumericLiteralPrefix: 29 | Enabled: false 30 | Style/TernaryParentheses: 31 | Enabled: false 32 | Style/EmptyMethod: 33 | Enabled: false 34 | Style/BracesAroundHashParameters: 35 | Enabled: false 36 | Lint/UselessAssignment: 37 | Exclude: 38 | - "**/spec/**/*" 39 | Require/MissingRequireStatement: 40 | Exclude: 41 | - "**/spec/**/*.rb" 42 | - "**/spec_helper.rb" 43 | - spaceship/lib/spaceship/babosa_fix.rb 44 | - "**/Fastfile" 45 | - "**/*.gemspec" 46 | - rakelib/**/* 47 | - "**/*.rake" 48 | - "**/Rakefile" 49 | - fastlane/**/* 50 | - supply/**/* 51 | Layout/IndentHash: 52 | Enabled: false 53 | Layout/AlignHash: 54 | Enabled: false 55 | Layout/DotPosition: 56 | Enabled: false 57 | Style/DoubleNegation: 58 | Enabled: false 59 | Style/SymbolArray: 60 | Enabled: false 61 | Layout/IndentHeredoc: 62 | Enabled: false 63 | Style/MixinGrouping: 64 | Exclude: 65 | - "**/spec/**/*" 66 | Lint/HandleExceptions: 67 | Enabled: false 68 | Lint/UnusedBlockArgument: 69 | Enabled: false 70 | Lint/AmbiguousBlockAssociation: 71 | Enabled: false 72 | Style/GlobalVars: 73 | Enabled: false 74 | Style/ClassAndModuleChildren: 75 | Enabled: false 76 | Style/SpecialGlobalVars: 77 | Enabled: false 78 | Metrics/AbcSize: 79 | Enabled: false 80 | Metrics/MethodLength: 81 | Enabled: false 82 | Metrics/CyclomaticComplexity: 83 | Enabled: false 84 | Style/WordArray: 85 | MinSize: 19 86 | Style/SignalException: 87 | Enabled: false 88 | Style/RedundantReturn: 89 | Enabled: false 90 | Style/IfUnlessModifier: 91 | Enabled: false 92 | Style/AndOr: 93 | Enabled: true 94 | EnforcedStyle: conditionals 95 | Metrics/ClassLength: 96 | Max: 320 97 | Metrics/LineLength: 98 | Max: 370 99 | Metrics/ParameterLists: 100 | Max: 17 101 | Metrics/PerceivedComplexity: 102 | Max: 18 103 | Style/GuardClause: 104 | Enabled: false 105 | Style/StringLiterals: 106 | Enabled: false 107 | Style/ConditionalAssignment: 108 | Enabled: false 109 | Style/RedundantSelf: 110 | Enabled: false 111 | Lint/UnusedMethodArgument: 112 | Enabled: false 113 | Lint/ParenthesesAsGroupedExpression: 114 | Exclude: 115 | - "**/spec/**/*" 116 | Style/PredicateName: 117 | Enabled: false 118 | Style/PerlBackrefs: 119 | Enabled: false 120 | Layout/SpaceAroundOperators: 121 | Exclude: 122 | - "**/spec/actions_specs/xcodebuild_spec.rb" 123 | AllCops: 124 | TargetRubyVersion: 2.0 125 | Include: 126 | - "*/lib/assets/*Template" 127 | - "*/lib/assets/*TemplateAndroid" 128 | Exclude: 129 | - "**/lib/assets/custom_action_template.rb" 130 | - "./vendor/**/*" 131 | - "**/lib/assets/DefaultFastfileTemplate" 132 | - "**/lib/assets/MatchfileTemplate" 133 | - "**/spec/fixtures/broken_files/broken_file.rb" 134 | Style/FileName: 135 | Exclude: 136 | - "**/Dangerfile" 137 | - "**/Brewfile" 138 | - "**/Gemfile" 139 | - "**/Podfile" 140 | - "**/Rakefile" 141 | - "**/Fastfile" 142 | - "**/Deliverfile" 143 | - "**/Snapfile" 144 | - "**/*.gemspec" 145 | Style/Documentation: 146 | Enabled: false 147 | Style/MutableConstant: 148 | Enabled: false 149 | Style/ZeroLengthPredicate: 150 | Enabled: false 151 | Style/IfInsideElse: 152 | Enabled: false 153 | Style/CollectionMethods: 154 | Enabled: false 155 | CrossPlatform/ForkUsage: 156 | Exclude: 157 | - "**/plugins/template/**/*" 158 | Style/MethodCallWithArgsParentheses: 159 | Enabled: true 160 | IgnoredMethods: 161 | - require 162 | - require_relative 163 | - fastlane_require 164 | - gem 165 | - program 166 | - command 167 | - raise 168 | - attr_accessor 169 | - attr_reader 170 | - desc 171 | - lane 172 | - private_lane 173 | - platform 174 | - to 175 | - describe 176 | - it 177 | - be 178 | - context 179 | - before 180 | - after 181 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source('https://rubygems.org') 2 | 3 | gemspec 4 | 5 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') 6 | eval_gemfile(plugins_path) if File.exist?(plugins_path) 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 CyberAgent, Inc. 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firebase Test Lab plugin for fastlane 2 | 3 |

4 | 5 |

6 | 7 | Inspired by [Firebase test lab plugin for ios](https://github.com/fastlane/fastlane-plugin-firebase_test_lab) 8 | 9 | 10 | ## Getting Started 11 | 12 | [![License](https://img.shields.io/github/license/cats-oss/fastlane-plugin-firebase_test_lab_android.svg)](https://github.com/cats-oss/fastlane-plugin-firebase_test_lab_android/blob/master/LICENSE) 13 | [![Gem](https://img.shields.io/gem/v/fastlane-plugin-firebase_test_lab_android.svg?style=flat)](https://rubygems.org/gems/fastlane-plugin-firebase_test_lab_android) 14 | [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-firebase_test_lab_android) 15 | 16 | ### Step 0. Enable the Google Cloud Testing API and Cloud Tool Results API 17 | 18 | You must enable the Google Cloud Testing API and Cloud Tool Results API in the [Google Developers Console API Library page](https://console.developers.google.com/apis/library). 19 | 20 | ### Step 1. First of all, get started with Firebase Test Lab from the gcloud Command Line 21 | 22 | Please check [Firebase documents](https://firebase.google.com/docs/test-lab/android/command-line) 23 | 24 | Using with Google Cloud SDK. 25 | ``` 26 | # gcloud firebase test android run \ 27 | --type robo \ 28 | --app app-debug-unaligned.apk \ 29 | --device model=Nexus6,version=24,locale=en,orientation=portrait \ 30 | --timeout 90s 31 | 32 | Have questions, feedback, or issues? Get support by visiting: 33 | https://firebase.google.com/support/ 34 | 35 | API [toolresults.googleapis.com] not enabled on project 36 | [************]. Would you like to enable and retry (this will take a 37 | few minutes)? (y/N)? y 38 | 39 | Enabling service [toolresults.googleapis.com] on project [************]... 40 | Operation "operations/acf.********-****-****-****-************" finished successfully. 41 | ... 42 | ... 43 | ... 44 | 45 | Robo testing complete. 46 | 47 | More details are available at [https://console.firebase.google.com/project/*******/testlab/histories/**********/matrices/********]. 48 | ┌─────────┬───────────────────────┬──────────────┐ 49 | │ OUTCOME │ TEST_AXIS_VALUE │ TEST_DETAILS │ 50 | ├─────────┼───────────────────────┼──────────────┤ 51 | │ Passed │ Nexus6-24-en-portrait │ -- │ 52 | └─────────┴───────────────────────┴──────────────┘ 53 | ``` 54 | 55 | ### Step 2. Add to your project 56 | This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-firebase_test_lab_android`, add it to your project by running: 57 | 58 | ``` 59 | fastlane add_plugin firebase_test_lab_android 60 | ``` 61 | 62 | ### Step 3. Find the devices you want to test on 63 | 64 | Using [gcloud tool](https://cloud.google.com/sdk/gcloud/), you can run. 65 | 66 | ``` 67 | gcloud firebase test android models list 68 | ``` 69 | 70 | to get a list of supported devices and their identifiers. 71 | 72 | Alternatively all available devices can also be seen [here](https://firebase.google.com/docs/test-lab/ios/available-testing-devices). 73 | 74 | 75 | ### Step 4. Add settings to your Fastfile 76 | 77 | Test your app with Firebase Test Lab with ease using fastlane. 78 | Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. 79 | 80 | ```ruby 81 | before_all do 82 | ENV["SLACK_URL"] = "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX" 83 | end 84 | 85 | lane :test do 86 | 87 | # Get Pull request number from CI 88 | pr_number = ENV["CI_PULL_REQUEST"] != nil ? ENV["CI_PULL_REQUEST"][/(?<=https:\/\/github.com\/cats-oss\/android\/pull\/)(.*)/] : nil 89 | 90 | # Upload to Firebase Test Lab 91 | firebase_test_lab_android( 92 | project_id: "cats-firebase", # Your Firebase project name. 93 | gcloud_service_key_file: "fastlane/client-secret.json", # File path containing the gcloud auth key. 94 | # gcloud_components_channel: "beta", # If you use gcloud component channel option (alpha/beta). 95 | type: "robo", # Optional: Test type (robo/instrumentation). 96 | devices: [ # Devices 97 | { 98 | model: "Nexus6P", 99 | version: "23", 100 | locale: "ja_JP", 101 | orientation: "portrait" 102 | }, 103 | { 104 | model: "Pixel2", 105 | version: "28" 106 | } 107 | ], 108 | app_apk: "app-debug.apk", # The path for your android app apk. 109 | # app_test_apk: "app-test.apk", # The path for your android instrumentation test apk. 110 | # use_orchestrator: false, # If you use orchestrator when set instrumentation test. 111 | console_log_file_name: "fastlane/console_output.log", 112 | timeout: "3m", 113 | firebase_test_lab_results_bucket: "firebase_cats_test_bucket", # If you want to naming bucket of GCS 114 | # firebase_test_lab_results_dir: "firebase_cats_test_dir", # If you want to naming results of GCS. (Maybe don't need it.) 115 | slack_url: ENV["SLACK_URL"], # If you want notify to Slack. 116 | 117 | # If you want notify to Github pull requests. 118 | github_owner: "******", # Owner name. 119 | github_repository: "************", # Repository name. 120 | github_pr_number: pr_number, # If you using run on CI that need pull request number for auto comment. 121 | github_api_token: ENV["GITHUB_API_TOKEN"], # https://github.com/settings/tokens 122 | download_dir: ".results", # If you want to download to the results of Firebase test lab. 123 | 124 | # If you want to ignore some social sign-in buttons. 125 | extra_options: "--robo-directives ignore:image_button_sign_in_twitter=,ignore:image_button_sign_in_instagram=" 126 | ) 127 | end 128 | ``` 129 | 130 | ### Finish. Check your slack or Github PR 131 | 132 | **If you set `slack_url: ENV["SLACK_URL"]` to Fastfile.** 133 | 134 | 135 | 136 | **If you set `github_owner, github_repository, github_pr_number, github_api_token` to Fastfile.** 137 | 138 | 139 | 140 | 141 | ## Issues and Feedback 142 | 143 | For any other issues and feedback about this plugin, please submit it to this repository. 144 | 145 | ## Troubleshooting 146 | 147 | If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide. 148 | 149 | ## Using _fastlane_ Plugins 150 | 151 | For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/). 152 | 153 | ## About _fastlane_ 154 | 155 | _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). 156 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | require 'rspec/core/rake_task' 4 | RSpec::Core::RakeTask.new 5 | 6 | require 'rubocop/rake_task' 7 | RuboCop::RakeTask.new(:rubocop) 8 | 9 | task(default: [:spec, :rubocop]) 10 | -------------------------------------------------------------------------------- /art/firebase-test-lab_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cats-oss/fastlane-plugin-firebase_test_lab_android/9082337cc39f956852eb2b6fd598025e4211da0b/art/firebase-test-lab_cover.jpg -------------------------------------------------------------------------------- /art/firebase_test_lab_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cats-oss/fastlane-plugin-firebase_test_lab_android/9082337cc39f956852eb2b6fd598025e4211da0b/art/firebase_test_lab_logo.png -------------------------------------------------------------------------------- /art/screenshot_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cats-oss/fastlane-plugin-firebase_test_lab_android/9082337cc39f956852eb2b6fd598025e4211da0b/art/screenshot_github.png -------------------------------------------------------------------------------- /art/screenshot_slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cats-oss/fastlane-plugin-firebase_test_lab_android/9082337cc39f956852eb2b6fd598025e4211da0b/art/screenshot_slack.png -------------------------------------------------------------------------------- /fastlane-plugin-firebase_test_lab_android.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | lib = File.expand_path("../lib", __FILE__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'fastlane/plugin/firebase_test_lab_android/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'fastlane-plugin-firebase_test_lab_android' 9 | spec.version = Fastlane::FirebaseTestLabAndroid::VERSION 10 | spec.author = 'wasabeef' 11 | spec.email = 'dadadada.chop@gmail.com' 12 | 13 | spec.summary = 'Test your app with Firebase Test Lab with ease using fastlane' 14 | spec.homepage = "https://github.com/cats-oss/fastlane-plugin-firebase_test_lab_android" 15 | spec.license = "MIT" 16 | 17 | spec.files = Dir["lib/**/*"] + %w(README.md LICENSE) 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | 21 | spec.add_development_dependency('pry') 22 | spec.add_development_dependency('bundler') 23 | spec.add_development_dependency('rspec') 24 | spec.add_development_dependency('rspec_junit_formatter') 25 | spec.add_development_dependency('rake') 26 | spec.add_development_dependency('rubocop', '0.49.1') 27 | spec.add_development_dependency('rubocop-require_tools') 28 | spec.add_development_dependency('simplecov') 29 | spec.add_development_dependency('fastlane', '>= 2.135.2') 30 | end 31 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | before_all do 2 | ENV["SLACK_URL"] = "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX" 3 | end 4 | 5 | lane :test do 6 | 7 | # Get Pull Request number 8 | pr_number = ENV["CI_PULL_REQUEST"] != nil ? ENV["CI_PULL_REQUEST"][/(?<=https:\/\/github.com\/cats-oss\/android\/pull\/)(.*)/] : nil 9 | 10 | # Upload to Firebase Test Lab 11 | firebase_test_lab_android( 12 | project_id: "cats-firebase", 13 | gcloud_service_key_file: "fastlane/client-secret.json", 14 | gcloud_components_channel: "beta", 15 | type: "robo", 16 | devices: [ 17 | { 18 | model: "Nexus6P", 19 | version: "23", 20 | locale: "ja_JP", 21 | orientation: "portrait" 22 | }, 23 | { 24 | model: "Pixel2", 25 | version: "28" 26 | } 27 | ], 28 | app_apk: "test.apk", 29 | console_log_file_name: "fastlane/console_output.log", 30 | timeout: "60s", 31 | firebase_test_lab_results_bucket: "firebase-test-lab-result-bucket", 32 | slack_url: ENV["SLACK_URL"], 33 | github_owner: "cats-oss", 34 | github_repository: "fastlane-plugin-firebase_test_lab_android", 35 | github_pr_number: pr_number, 36 | github_api_token: ENV["DANGER_GITHUB_API_TOKEN"], 37 | download_dir: ".results" 38 | ) 39 | end 40 | -------------------------------------------------------------------------------- /fastlane/Pluginfile: -------------------------------------------------------------------------------- 1 | # Autogenerated by fastlane 2 | -------------------------------------------------------------------------------- /fastlane/client-secret.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "cats-firebase", 4 | "private_key_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 5 | "private_key": "-----BEGIN PRIVATE KEY-----\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXX=\n-----END PRIVATE KEY-----\n", 6 | "client_email": "firebase-adminsdk-xxxxx@cats-firebase.iam.gserviceaccount.com", 7 | "client_id": "012345678901234567890", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-xxxxx%40cats-firebase.iam.gserviceaccount.com" 12 | } 13 | -------------------------------------------------------------------------------- /lib/fastlane/plugin/firebase_test_lab_android.rb: -------------------------------------------------------------------------------- 1 | require 'fastlane/plugin/firebase_test_lab_android/version' 2 | 3 | module Fastlane 4 | module FirebaseTestLabAndroid 5 | def self.all_classes 6 | Dir[File.expand_path('**/{actions,helper}/**/*.rb', File.dirname(__FILE__))] 7 | end 8 | end 9 | end 10 | 11 | # By default we want to import all available actions 12 | # A plugin can contain any number of actions and plugins 13 | Fastlane::FirebaseTestLabAndroid.all_classes.each do |current| 14 | require current 15 | end 16 | -------------------------------------------------------------------------------- /lib/fastlane/plugin/firebase_test_lab_android/actions/firebase_test_lab_android_action.rb: -------------------------------------------------------------------------------- 1 | require 'fastlane/action' 2 | require 'json' 3 | require 'fileutils' 4 | 5 | module Fastlane 6 | module Actions 7 | class FirebaseTestLabAndroidAction < Action 8 | 9 | def self.run(params) 10 | UI.message("Starting...") 11 | 12 | results_bucket = params[:firebase_test_lab_results_bucket] || "#{params[:project_id]}_test_results" 13 | results_dir = params[:firebase_test_lab_results_dir] || "firebase_test_result_#{DateTime.now.strftime('%Y-%m-%d-%H:%M:%S')}" 14 | 15 | # Set target project 16 | Helper.config(params[:project_id]) 17 | # Activate service account 18 | Helper.authenticate(params[:gcloud_service_key_file]) 19 | # Run Firebase Test Lab 20 | Helper.run_tests(params[:gcloud_components_channel], "--type #{params[:type]} "\ 21 | "--app #{params[:app_apk]} "\ 22 | "#{"--test #{params[:app_test_apk]} " unless params[:app_test_apk].nil?}"\ 23 | "#{"--use-orchestrator " if params[:type] == "instrumentation" && params[:use_orchestrator]}"\ 24 | "#{params[:devices].map { |d| "--device model=#{d[:model]},version=#{d[:version]},locale=#{d[:locale]},orientation=#{d[:orientation]} " }.join}"\ 25 | "--timeout #{params[:timeout]} "\ 26 | "--results-bucket #{results_bucket} "\ 27 | "--results-dir #{results_dir} "\ 28 | "#{params[:extra_options]} "\ 29 | "--format=json 1>#{Helper.if_need_dir(params[:console_log_file_name])}" 30 | ) 31 | 32 | # Sample data 33 | # [ 34 | # { 35 | # "axis_value": "Nexus6P-23-ja_JP-portrait", 36 | # "outcome": "Passed", 37 | # "test_details": "--" 38 | # }, 39 | # { 40 | # "axis_value": "Pixel2-28-en_US-portrait", 41 | # "outcome": "Passed", 42 | # "test_details": "--" 43 | # } 44 | # ] 45 | json = JSON.parse(File.read(params[:console_log_file_name])) 46 | UI.message("Test status: #{json}") 47 | 48 | # Fetch results 49 | download_dir = params[:download_dir] 50 | if download_dir 51 | UI.message("Fetch results from Firebase Test Lab results bucket") 52 | json.each do |status| 53 | axis = status["axis_value"] 54 | Helper.if_need_dir("#{download_dir}/#{axis}") 55 | Helper.copy_from_gcs("#{results_bucket}/#{results_dir}/#{axis}", download_dir) 56 | Helper.set_public("#{results_bucket}/#{results_dir}/#{axis}") 57 | end 58 | end 59 | 60 | # Notify to Slack 61 | if params[:slack_url] 62 | success, body = Helper.make_slack_text(json) 63 | SlackNotifier.notify(params[:slack_url], body, success) 64 | end 65 | 66 | # Notify to Github 67 | owner = params[:github_owner] 68 | repository = params[:github_repository] 69 | pr_number = params[:github_pr_number] 70 | api_token = params[:github_api_token] 71 | if owner && repository && pr_number && api_token 72 | prefix, comment = Helper.make_github_text(json, params[:project_id], results_bucket, results_dir, params[:type]) 73 | # Delete past comments 74 | GitHubNotifier.delete_comments(owner, repository, pr_number, prefix, api_token) 75 | GitHubNotifier.put_comment(owner, repository, pr_number, comment, api_token) 76 | end 77 | 78 | UI.message("Finishing...") 79 | end 80 | 81 | def self.description 82 | "Runs Android tests in Firebase Test Lab." 83 | end 84 | 85 | def self.authors 86 | ["Wasabeef"] 87 | end 88 | 89 | def self.return_value 90 | ["Authenticates with Google Cloud.", 91 | "Runs tests in Firebase Test Lab.", 92 | "Fetches the results to a local directory."].join("\n") 93 | end 94 | 95 | def self.details 96 | # Optional: 97 | "Test your app with Firebase Test Lab with ease using fastlane" 98 | end 99 | 100 | def self.available_options 101 | [FastlaneCore::ConfigItem.new(key: :project_id, 102 | env_name: "PROJECT_ID", 103 | description: "Your Firebase project id", 104 | is_string: true, 105 | optional: false), 106 | FastlaneCore::ConfigItem.new(key: :gcloud_service_key_file, 107 | env_name: "GCLOUD_SERVICE_KEY_FILE", 108 | description: "File path containing the gcloud auth key. Default: Created from GCLOUD_SERVICE_KEY environment variable", 109 | is_string: true, 110 | optional: false), 111 | FastlaneCore::ConfigItem.new(key: :type, 112 | env_name: "TYPE", 113 | description: "Test type. Default: robo (robo/instrumentation)", 114 | is_string: true, 115 | optional: true, 116 | default_value: "robo", 117 | verify_block: proc do |value| 118 | if value != "robo" && value != "instrumentation" 119 | UI.user_error!("Unknown test type.") 120 | end 121 | end), 122 | FastlaneCore::ConfigItem.new(key: :devices, 123 | description: "Devices to test the app on", 124 | type: Array, 125 | default_value: [{ 126 | model: "Nexus6", 127 | version: "21", 128 | locale: "en_US", 129 | orientation: "portrait" 130 | }], 131 | verify_block: proc do |value| 132 | if value.empty? 133 | UI.user_error!("Devices cannot be empty") 134 | end 135 | value.each do |current| 136 | if current.class != Hash 137 | UI.user_error!("Each device must be represented by a Hash object, " \ 138 | "#{current.class} found") 139 | end 140 | check_has_property(current, :model) 141 | check_has_property(current, :version) 142 | set_default_property(current, :locale, "en_US") 143 | set_default_property(current, :orientation, "portrait") 144 | end 145 | end), 146 | FastlaneCore::ConfigItem.new(key: :timeout, 147 | env_name: "TIMEOUT", 148 | description: "The max time this test execution can run before it is cancelled. Default: 5m (this value must be greater than or equal to 1m)", 149 | type: String, 150 | optional: true, 151 | default_value: "3m"), 152 | FastlaneCore::ConfigItem.new(key: :app_apk, 153 | env_name: "APP_APK", 154 | description: "The path for your android app apk", 155 | type: String, 156 | optional: false), 157 | FastlaneCore::ConfigItem.new(key: :app_test_apk, 158 | env_name: "APP_TEST_APK", 159 | description: "The path for your android test apk. Default: empty string", 160 | type: String, 161 | optional: true, 162 | default_value: nil), 163 | FastlaneCore::ConfigItem.new(key: :use_orchestrator, 164 | env_name: "USE_ORCHESTRATOR", 165 | description: "If you use orchestrator when set instrumentation test. Default: false", 166 | type: Boolean, 167 | optional: true, 168 | default_value: false), 169 | FastlaneCore::ConfigItem.new(key: :gcloud_components_channel, 170 | env_name: "gcloud_components_channel", 171 | description: "If you use beta or alpha components. Default stable (alpha/beta)", 172 | is_string: true, 173 | optional: true, 174 | default_value: "stable", 175 | verify_block: proc do |value| 176 | if value != "stable" && value != "alpha" && value != "beta" 177 | UI.user_error!("Unknown gcloud component channel.") 178 | end 179 | end), 180 | FastlaneCore::ConfigItem.new(key: :console_log_file_name, 181 | env_name: "CONSOLE_LOG_FILE_NAME", 182 | description: "The filename to save the output results. Default: ./console_output.log", 183 | type: String, 184 | optional: true, 185 | default_value: "./console_output.log"), 186 | FastlaneCore::ConfigItem.new(key: :extra_options, 187 | env_name: "EXTRA_OPTIONS", 188 | description: "Extra options that you need to pass to the gcloud command. Default: empty string", 189 | type: String, 190 | optional: true, 191 | default_value: ""), 192 | FastlaneCore::ConfigItem.new(key: :slack_url, 193 | env_name: "SLACK_URL", 194 | description: "If Notify to Slack after finishing of the test. Set your slack incoming webhook url", 195 | type: String, 196 | optional: true, 197 | default_value: nil), 198 | FastlaneCore::ConfigItem.new(key: :firebase_test_lab_results_bucket, 199 | env_name: "FIREBASE_TEST_LAB_RESULTS_BUCKET", 200 | description: "Name of Firebase Test Lab results bucket", 201 | type: String, 202 | optional: true, 203 | default_value: nil), 204 | FastlaneCore::ConfigItem.new(key: :firebase_test_lab_results_dir, 205 | env_name: "FIREBASE_TEST_LAB_RESULTS_DIR", 206 | description: "Name of Firebase Test Lab results directory", 207 | type: String, 208 | optional: true, 209 | default_value: nil), 210 | FastlaneCore::ConfigItem.new(key: :github_owner, 211 | env_name: "GITHUB_OWNER", 212 | description: "Owner name", 213 | type: String, 214 | optional: true, 215 | default_value: nil), 216 | FastlaneCore::ConfigItem.new(key: :github_repository, 217 | env_name: "GITHUB_REPOSITORY", 218 | description: "Repository name", 219 | type: String, 220 | optional: true, 221 | default_value: nil), 222 | FastlaneCore::ConfigItem.new(key: :github_pr_number, 223 | env_name: "GITHUB_PR_NUMBER", 224 | description: "Pull request number", 225 | type: String, 226 | optional: true, 227 | default_value: nil), 228 | FastlaneCore::ConfigItem.new(key: :github_api_token, 229 | env_name: "GITHUB_API_TOKEN", 230 | description: "GitHub API Token", 231 | type: String, 232 | optional: true, 233 | default_value: nil), 234 | FastlaneCore::ConfigItem.new(key: :download_dir, 235 | env_name: "DOWNLOAD_DIR", 236 | description: "Target directory to download screenshots from firebase", 237 | type: String, 238 | optional: true, 239 | default_value: nil) 240 | ] 241 | end 242 | 243 | def self.check_has_property(hash_obj, property) 244 | UI.user_error!("Each device must have #{property} property") unless hash_obj.key?(property) 245 | end 246 | 247 | def self.set_default_property(hash_obj, property, default) 248 | unless hash_obj.key?(property) 249 | hash_obj[property] = default 250 | end 251 | end 252 | 253 | def self.is_supported?(platform) 254 | platform == :android 255 | end 256 | 257 | def self.output 258 | [['console_output.log', 'A console log when running Firebase Test Lab with gcloud']] 259 | end 260 | 261 | def self.example_code 262 | ['before_all do 263 | ENV["SLACK_URL"] = "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX" 264 | end 265 | 266 | lane :test do 267 | 268 | # Get Pull request number 269 | pr_number = ENV["CI_PULL_REQUEST"] != nil ? ENV["CI_PULL_REQUEST"][/(?<=https:\/\/github.com\/cats-oss\/android\/pull\/)(.*)/] : nil 270 | 271 | # Upload to Firebase Test Lab 272 | firebase_test_lab_android( 273 | project_id: "cats-firebase", 274 | gcloud_service_key_file: "fastlane/client-secret.json", 275 | type: "robo", 276 | devices: [ 277 | { 278 | model: "Nexus6P", 279 | version: "23", 280 | locale: "ja_JP", 281 | orientation: "portrait" 282 | }, 283 | { 284 | model: "Pixel2", 285 | version: "28" 286 | } 287 | ], 288 | app_apk: "test.apk", 289 | # app_test_apk: "androidTest.apk", 290 | # use_orchestrator: false, 291 | console_log_file_name: "fastlane/console_output.log", 292 | timeout: "3m", 293 | firebase_test_lab_results_bucket: "firebase_cats_test_bucket", 294 | slack_url: ENV["SLACK_URL"], 295 | github_owner: "cats-oss", 296 | github_repository: "fastlane-plugin-firebase_test_lab_android", 297 | github_pr_number: pr_number, 298 | github_api_token: ENV["DANGER_GITHUB_API_TOKEN"], 299 | download_dir: ".results" 300 | ) 301 | end'] 302 | end 303 | end 304 | end 305 | end 306 | -------------------------------------------------------------------------------- /lib/fastlane/plugin/firebase_test_lab_android/helper/Helper.rb: -------------------------------------------------------------------------------- 1 | require 'fastlane_core/ui/ui' 2 | 3 | module Fastlane 4 | 5 | # Outcome 6 | PASSED = 'Passed' 7 | FAILED = 'Failed' 8 | SKIPPED = 'Skipped' 9 | INCONCLUSIVE = 'Inconclusive' 10 | 11 | module Helper 12 | 13 | def self.gcs_result_bucket_url(bucket, dir) 14 | "https://console.developers.google.com/storage/browser/#{bucket}/#{CGI.escape(dir)}" 15 | end 16 | 17 | def self.gcs_object_url(bucket, path) 18 | "https://storage.googleapis.com/#{bucket}/#{CGI.escape(path)}" 19 | end 20 | 21 | def self.firebase_test_lab_histories_url(project_id) 22 | "https://console.firebase.google.com/u/0/project/#{project_id}/testlab/histories/" 23 | end 24 | 25 | def self.config(project_id) 26 | UI.message "Set Google Cloud target project" 27 | Action.sh("gcloud config set project #{project_id}") 28 | end 29 | 30 | def self.authenticate(gcloud_key_file) 31 | UI.message "Authenticate with GCP" 32 | Action.sh("gcloud auth activate-service-account --key-file #{gcloud_key_file}") 33 | end 34 | 35 | def self.run_tests(gcloud_components_channel, arguments) 36 | UI.message("Test running...") 37 | Action.sh("set +e; gcloud #{gcloud_components_channel unless gcloud_components_channel == "stable"} firebase test android run #{arguments}; set -e") 38 | end 39 | 40 | def self.copy_from_gcs(bucket_and_path, copy_to) 41 | UI.message("Copy from gs://#{bucket_and_path}") 42 | Action.sh("gsutil -m cp -r gs://#{bucket_and_path} #{copy_to}") 43 | end 44 | 45 | def self.set_public(bucket_and_path) 46 | UI.message("Set public for reading gs://#{bucket_and_path}") 47 | Action.sh("gsutil -m acl -r set public-read gs://#{bucket_and_path}") 48 | end 49 | 50 | def self.is_failure(outcome) 51 | outcome == FAILED || outcome == INCONCLUSIVE 52 | end 53 | 54 | def self.if_need_dir(path) 55 | dirname = File.dirname(path) 56 | unless File.directory?(dirname) 57 | UI.message("Crate directory: #{dirname}") 58 | FileUtils.mkdir_p(dirname) 59 | end 60 | path 61 | end 62 | 63 | def self.split_device_name(axis_value) 64 | # Sample Nexus6P-23-ja_JP-portrait 65 | array = axis_value.split("-") 66 | "#{array[0]} (API #{array[1]})" 67 | end 68 | 69 | def self.emoji_status(outcome) 70 | # Github emoji list 71 | # https://github.com/ikatyang/emoji-cheat-sheet 72 | return case outcome 73 | when PASSED 74 | ":tada:" 75 | when FAILED 76 | ":fire:" 77 | when INCONCLUSIVE 78 | ":warning:" 79 | when SKIPPED 80 | ":expressionless:" 81 | else 82 | ":question:" 83 | end 84 | end 85 | 86 | def self.random_emoji_cat 87 | # Github emoji list 88 | # https://github.com/ikatyang/emoji-cheat-sheet#cat-face 89 | %w(:smiley_cat: :smile_cat: :joy_cat: :heart_eyes_cat: :smirk_cat: :kissing_cat:).sample 90 | end 91 | 92 | def self.make_slack_text(json) 93 | success = true 94 | json.each do |status| 95 | success = !is_failure(status["outcome"]) 96 | break unless success 97 | end 98 | 99 | body = json.map { |status| 100 | outcome = status["outcome"] 101 | emoji = emoji_status(outcome) 102 | device = split_device_name(status["axis_value"]) 103 | "#{device}: #{emoji} #{outcome}\n" 104 | }.inject(&:+) 105 | return success, body 106 | end 107 | 108 | def self.make_github_text(json, project_id, bucket, dir, test_type) 109 | prefix = "" 110 | cells = json.map { |data| 111 | axis = data["axis_value"] 112 | device = split_device_name(axis) 113 | outcome = data["outcome"] 114 | status = "#{emoji_status(outcome)} #{outcome}" 115 | message = data["test_details"] 116 | logcat = "#{random_emoji_cat}" 117 | if test_type == "robo" 118 | sitemp = "" 119 | else 120 | sitemp = "--" 121 | end 122 | 123 | "| **#{device}** | #{status} | #{message} | #{logcat} | #{sitemp} |\n" 124 | }.inject(&:+) 125 | comment = <<~EOS 126 | #{prefix} 127 | 128 | ### Results 129 | Firebase console: [#{project_id}](#{Helper.firebase_test_lab_histories_url(project_id)}) 130 | Test results: [#{dir}](#{Helper.gcs_result_bucket_url(bucket, dir)}) 131 | 132 | | :iphone: Device | :thermometer: Status | :memo: Message | :eyes: Logcat | :japan: Sitemap | 133 | | --- | :---: | --- | :---: | :---: | 134 | #{cells} 135 | EOS 136 | return prefix, comment 137 | end 138 | end 139 | end -------------------------------------------------------------------------------- /lib/fastlane/plugin/firebase_test_lab_android/helper/github_notifier.rb: -------------------------------------------------------------------------------- 1 | require 'fastlane_core/ui/ui' 2 | require 'json' 3 | require 'net/http' 4 | require 'uri' 5 | 6 | module Fastlane 7 | module GitHubNotifier 8 | def self.fold_comments(github_owner, github_repository, github_pr_number, comment_prefix, summary, github_api_token) 9 | res = get_comments(github_owner, github_repository, github_pr_number, github_api_token) 10 | JSON.parse(res.body) 11 | .select {|comment| comment["body"].start_with?(comment_prefix)} 12 | .each {|comment| 13 | body = "
#{summary}\n\n#{comment["body"]}\n\n
\n" 14 | patch_comment(github_owner, github_repository, comment["id"], body, github_api_token) 15 | } 16 | end 17 | 18 | def self.delete_comments(github_owner, github_repository, github_pr_number, comment_prefix, github_api_token) 19 | res = get_comments(github_owner, github_repository, github_pr_number, github_api_token) 20 | JSON.parse(res.body) 21 | .select {|comment| comment["body"].start_with?(comment_prefix)} 22 | .each {|comment| delete_comment(github_owner, github_repository, comment["id"], github_api_token)} 23 | end 24 | 25 | def self.get_comments(github_owner, github_repository, github_pr_number, github_api_token) 26 | api_url = "https://api.github.com/repos/#{github_owner}/#{github_repository}/issues/#{github_pr_number}/comments" 27 | UI.message "get comments #{api_url}" 28 | 29 | uri = URI.parse(api_url) 30 | req = Net::HTTP::Get.new(uri) 31 | req["Content-Type"] = "application/json" 32 | req["Authorization"] = "token #{github_api_token}" 33 | 34 | res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: uri.scheme = "https"}) {|http| http.request(req)} 35 | UI.message "#{res.code}\n#{res.body}" 36 | 37 | res 38 | end 39 | 40 | def self.put_comment(github_owner, github_repository, github_pr_number, body, github_api_token) 41 | api_url = "https://api.github.com/repos/#{github_owner}/#{github_repository}/issues/#{github_pr_number}/comments" 42 | UI.message "put comment #{api_url}" 43 | 44 | uri = URI.parse(api_url) 45 | req = Net::HTTP::Post.new(uri) 46 | req["Content-Type"] = "application/json" 47 | req["Authorization"] = "token #{github_api_token}" 48 | req.body = {:body => body}.to_json 49 | 50 | res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: uri.scheme = "https"}) {|http| http.request(req)} 51 | UI.message "#{res.code}\n#{res.body}" 52 | 53 | res 54 | end 55 | 56 | def self.patch_comment(github_owner, github_repository, comment_id, body, github_api_token) 57 | api_url = "https://api.github.com/repos/#{github_owner}/#{github_repository}/issues/comments/#{comment_id}" 58 | UI.message "patch comment #{api_url}" 59 | 60 | uri = URI.parse(api_url) 61 | req = Net::HTTP::Patch.new(uri) 62 | req["Content-Type"] = "application/json" 63 | req["Authorization"] = "token #{github_api_token}" 64 | req.body = {:body => body}.to_json 65 | 66 | res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: uri.scheme = "https"}) {|http| http.request(req)} 67 | UI.message "#{res.code}\n#{res.body}" 68 | 69 | res 70 | end 71 | 72 | def self.delete_comment(github_owner, github_repository, comment_id, github_api_token) 73 | api_url = "https://api.github.com/repos/#{github_owner}/#{github_repository}/issues/comments/#{comment_id}" 74 | UI.message "delete comment #{api_url}" 75 | 76 | uri = URI.parse(api_url) 77 | req = Net::HTTP::Delete.new(uri) 78 | req["Content-Type"] = "application/json" 79 | req["Authorization"] = "token #{github_api_token}" 80 | 81 | res = Net::HTTP.start(uri.hostname, uri.port, {use_ssl: uri.scheme = "https"}) {|http| http.request(req)} 82 | UI.message "#{res.code}\n#{res.body}" 83 | 84 | res 85 | end 86 | end 87 | end -------------------------------------------------------------------------------- /lib/fastlane/plugin/firebase_test_lab_android/helper/slack_notifier.rb: -------------------------------------------------------------------------------- 1 | require 'fastlane/action' 2 | require 'fastlane_core/ui/ui' 3 | require 'json' 4 | 5 | module Fastlane 6 | module SlackNotifier 7 | def self.notify(slack_url, message, success) 8 | slackArgs = Fastlane::ConfigurationHelper.parse(Fastlane::Actions::SlackAction, { 9 | slack_url: slack_url, 10 | message: message, 11 | success: success, 12 | use_webhook_configured_username_and_icon: true, 13 | default_payloads: [:git_branch, :git_author, :last_git_commit] 14 | }) 15 | Fastlane::Actions::SlackAction.run(slackArgs) 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /lib/fastlane/plugin/firebase_test_lab_android/version.rb: -------------------------------------------------------------------------------- 1 | module Fastlane 2 | module FirebaseTestLabAndroid 3 | VERSION = "1.1.0" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) 2 | 3 | require 'simplecov' 4 | 5 | # SimpleCov.minimum_coverage 95 6 | SimpleCov.start 7 | 8 | # This module is only used to check the environment is currently a testing env 9 | module SpecHelper 10 | end 11 | 12 | require 'fastlane' # to import the Action super class 13 | require 'fastlane/plugin/firebase_test_lab_android' # import the actual plugin 14 | 15 | Fastlane.load_actions # load other actions (in case your plugin calls other actions or shared values) 16 | --------------------------------------------------------------------------------