├── .github └── stale.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── bin └── dryrun ├── docs ├── css │ └── main.css ├── index.html ├── js │ ├── index.js │ └── vendor │ │ └── .keep └── res │ └── .keep ├── dryrun.gemspec ├── extras ├── gift.gif ├── logo.png ├── ss.gif ├── usage.gif ├── usage_v2.gif ├── usage_v3.gif └── usage_v4.gif ├── facelift.json ├── lib ├── dryrun.rb └── dryrun │ ├── android_project.rb │ ├── android_utils.rb │ ├── device.rb │ ├── dryrun_utils.rb │ ├── github.rb │ ├── gradle_adapter.rb │ ├── install_application_command.rb │ ├── manifest_parser.rb │ ├── test_application_command.rb │ └── version.rb └── spec ├── dryrun_spec.rb ├── github_spec.rb └── spec_helper.rb /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | .idea/ 10 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --tty 3 | --color 4 | --format documentation 5 | --format html -o "tmp/rspec_result.html" 6 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Style/Encoding: 2 | Enabled: false 3 | 4 | Metrics/LineLength: 5 | Enabled: false 6 | 7 | Metrics/MethodLength: 8 | Enabled: false 9 | 10 | Metrics/ClassLength: 11 | Enabled: false 12 | 13 | Metrics/PerceivedComplexity: 14 | Enabled: false 15 | 16 | Metrics/AbcSize: 17 | Enabled: false 18 | 19 | Metrics/CyclomaticComplexity: 20 | Enabled: false 21 | 22 | AllCops: 23 | TargetRubyVersion: 2.0 24 | 25 | Documentation: 26 | Enabled: false 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: false 3 | rvm: 4 | - 2.3.0 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in sinderella.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | dryrun (1.3.2) 5 | bundler 6 | colorize 7 | highline 8 | oga 9 | rjb 10 | 11 | GEM 12 | remote: https://rubygems.org/ 13 | specs: 14 | ansi (1.5.0) 15 | ast (2.4.1) 16 | byebug (11.1.3) 17 | coderay (1.1.3) 18 | colorize (0.8.1) 19 | diff-lcs (1.4.4) 20 | highline (2.0.3) 21 | method_source (1.0.0) 22 | oga (3.3) 23 | ast 24 | ruby-ll (~> 2.1) 25 | pry (0.13.1) 26 | coderay (~> 1.1) 27 | method_source (~> 1.0) 28 | pry-byebug (3.9.0) 29 | byebug (~> 11.0) 30 | pry (~> 0.13.0) 31 | rake (13.0.1) 32 | rjb (1.6.2) 33 | rspec (3.9.0) 34 | rspec-core (~> 3.9.0) 35 | rspec-expectations (~> 3.9.0) 36 | rspec-mocks (~> 3.9.0) 37 | rspec-core (3.9.2) 38 | rspec-support (~> 3.9.3) 39 | rspec-expectations (3.9.2) 40 | diff-lcs (>= 1.2.0, < 2.0) 41 | rspec-support (~> 3.9.0) 42 | rspec-mocks (3.9.1) 43 | diff-lcs (>= 1.2.0, < 2.0) 44 | rspec-support (~> 3.9.0) 45 | rspec-support (3.9.3) 46 | ruby-ll (2.1.2) 47 | ansi 48 | ast 49 | 50 | PLATFORMS 51 | ruby 52 | 53 | DEPENDENCIES 54 | dryrun! 55 | pry-byebug 56 | rake 57 | rspec 58 | 59 | BUNDLED WITH 60 | 2.1.4 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 César Ferreira 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | <!--<p align="center"> 2 | <a href="https://github.com/cesarferreira/dryrun" target="_blank"> 3 | <img width="200"src="extras/gift.gif"> 4 | </a> 5 | </p>--> 6 | <h1 align="center">dryrun</h1> 7 | <p align="center"><strong>Try any android library</strong> hosted online <strong>directly</strong> from the <strong>command line</strong></p> 8 | <p align="center"> 9 | <a href="https://github.com/cesarferreira/dryrun"><img src="http://ruby-gem-downloads-badge.herokuapp.com/dryrun?type=total" alt="downloads"></a> 10 | <a href="https://github.com/cesarferreira/dryrun"><img src="https://badge.fury.io/rb/dryrun.svg" alt="npm"></a> 11 | <a href="http://androidweekly.net/issues/issue-200"><img src="https://img.shields.io/badge/Android%20Weekly-%23200-blue.svg" alt="Android Weekly"></a> 12 | <a href="https://www.codacy.com/app/cesarferreira/dryrun?utm_source=github.com&utm_medium=referral&utm_content=cesarferreira/dryrun&utm_campaign=badger"><img src="https://api.codacy.com/project/badge/Grade/c9f73e75e72547008558b3e337acbff3" alt="Codacy Badge"></a> 13 | <a href="https://github.com/cesarferreira/dryrun/issues?q=is%3Aissue+is%3Aclosed"><img src="https://img.shields.io/github/issues-closed-raw/cesarferreira/dryrun.svg?color=%23FF69B4" alt="Closed"></a> 14 | 15 | </p> 16 | 17 | <p align="center"> 18 | <img src="extras/ss.gif" width="100%" /> 19 | </p> 20 | 21 | ## Install 22 | 23 | ```sh 24 | gem install dryrun 25 | ``` 26 | 27 | ## Usage 28 | 29 | ```bash 30 | dryrun https://github.com/cesarferreira/android-helloworld 31 | ``` 32 | 33 | Wait a few seconds and the app is now opened on your phone :smiley: 34 | 35 | ```bash 36 | $ dryrun -h 37 | Usage: dryrun GIT_URL [OPTIONS] 38 | 39 | Options 40 | -m, --module MODULE_NAME Custom module to run 41 | -b, --branch BRANCH_NAME Checkout custom branch to run 42 | -f, --flavour FLAVOUR Custom flavour (e.g. dev, qa, prod) 43 | -p, --path PATH Custom path to android project 44 | -t, --tag TAG Checkout tag/commit hash to clone (e.g. "v0.4.5", "6f7dd4b") 45 | -c, --cleanup Clean the temporary folder before cloning the project 46 | -w, --wipe Wipe the temporary dryrun folder 47 | -h, --help Displays help 48 | -v, --version Displays the version 49 | -a, --android-test Execute android tests 50 | ``` 51 | 52 | ## Alternative scenario (if you don't use `dryrun`) 53 | 54 | 1. Find the github's repository url 55 | 2. Click the `download zip` 56 | 3. Extract the `zip file` 57 | 4. Open Android Studio 58 | 5. Import the project you just downloaded 59 | 6. Sync gradle 60 | 7. Run the project 61 | 8. Choose the device you want to run 62 | 9. Test all you want 63 | 10. Delete the `project folder` and the `zip file` when you don't want it anymore 64 | 65 | ## Goodies 66 | 67 | - Private repos can be tested too :smiley: 68 | ``` 69 | $ dryrun git@github.com:cesarferreira/android-helloworld.git 70 | ``` 71 | - No need to cleanup after you test the library. 72 | - No need to wait for **Android Studio** to load. 73 | 74 | 75 | ## Notes 76 | 77 | Be aware that `$ANDROID_SDK_ROOT` environment variable needs to be set. See more in [here](https://developer.android.com/studio/command-line/variables#set) 78 | 79 | Additionally, on windows in order to use git commands, the following path should be on the environment variable 80 | - ```...\Git\cmd ``` 81 | 82 | ## Created by 83 | [Cesar Ferreira](https://cesarferreira.com) 84 | 85 | ## License 86 | MIT © [Cesar Ferreira](http://cesarferreira.com) 87 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | task default: :test 5 | RSpec::Core::RakeTask.new(:test) 6 | -------------------------------------------------------------------------------- /bin/dryrun: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'bundler/setup' 4 | require 'dryrun' 5 | 6 | Dryrun::MainApp.new(ARGV).call 7 | -------------------------------------------------------------------------------- /docs/css/main.css: -------------------------------------------------------------------------------- 1 | div.notification.notification-producthunt { 2 | border-radius: 0 !important; 3 | background: #da552f; 4 | } 5 | 6 | p.install-text code { 7 | border-radius: 5px; 8 | padding: 0.75rem; 9 | } 10 | 11 | p.install-text code::before { 12 | content: '$ '; 13 | } 14 | 15 | p.install-text code:hover { 16 | cursor: text; 17 | } 18 | 19 | p.install-text code:hover span { 20 | background: pink; 21 | } 22 | 23 | .hero .hero-foot { 24 | margin-bottom: 1rem; 25 | } 26 | 27 | #madeWithBulma { 28 | vertical-align: bottom; 29 | } 30 | 31 | div.column.has-content-vcentered { 32 | display: flex; 33 | flex-direction: column; 34 | justify-content: center; 35 | } 36 | 37 | div.notification.copy-notification { 38 | position: absolute; 39 | top: 0; 40 | right: 0; 41 | z-index: 999; 42 | } 43 | 44 | .makerlink { 45 | font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 46 | right: 0px; 47 | bottom: 0px; 48 | position: fixed; 49 | z-index: 100; 50 | border-top-left-radius: 5px; 51 | color: rgb(111, 111, 111); 52 | padding: 6px; 53 | border-top: 1px solid rgb(239, 239, 239); 54 | border-left: 1px solid rgb(239, 239, 239); 55 | background: rgb(255, 255, 255); 56 | text-decoration: none; 57 | } 58 | 59 | .makerlink .makerlink__img { 60 | width: 22px; 61 | vertical-align: middle; 62 | border-radius: 100%; 63 | } 64 | 65 | .makerlink .makerlink__author { 66 | vertical-align: middle; 67 | display: inline; 68 | font-weight: 500; 69 | font-size: 14px; 70 | margin: 0px 0px 0px 7px; 71 | } 72 | 73 | h1 a { 74 | color:inherit; 75 | text-decoration: none; 76 | } -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | <!-- Generated with: https://github.com/cesarferreira/facelift --> 2 | <!DOCTYPE html> 3 | <html> 4 | <head> 5 | <meta charset="utf-8"> 6 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> 7 | <meta name="viewport" content="width=device-width, initial-scale=1"> 8 | <title>dryrun - ☁️ Try the demo project of any Android Library</title> 9 | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css" /> 10 | <link rel="stylesheet" href="./css/main.css" /> 11 | <!-- Twitter Meta --> 12 | <meta name="twitter:card" content="summary" /> 13 | <meta name="twitter:title" content="dryrun" /> 14 | <meta name="twitter:url" content="https://github.com/cesarferreira/dryrun" /> 15 | <meta name="twitter:description" content="☁️ Try the demo project of any Android Library" /> 16 | <meta property="og:url" content="https://github.com/cesarferreira/dryrun" /> 17 | 18 | <meta property="og:title" content="dryrun" /> 19 | <meta property="og:description" content="☁️ Try the demo project of any Android Library" /> 20 | </head> 21 | <body> 22 | <div class="hero is-fullheight"> 23 | <div class="hero-body"> 24 | <div class="container"> 25 | <div class="columns is-variable is-6"> 26 | <!-- Content Left --> 27 | <div class="column is-5 has-content-vcentered"> 28 | <h1 class="title is-1"><a href="https://github.com/cesarferreira/dryrun">dryrun</a></h1> 29 | <h2 class="subtitle">☁️ Try the demo project of any Android Library</h2> 30 | <div class="call-to-action content"> 31 | <p class="install-text is-size-4"> 32 | <code><span>gem install dryrun</span></code> 33 | </p> 34 | <div class="notification is-link is-hidden copy-notification">Copied to clipboard!</div> 35 | </div> 36 | 37 | <div class="features content"> 38 | <strong>Features</strong> 39 | <ul> 40 | <li>Try any android library hosted online directly from the command line</li> 41 | <li>Specify any custom branch to run</li> 42 | <li>Specify any flavour to run</li> 43 | <li>Specify any app module to run</li> 44 | <li>Checkout tag/commit hash to clone (e.g. "v0.4.5", "6f7dd4b")</li> 45 | </ul> 46 | </div> 47 | <div class="features content"> 48 | <strong>Links</strong> 49 | <ul> 50 | <li>GitHub: <a href='https://github.com/cesarferreira/dryrun'>https://github.com/cesarferreira/dryrun</a></li> 51 | <li>rubygems: <a href='https://rubygems.org/gems/dryrun/'>https://rubygems.org/gems/dryrun/</a></li> 52 | </ul> 53 | </div> 54 | 55 | </div> 56 | <!-- Media Right --> 57 | <div class="column is-7 has-content-vcentered"> 58 | <figure class="image is-4by2"> 59 | <img src="https://raw.githubusercontent.com/cesarferreira/dryrun/master/extras/ss.gif" /> 60 | </figure> 61 | </div> 62 | </div> 63 | </div> 64 | </div> 65 | </div> 66 | 67 | <a target="_blank" rel="noopener" class="makerlink" href="http://cesarferreira.com"> 68 | <img class="makerlink__img" src="https://pbs.twimg.com/profile_images/884351017097322496/2mmpORsM_400x400.jpg" style="display: inline-block"> 69 | <p class="makerlink__author">by cesar ferreira</p> 70 | </a> 71 | <script async type="text/javascript" src="./js/index.js"></script> 72 | </body> 73 | </html> -------------------------------------------------------------------------------- /docs/js/index.js: -------------------------------------------------------------------------------- 1 | // `copyToClipboard()` courtesy @chalarangelo (Angelos Chalaris) 2 | // refs: https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f 3 | const copyToClipboard = (str) => { 4 | const el = document.createElement('textarea'); // Create a <textarea> element 5 | el.value = str; // Set its value to the string that you want copied 6 | el.setAttribute('readonly', ''); // Make it readonly to be tamper-proof 7 | el.style.position = 'absolute'; 8 | el.style.left = '-9999px'; // Move outside the screen to make it invisible 9 | document.body.appendChild(el); // Append the <textarea> element to the HTML document 10 | const selected = 11 | document.getSelection().rangeCount > 0 // Check if there is any content selected previously 12 | ? document.getSelection().getRangeAt(0) // Store selection if found 13 | : false; // Mark as false to know no selection existed before 14 | el.select(); // Select the <textarea> content 15 | document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events) 16 | document.body.removeChild(el); // Remove the <textarea> element 17 | if (selected) { // If a selection existed before copying 18 | document.getSelection().removeAllRanges(); // Unselect everything on the HTML document 19 | document.getSelection().addRange(selected); // Restore the original selection 20 | } 21 | }; 22 | 23 | const showCopyNotification = () => { 24 | const notification = document.querySelector('div.notification.copy-notification'); 25 | notification.classList.remove('is-hidden'); 26 | setTimeout(() => { 27 | notification.classList.add('is-hidden'); 28 | }, 2000); 29 | }; 30 | 31 | const installTextCode = document.querySelector('p.install-text code'); 32 | const installTextSpan = document.querySelector('p.install-text code span'); 33 | 34 | const clickableElements = [installTextCode, installTextSpan]; 35 | 36 | clickableElements.forEach((el) => { 37 | const npmInstallText = installTextSpan.textContent; // 'npm i -g amo-cli' 38 | el.addEventListener('click', () => { 39 | copyToClipboard(npmInstallText); 40 | showCopyNotification(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /docs/js/vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/docs/js/vendor/.keep -------------------------------------------------------------------------------- /docs/res/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/docs/res/.keep -------------------------------------------------------------------------------- /dryrun.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require File.join([File.dirname(__FILE__), 'lib', 'dryrun', 'version.rb']) 3 | 4 | # rake build # Build dryrun-0.0.1.gem into the pkg directory 5 | # rake install # Build and install dryrun-0.0.1.gem into system gems 6 | # rake release # Create tag v0.0.1 and build and push dryrun-0.0.1.gem t... 7 | # rake spec # Run RSpec code examples 8 | 9 | Gem::Specification.new do |s| 10 | s.name = 'dryrun' 11 | s.version = Dryrun::VERSION 12 | s.authors = ['cesar ferreira'] 13 | s.email = ['cesar.manuel.ferreira@gmail.com'] 14 | 15 | s.summary = 'Tool to try any android library hosted online directly from the command line' 16 | s.homepage = 'http://cesarferreira.com' 17 | s.license = 'MIT' 18 | s.platform = Gem::Platform::RUBY 19 | 20 | s.files = `git ls-files`.split(" 21 | ") 22 | s.bindir = 'bin' 23 | s.require_paths << 'lib' 24 | s.executables << 'dryrun' 25 | 26 | # s.required_ruby_version = '>= 2.0.0' 27 | 28 | s.add_development_dependency 'rake' 29 | s.add_development_dependency 'pry-byebug' 30 | s.add_development_dependency 'rspec' 31 | 32 | s.add_dependency 'bundler' 33 | s.add_dependency 'colorize' 34 | s.add_dependency 'oga' 35 | s.add_dependency 'highline' 36 | s.add_dependency 'rjb' 37 | end 38 | -------------------------------------------------------------------------------- /extras/gift.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/gift.gif -------------------------------------------------------------------------------- /extras/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/logo.png -------------------------------------------------------------------------------- /extras/ss.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/ss.gif -------------------------------------------------------------------------------- /extras/usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/usage.gif -------------------------------------------------------------------------------- /extras/usage_v2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/usage_v2.gif -------------------------------------------------------------------------------- /extras/usage_v3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/usage_v3.gif -------------------------------------------------------------------------------- /extras/usage_v4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesarferreira/dryrun/0ca622b596761cd171246e46b726cc838f179cb0/extras/usage_v4.gif -------------------------------------------------------------------------------- /facelift.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "dryrun", 3 | "subtitle": "☁️ Try the demo project of any Android Library", 4 | "installation": "gem install dryrun", 5 | "url": "https://github.com/cesarferreira/dryrun", 6 | "screenshot": "https://raw.githubusercontent.com/cesarferreira/dryrun/master/extras/ss.gif", 7 | "author": { 8 | "name": "cesar ferreira", 9 | "thumbnail": "https://pbs.twimg.com/profile_images/884351017097322496/2mmpORsM_400x400.jpg", 10 | "homepage": "http://cesarferreira.com" 11 | }, 12 | "highlights": [{ 13 | "title": "Features", 14 | "items": [ 15 | "Try any android library hosted online directly from the command line", 16 | "Specify any custom branch to run", 17 | "Specify any flavour to run", 18 | "Specify any app module to run", 19 | "Checkout tag/commit hash to clone (e.g. \"v0.4.5\", \"6f7dd4b\")" 20 | ] 21 | }, 22 | { 23 | "title": "Links", 24 | "items": [ 25 | "GitHub: <a href='https://github.com/cesarferreira/dryrun'>https://github.com/cesarferreira/dryrun</a>", 26 | "rubygems: <a href='https://rubygems.org/gems/dryrun/'>https://rubygems.org/gems/dryrun/</a>" 27 | ] 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /lib/dryrun.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | require 'tmpdir' 3 | require 'fileutils' 4 | require 'dryrun/github' 5 | require 'dryrun/version' 6 | require 'dryrun/android_project' 7 | require 'dryrun/install_application_command' 8 | require 'dryrun/test_application_command' 9 | require 'dryrun/device' 10 | require 'highline/import' 11 | require 'openssl' 12 | require 'open3' 13 | require 'optparse' 14 | 15 | module Dryrun 16 | class MainApp 17 | def initialize(arguments) 18 | outdated_verification 19 | 20 | @url = %w(-h --help -v --version -w --wipe).include?(arguments.first) ? nil : arguments.shift 21 | 22 | # defaults 23 | @app_path = nil 24 | @custom_module = nil 25 | @flavour = '' 26 | @tag = nil 27 | @branch = 'master' 28 | @devices = [] 29 | @cleanup = false 30 | @command = InstallApplicationCommand.new 31 | 32 | # Parse Options 33 | create_options_parser(arguments) 34 | end 35 | 36 | def create_options_parser(args) 37 | args.options do |opts| 38 | opts.banner = 'Usage: dryrun GIT_URL [OPTIONS]' 39 | opts.separator '' 40 | opts.separator 'Options' 41 | 42 | opts.on('-m MODULE_NAME', '--module MODULE_NAME', 'Custom module to run') do |custom_module| 43 | @custom_module = custom_module 44 | end 45 | 46 | opts.on('-b BRANCH_NAME', '--branch BRANCH_NAME', 'Checkout custom branch to run') do |branch| 47 | @branch = branch 48 | end 49 | 50 | opts.on('-f', '--flavour FLAVOUR', 'Custom flavour (e.g. dev, qa, prod)') do |flavour| 51 | @flavour = flavour.capitalize 52 | end 53 | 54 | opts.on('-p PATH', '--path PATH', 'Custom path to android project') do |app_path| 55 | @app_path = app_path 56 | end 57 | 58 | opts.on('-t TAG', '--tag TAG', 'Checkout tag/commit hash to clone (e.g. "v0.4.5", "6f7dd4b")') do |tag| 59 | @tag = tag 60 | end 61 | 62 | opts.on('-c', '--cleanup', 'Clean the temporary folder before cloning the project') do 63 | @cleanup = true 64 | end 65 | 66 | opts.on('-w', '--wipe', 'Wipe the temporary dryrun folder') do 67 | wipe_temporary_folder 68 | end 69 | 70 | opts.on('-h', '--help', 'Displays help') do 71 | puts opts.help 72 | exit 73 | end 74 | 75 | opts.on('-v', '--version', 'Displays the version') do 76 | puts Dryrun::VERSION 77 | exit 78 | end 79 | 80 | opts.on('-a', '--android-test', 'Execute android tests') do 81 | @command = TestApplicationCommand.new 82 | end 83 | 84 | opts.parse! 85 | end 86 | end 87 | 88 | def outdated_verification 89 | return if DryrunUtils.up_to_date 90 | 91 | input = nil 92 | 93 | begin 94 | input = ask "\n#{'Your Dryrun version is outdated, want to update?'.yellow} #{'[Y/n]:'.white}" 95 | end until %w(y n s).include?(input.downcase) 96 | 97 | DryrunUtils.execute('gem update dryrun') if input.casecmp('y') == 0 98 | end 99 | 100 | def pick_device 101 | @device = nil 102 | 103 | if !Gem.win_platform? 104 | @sdk = `echo $ANDROID_SDK_ROOT`.delete("\n") 105 | else 106 | @sdk = `echo %ANDROID_SDK_ROOT%`.delete("\n") 107 | end 108 | 109 | @sdk = 'adb' if @sdk.empty? 110 | 111 | $sdk = @sdk 112 | 113 | puts 'Searching for devices...'.yellow 114 | 115 | @devices = DryrunUtils.run_adb('devices') 116 | 117 | # if @devices.nil? || @devices.empty? 118 | # puts 'Killing adb, there might be an issue with it...' 119 | # DryrunUtils.run_adb('kill-server') 120 | # @devices = DryrunUtils.run_adb('devices') 121 | # end 122 | 123 | puts 'No devices attached, but I\'ll run anyway' if @devices.empty? 124 | 125 | if @devices.size >= 2 126 | puts 'Pick your device (1,2,3...):' 127 | 128 | @devices.each_with_index.map {|key, index| puts "#{index.to_s.green} - #{key.name} \n"} 129 | 130 | input = gets.chomp 131 | 132 | @device = if input.match(/^\d+$/) && input.to_i <= (@devices.length - 1) && input.to_i >= 0 133 | @devices[input.to_i] 134 | else 135 | @devices.first 136 | end 137 | else 138 | @device = @devices.first 139 | end 140 | 141 | $device = @device 142 | puts "Picked #{@device.name.to_s.green}" unless @device.nil? 143 | end 144 | 145 | def android_sdk_root_is_defined 146 | @sdk = if !Gem.win_platform? 147 | `echo $ANDROID_SDK_ROOT`.delete('\n') 148 | else 149 | `echo %ANDROID_SDK_ROOT%`.delete('\n') 150 | end 151 | !@sdk.empty? 152 | end 153 | 154 | def wipe_temporary_folder 155 | tmpdir = Dir.tmpdir + '/dryrun/' 156 | puts 'Wiping ' + tmpdir.red 157 | FileUtils.rm_rf tmpdir 158 | puts 'Folder totally removed!'.green 159 | exit 1 160 | end 161 | 162 | def call 163 | unless android_sdk_root_is_defined 164 | puts "\nWARNING: your #{'$ANDROID_SDK_ROOT'.yellow} is not defined\n" 165 | puts "\nhint: in your #{'~/.bashrc'.yellow} or #{'~/.bash_profile'.yellow} add:\n #{"export ANDROID_SDK_ROOT='/Users/cesarferreira/Library/Android/sdk/'".yellow}" 166 | puts "\nNow type #{'source ~/.bashrc'.yellow}\n\n" 167 | exit 1 168 | end 169 | 170 | if @url.nil? 171 | puts 'You need to insert a valid GIT URL/folder' 172 | exit 1 173 | end 174 | 175 | pick_device 176 | 177 | if DryrunUtils::is_folder? (@url) 178 | repository_path = File.expand_path @url 179 | else 180 | 181 | @url = @url.split('?').first 182 | @url.chop! if @url.end_with? '/' 183 | 184 | github = Github.new(@url) 185 | 186 | unless github.valid? 187 | puts "#{@url.red} is not a valid git @url" 188 | exit 1 189 | end 190 | 191 | # clone the repository 192 | repository_path = github.clone(@branch, @tag, @cleanup) 193 | 194 | end 195 | 196 | android_project = AndroidProject.new(repository_path, @app_path, @custom_module, @flavour, @device) 197 | 198 | # is a valid android project? 199 | unless android_project.valid? 200 | puts "#{@url.red} is not a valid android project" 201 | exit 1 202 | end 203 | 204 | puts "Using custom app folder: #{@app_path.green}" if @app_path 205 | puts "Using custom module: #{@custom_module.green}" if @custom_module 206 | 207 | # clean and install the apk 208 | android_project.execute_command(@command) 209 | 210 | puts "\n> If you want to remove the app you just installed, execute:\n#{android_project.uninstall_command.yellow}\n\n" 211 | end 212 | end 213 | end 214 | -------------------------------------------------------------------------------- /lib/dryrun/android_project.rb: -------------------------------------------------------------------------------- 1 | require 'oga' 2 | require 'fileutils' 3 | require 'tempfile' 4 | require 'find' 5 | require_relative 'dryrun_utils' 6 | require_relative 'manifest_parser' 7 | require_relative 'gradle_adapter' 8 | 9 | module Dryrun 10 | class AndroidProject 11 | def initialize(path, custom_app_path, custom_module, flavour, device) 12 | @custom_app_path = custom_app_path 13 | @custom_module = custom_module 14 | @base_path = @custom_app_path ? File.join(path, @custom_app_path) : path 15 | @flavour = flavour 16 | @device = device 17 | @gradle_file_extension = gradle_file_extension 18 | @settings_gradle_path = settings_gradle_file 19 | @main_gradle_file = main_gradle_file 20 | 21 | check_custom_app_path 22 | 23 | @modules = find_modules 24 | end 25 | 26 | def gradle_file_extension 27 | gradle_file = File.join(@base_path, 'settings.gradle.kts') 28 | if (File.exist?(gradle_file)) 29 | return ".gradle.kts" 30 | end 31 | ".gradle" 32 | end 33 | 34 | def check_custom_app_path 35 | return unless @custom_app_path 36 | 37 | full_custom_path = @base_path 38 | settings_path = settings_gradle_file(full_custom_path) 39 | main_gradle_path = main_gradle_file(full_custom_path) 40 | return unless valid?(main_gradle_path) 41 | 42 | @settings_gradle_path = settings_path 43 | @main_gradle_file = main_gradle_file 44 | 45 | @base_path = full_custom_path 46 | end 47 | 48 | def remove_local_properties 49 | Dir.chdir @base_path 50 | file_name = 'local.properties' 51 | 52 | File.delete(file_name) if File.exist?(file_name) 53 | 54 | DryrunUtils.execute("touch #{file_name}") unless Gem.win_platform? 55 | end 56 | 57 | def remove_application_id 58 | # Open temporary file 59 | tmp = Tempfile.new('extract') 60 | 61 | file = "#{@path_to_sample}/build#{@gradle_file_extension}" 62 | 63 | # Write good lines to temporary file 64 | File.open(file, 'r') do |f| 65 | f.each do |l| 66 | tmp << l unless l.include? 'applicationId' 67 | end 68 | end 69 | tmp.close 70 | 71 | # Move temp file to origin 72 | FileUtils.mv(tmp.path, file) 73 | end 74 | 75 | def settings_gradle_file(path = @base_path) 76 | File.join(path, "settings#{@gradle_file_extension}") 77 | end 78 | 79 | def main_gradle_file(path = @base_path) 80 | File.join(path, "build#{@gradle_file_extension}") 81 | end 82 | 83 | def valid?(main_gradle_file = @main_gradle_file) 84 | File.exist?(main_gradle_file) && 85 | File.exist?(@settings_gradle_path) 86 | end 87 | 88 | def find_modules 89 | return [] unless valid? 90 | 91 | content = File.open(@settings_gradle_path, 'rb').read 92 | 93 | content = content.split(/\n/).delete_if { |x| !x.start_with?("include")}.join("\n") 94 | modules = content.scan(/'([^']*)'/) + content.scan(/\"([^"]*)\"/) 95 | 96 | modules.each {|replacement| replacement.first.tr!(':', '')} 97 | end 98 | 99 | def execute_command(command) 100 | Dir.chdir @base_path 101 | 102 | path = sample_project 103 | if path == false or !@launcher_activity 104 | puts "Couldn't open or there isnt any sample project, sorry!".red 105 | exit 1 106 | end 107 | 108 | builder = create_builder 109 | 110 | remove_application_id 111 | remove_local_properties 112 | 113 | command.run(builder, @package, @launcher_activity, @custom_module, @flavour, @device) 114 | end 115 | 116 | def gradle_wrapped? 117 | return false unless File.directory?('gradle/') 118 | 119 | File.exist?('gradle/wrapper/gradle-wrapper.properties') && 120 | File.exist?('gradle/wrapper/gradle-wrapper.jar') 121 | end 122 | 123 | def sample_project 124 | if @custom_module && @modules.any? {|m| m.first == "#{@custom_module}"} 125 | @path_to_sample = File.join(@base_path, "#{@custom_module}") 126 | return @path_to_sample if parse_manifest(@path_to_sample) 127 | else 128 | @modules.each do |child| 129 | full_path = File.join(@base_path, child.first) 130 | @path_to_sample = full_path 131 | return full_path if parse_manifest(full_path) 132 | end 133 | end 134 | false 135 | end 136 | 137 | def uninstall_command 138 | "adb uninstall \"#{@package}\"" 139 | end 140 | 141 | def uninstall_application 142 | DryrunUtils.run_adb("shell pm uninstall #{@package}") 143 | end 144 | 145 | def parse_manifest(path_to_sample) 146 | manifest_file = get_manifest(path_to_sample) 147 | 148 | return false if manifest_file.nil? 149 | 150 | manifest_parser = ManifestParser.new(manifest_file) 151 | @package = manifest_parser.package 152 | @launcher_activity = manifest_parser.launcher_activity 153 | manifest_file.close 154 | @launcher_activity && @package 155 | end 156 | 157 | def get_manifest(path_to_sample) 158 | default_path = File.join(path_to_sample, 'src/main/AndroidManifest.xml') 159 | 160 | if File.exist?(default_path) 161 | File.open(default_path) 162 | else 163 | puts path_to_sample 164 | Find.find(path_to_sample) do |path| 165 | return File.open(path) if path =~ /.*AndroidManifest.xml$/ 166 | end 167 | end 168 | end 169 | 170 | def create_builder 171 | builder = 'gradle' 172 | 173 | if File.exist?('gradlew') 174 | if !Gem.win_platform? 175 | DryrunUtils.execute('chmod +x gradlew') 176 | else 177 | DryrunUtils.execute('icacls gradlew /T') 178 | end 179 | builder = './gradlew' 180 | end 181 | 182 | # Generate the gradle/ folder 183 | DryrunUtils.execute('gradle wrap') if File.exist?('gradlew') && !gradle_wrapped? 184 | GradleAdapter.new(builder) 185 | end 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /lib/dryrun/android_utils.rb: -------------------------------------------------------------------------------- 1 | require_relative 'dryrun_utils' 2 | 3 | module Dryrun 4 | class AndroidUtils 5 | 6 | def self.pretty_run(execute_line, package) 7 | puts "Installing #{package.green}...\n" 8 | puts "executing: #{execute_line.green}\n" 9 | 10 | DryrunUtils.run_adb("shell #{execute_line}") 11 | end 12 | 13 | def self.clear_app_data(package) 14 | DryrunUtils.run_adb("shell pm clear #{package}") 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/dryrun/device.rb: -------------------------------------------------------------------------------- 1 | module Dryrun 2 | class Device 3 | attr_accessor :name, :id 4 | 5 | def initialize(name, id) 6 | @name = name 7 | @id = id 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/dryrun/dryrun_utils.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | require 'dryrun/version' 3 | require 'open3' 4 | 5 | module Dryrun 6 | class DryrunUtils 7 | attr_accessor :sdk 8 | attr_accessor :device 9 | 10 | def self.execute(command) 11 | is_success = system command 12 | unless is_success 13 | puts "\n\n======================================================\n\n" 14 | puts ' Something went wrong while executing this:'.red 15 | puts " $ #{command}\n".yellow 16 | puts "======================================================\n\n" 17 | exit 1 18 | end 19 | end 20 | 21 | def self.latest_version 22 | url = 'https://raw.githubusercontent.com/cesarferreira/dryrun/master/lib/dryrun/version.rb' 23 | page_string = nil 24 | 25 | if Gem.win_platform? 26 | open(url, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) do |f| 27 | page_string = f.read 28 | end 29 | else 30 | URI.open(url) do |f| 31 | page_string = f.read 32 | end 33 | end 34 | 35 | page_string[/#{Regexp.escape('\'')}(.*?)#{Regexp.escape('\'')}/m, 1] 36 | end 37 | 38 | def self.up_to_date 39 | latest = latest_version 40 | latest.to_s <= Dryrun::VERSION.to_s 41 | end 42 | 43 | def self.run_adb(args) 44 | adb_arg = " -s #{$device.name} " unless $device.nil? 45 | path = "#{$sdk} #{adb_arg} #{args} " 46 | run(path) 47 | end 48 | 49 | def self.is_folder? (path) 50 | File.directory?(path) 51 | end 52 | 53 | def self.run(path) 54 | Open3.popen3(path) do |_stdin, stdout, _stderr| 55 | devices = [] 56 | stdout.each do |line| 57 | line = line.strip 58 | if !line.empty? && line !~ /^List of devices/ && !line.start_with?('adb') && !line.start_with?('*') 59 | parts = line.split 60 | devices << Dryrun::Device.new(parts[0], parts[1]) 61 | end 62 | end 63 | devices 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/dryrun/github.rb: -------------------------------------------------------------------------------- 1 | require 'tmpdir' 2 | require 'fileutils' 3 | require 'uri' 4 | require_relative 'dryrun_utils' 5 | 6 | module Dryrun 7 | class Github 8 | def initialize(url) 9 | @base_url = sanitize_url(url) 10 | @destination = destination 11 | end 12 | 13 | def sanitize_url(url) 14 | url = url.split('?').first 15 | url.chop! if url.end_with? '/' 16 | url 17 | end 18 | 19 | def destination 20 | unless @base_url.include? 'github.com' 21 | return Digest::SHA256.hexdigest @base_url 22 | end 23 | 24 | stripped_url = @base_url.gsub('.git', '') 25 | stripped_url = stripped_url.gsub('.git', '') 26 | stripped_url = stripped_url.gsub('git@github.com:', '') 27 | stripped_url = stripped_url.gsub('https://github.com/', '') 28 | stripped_url.gsub('http://github.com/', '') 29 | end 30 | 31 | def valid? 32 | starts_with_git = @base_url.split(//).first(4).join.eql? 'git@' 33 | starts_with_http = @base_url.split(//).first(7).join.eql? 'http://' 34 | starts_with_https = @base_url.split(//).first(8).join.eql? 'https://' 35 | 36 | (starts_with_git || starts_with_https || starts_with_http) 37 | end 38 | 39 | def cloneable_url 40 | starts_with_git = @base_url.split(//).first(4).join.eql? 'git@' 41 | ends_with_git = @base_url.split(//).last(4).join.eql? '.git' 42 | 43 | # ends with git but doesnt start with git 44 | return @base_url if ends_with_git && !starts_with_git 45 | 46 | # ends with git but doesnt start with git 47 | return "#{@base_url}.git" if !ends_with_git && !starts_with_git 48 | 49 | @base_url 50 | end 51 | 52 | ## 53 | ## CLONE THE REPOSITORY 54 | ## 55 | def clone(branch, tag, cleanup) 56 | cloneable = cloneable_url 57 | 58 | tmpdir = Dir.tmpdir + "/dryrun/#{@destination}" 59 | 60 | if cleanup 61 | puts 'Wiping the folder: ' + tmpdir.green 62 | FileUtils.rm_rf tmpdir 63 | # FileUtils.mkdir_p tmpdir 64 | end 65 | 66 | folder_exists = File.directory?(tmpdir) 67 | 68 | if folder_exists 69 | Dir.chdir tmpdir 70 | 71 | is_git_repo = system('git rev-parse') 72 | 73 | if !is_git_repo 74 | FileUtils.rm_rf(tmpdir) 75 | DryrunUtils.execute("git clone --depth 1 #{cloneable} #{tmpdir}") 76 | DryrunUtils.execute("git checkout #{branch}") 77 | else 78 | puts "Found project in #{tmpdir.green}..." 79 | DryrunUtils.execute('git reset --hard HEAD') 80 | DryrunUtils.execute('git fetch --all') 81 | DryrunUtils.execute("git checkout #{branch}") 82 | DryrunUtils.execute("git pull origin #{branch}") 83 | end 84 | else 85 | DryrunUtils.execute("git clone --depth 1 #{cloneable} #{tmpdir}") 86 | end 87 | 88 | if tag 89 | Dir.chdir tmpdir 90 | DryrunUtils.execute('git fetch --depth=10000') 91 | DryrunUtils.execute("git checkout #{tag}") 92 | end 93 | 94 | tmpdir 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /lib/dryrun/gradle_adapter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'dryrun_utils' 2 | 3 | module Dryrun 4 | class GradleAdapter 5 | 6 | def initialize(builder) 7 | @builder = builder 8 | end 9 | 10 | def clean 11 | DryrunUtils.execute("#{@builder} clean") 12 | end 13 | 14 | def run_android_tests(custom_module, flavour) 15 | if custom_module 16 | puts "#{@builder} :#{custom_module}:connected#{flavour}DebugAndroidTest" 17 | DryrunUtils.execute("#{@builder} :#{custom_module}:connected#{flavour}DebugAndroidTest") 18 | else 19 | puts "#{@builder} connected#{flavour}DebugAndroidTest" 20 | DryrunUtils.execute("#{@builder} connected#{flavour}DebugAndroidTest") 21 | end 22 | end 23 | 24 | def run_unit_tests(custom_module, flavour) 25 | if custom_module 26 | puts "#{@builder} :#{custom_module}:test#{flavour}DebugUnitTest" 27 | DryrunUtils.execute("#{@builder} :#{custom_module}:test#{flavour}DebugUnitTest") 28 | else 29 | puts "#{@builder} test#{flavour}DebugUnitTest" 30 | DryrunUtils.execute("#{@builder} test#{flavour}DebugUnitTest") 31 | end 32 | end 33 | 34 | def install(custom_module, flavour) 35 | if custom_module 36 | puts "#{@builder} :#{custom_module}:install#{flavour}Debug" 37 | DryrunUtils.execute("#{@builder} :#{custom_module}:install#{flavour}Debug") 38 | else 39 | puts "#{@builder} install#{flavour}Debug" 40 | DryrunUtils.execute("#{@builder} install#{flavour}Debug") 41 | end 42 | end 43 | 44 | 45 | def assemble(custom_module, flavour) 46 | if custom_module 47 | puts "#{@builder} :#{custom_module}:assemble#{flavour}Debug" 48 | DryrunUtils.execute("#{@builder} :#{custom_module}:assemble#{flavour}Debug") 49 | else 50 | puts "#{@builder} assemble#{flavour}Debug" 51 | DryrunUtils.execute("#{@builder} assemble#{flavour}Debug") 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/dryrun/install_application_command.rb: -------------------------------------------------------------------------------- 1 | require_relative 'dryrun_utils' 2 | require_relative 'android_utils' 3 | 4 | module Dryrun 5 | class InstallApplicationCommand 6 | 7 | def run(builder, package, launcher_activity, custom_module, flavour, device) 8 | execute_line = get_execution_command_line(package, launcher_activity) 9 | builder.clean 10 | 11 | if device.nil? 12 | puts 'No devices picked/available, proceeding with assemble instead'.green 13 | builder.assemble(custom_module, flavour) 14 | else 15 | builder.install(custom_module, flavour) 16 | end 17 | 18 | unless device.nil? 19 | AndroidUtils.clear_app_data(package) 20 | AndroidUtils.pretty_run(execute_line, package) 21 | end 22 | end 23 | 24 | def get_execution_command_line(package, launcher_activity) 25 | "am start -n \"#{launcheable_activity(package, launcher_activity)}\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER" 26 | end 27 | 28 | def launcheable_activity(package, launcher_activity) 29 | full_path_to_launcher = "#{package}#{launcher_activity.gsub(package, '')}" 30 | "#{package}/#{full_path_to_launcher}" 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/dryrun/manifest_parser.rb: -------------------------------------------------------------------------------- 1 | require 'oga' 2 | 3 | module Dryrun 4 | 5 | class ManifestParser 6 | 7 | attr_accessor :package, :launcher_activity 8 | 9 | def initialize(manifest_file) 10 | doc = Oga.parse_xml(manifest_file) 11 | 12 | @package = get_package(doc) 13 | @launcher_activity = get_launcher_activity(doc) 14 | end 15 | 16 | def get_package(doc) 17 | doc.xpath('//manifest').attr('package').first.value 18 | end 19 | 20 | def get_launcher_activity(doc) 21 | activities = doc.css('activity') 22 | activities.each do |child| 23 | intent_filter = child.css('intent-filter') 24 | 25 | if !intent_filter.nil? && !intent_filter.empty? 26 | return child.attr('android:name').value 27 | end 28 | end 29 | false 30 | end 31 | 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/dryrun/test_application_command.rb: -------------------------------------------------------------------------------- 1 | require_relative 'dryrun_utils' 2 | require_relative 'android_utils' 3 | 4 | module Dryrun 5 | class TestApplicationCommand 6 | 7 | def run(builder, package, launcher_activity, custom_module, flavour, device) 8 | execute_line = get_execution_command_line(package) 9 | builder.clean 10 | 11 | if device.nil? 12 | puts 'No devices picked/available, proceeding with unit tests instead'.green 13 | builder.run_unit_tests(custom_module, flavour) 14 | else 15 | builder.run_android_tests(custom_module, flavour) 16 | end 17 | 18 | unless device.nil? 19 | AndroidUtils.clear_app_data(package) 20 | AndroidUtils.pretty_run(execute_line, package) 21 | end 22 | end 23 | 24 | def get_execution_command_line(package) 25 | "adb shell am instrument -w #{package}" 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/dryrun/version.rb: -------------------------------------------------------------------------------- 1 | module Dryrun 2 | VERSION = '1.3.2'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /spec/dryrun_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | -------------------------------------------------------------------------------- /spec/github_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'dryrun/github' 3 | 4 | describe '# Github' do 5 | 6 | context 'URL validity' do 7 | it 'URL should be valid' do 8 | url = 'https://github.com/cesarferreira/android-helloworld' 9 | github = Dryrun::Github.new(url) 10 | expected = 'https://github.com/cesarferreira/android-helloworld.git' 11 | expect(github.cloneable_url).to eq(expected) 12 | end 13 | 14 | it 'URL that ends in .git should be valid' do 15 | url = 'https://github.com/googlesamples/google-services.git' 16 | github = Dryrun::Github.new(url) 17 | expected = 'https://github.com/googlesamples/google-services.git' 18 | expect(github.cloneable_url).to eq(expected) 19 | end 20 | 21 | it 'SSH URL should be valid' do 22 | url = 'git@github.com:cesarferreira/android-helloworld.git' 23 | github = Dryrun::Github.new(url) 24 | expected = 'git@github.com:cesarferreira/android-helloworld.git' 25 | expect(github.cloneable_url).to eq(expected) 26 | end 27 | 28 | it 'URL should not be valid' do 29 | url = 'asdasdas' 30 | github = Dryrun::Github.new(url) 31 | expect(github.valid?).to be false 32 | end 33 | end 34 | 35 | context 'URL destination folders' do 36 | it 'Given a regular url' do 37 | url = 'https://github.com/cesarferreira/android-helloworld' 38 | github = Dryrun::Github.new(url) 39 | expected = 'cesarferreira/android-helloworld' 40 | expect(github.destination).to eq(expected) 41 | end 42 | 43 | it 'Given a URL that ends in .git' do 44 | url = 'https://github.com/googlesamples/google-services.git' 45 | github = Dryrun::Github.new(url) 46 | expected = 'googlesamples/google-services' 47 | expect(github.destination).to eq(expected) 48 | end 49 | 50 | it 'Given a SSH URL' do 51 | url = 'git@github.com:cesarferreira/android-helloworld.git' 52 | github = Dryrun::Github.new(url) 53 | expected = 'cesarferreira/android-helloworld' 54 | expect(github.destination).to eq(expected) 55 | end 56 | 57 | it 'Given a non Github URL' do 58 | url = 'git@bitbucket.org:RyanBis/another-android-library.git' 59 | github = Dryrun::Github.new(url) 60 | expected = '2ef4153951350a0521cd8e02e4b629072dd515637610f0b48fe17a1a89a2c51a' 61 | expect(github.destination).to eq(expected) 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'dryrun' 2 | RSpec.configure do |config| 3 | # rspec-expectations config goes here. You can use an alternate 4 | # assertion/expectation library such as wrong or the stdlib/minitest 5 | # assertions if you prefer. 6 | config.expect_with :rspec do |expectations| 7 | # This option will default to `true` in RSpec 4. It makes the `description` 8 | # and `failure_message` of custom matchers include text for helper methods 9 | # defined using `chain`, e.g.: 10 | # be_bigger_than(2).and_smaller_than(4).description 11 | # # => "be bigger than 2 and smaller than 4" 12 | # ...rather than: 13 | # # => "be bigger than 2" 14 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 15 | end 16 | 17 | # rspec-mocks config goes here. You can use an alternate test double 18 | # library (such as bogus or mocha) by changing the `mock_with` option here. 19 | config.mock_with :rspec do |mocks| 20 | # Prevents you from mocking or stubbing a method that does not exist on 21 | # a real object. This is generally recommended, and will default to 22 | # `true` in RSpec 4. 23 | mocks.verify_partial_doubles = true 24 | end 25 | end 26 | --------------------------------------------------------------------------------