smartdust-client 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d5ad0e2e8968ddc2d92112ef56c8ad15403b99507f9699b45e18fd75c42adf76
4
+ data.tar.gz: a44754161a3a0a3eb9ce069717c6c00e5ce2ffce12d00380c43764863172101d
5
+ SHA512:
6
+ metadata.gz: 9ff4070a8ba7c49778a713b777e149487747a9aa28462f5cbc8bbbc7f0e8d918a344312f6652c49a4a6a2469fd4ab56d0ef09f4f6cb1b3feb1cb49598df3a7df
7
+ data.tar.gz: 84bd40b60b759ea84d4b498c96b0a91c139be9bc398bb0de72c3cc71d89347b5713b0c502d034bc3e75edd25447bd38c36832263059b632754bf5dd8145ad03f
data/.gitignore ADDED
@@ -0,0 +1,103 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ ### JetBrains template
10
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
11
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
12
+
13
+ # User-specific stuff:
14
+ .idea/workspace.xml
15
+ .idea/tasks.xml
16
+ .idea/dictionaries
17
+ .idea/vcs.xml
18
+ .idea/jsLibraryMappings.xml
19
+
20
+ # Sensitive or high-churn files:
21
+ .idea/dataSources.ids
22
+ .idea/dataSources.xml
23
+ .idea/dataSources.local.xml
24
+ .idea/sqlDataSources.xml
25
+ .idea/dynamic.xml
26
+ .idea/uiDesigner.xml
27
+
28
+ # Gradle:
29
+ .idea/gradle.xml
30
+ .idea/libraries
31
+
32
+ # Mongo Explorer plugin:
33
+ .idea/mongoSettings.xml
34
+
35
+ # RubyMine
36
+ .idea/.rakeTasks
37
+ .idea/misc.xml
38
+ .idea/modules.xml
39
+ .idea/stf-client.iml
40
+
41
+ ## File-based project format:
42
+ *.iws
43
+
44
+ ## Plugin-specific files:
45
+
46
+ # IntelliJ
47
+ /out/
48
+
49
+ # mpeltonen/sbt-idea plugin
50
+ .idea_modules/
51
+
52
+ # JIRA plugin
53
+ atlassian-ide-plugin.xml
54
+
55
+ # Crashlytics plugin (for Android Studio and IntelliJ)
56
+ com_crashlytics_export_strings.xml
57
+ crashlytics.properties
58
+ crashlytics-build.properties
59
+ fabric.properties
60
+ ### Ruby template
61
+ *.gem
62
+ *.rbc
63
+ /.config
64
+ /InstalledFiles
65
+ /spec/examples.txt
66
+ /test/tmp/
67
+ /test/version_tmp/
68
+
69
+ # Used by dotenv library to load environment variables.
70
+ # .env
71
+
72
+ ## Specific to RubyMotion:
73
+ .dat*
74
+ .repl_history
75
+ build/
76
+ *.bridgesupport
77
+ build-iPhoneOS/
78
+ build-iPhoneSimulator/
79
+
80
+ ## Specific to RubyMotion (use of CocoaPods):
81
+ #
82
+ # We recommend against adding the Pods directory to your .gitignore. However
83
+ # you should judge for yourself, the pros and cons are mentioned at:
84
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
85
+ #
86
+ # vendor/Pods/
87
+
88
+ ## Documentation cache and generated files:
89
+ /.yardoc/
90
+ /rdoc/
91
+
92
+ ## Environment normalization:
93
+ /vendor/bundle
94
+ /lib/bundler/man/
95
+
96
+ # for a library or gem, you might want to ignore these files since the code is
97
+ # intended to run in multiple environments; otherwise, check them in:
98
+ # Gemfile.lock
99
+ # .ruby-version
100
+ # .ruby-gemset
101
+
102
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
103
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
4
+ --format RspecJunitFormatter --out spec/reports/rspec.xml
5
+ --format html --out spec/reports/rspec.html
data/.simplecov ADDED
@@ -0,0 +1,10 @@
1
+ require 'simplecov-json'
2
+
3
+ SimpleCov.formatters = [
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ SimpleCov::Formatter::JSONFormatter
6
+ ]
7
+ SimpleCov.minimum_coverage 80
8
+ SimpleCov.start do
9
+ add_filter '/spec/'
10
+ end
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ smartdust-client (1.0.0)
5
+ ADB (~> 0.5)
6
+ dante (~> 0.2.0)
7
+ dry-configurable (~> 0.1.4)
8
+ dry-container (~> 0.6.0)
9
+ facets (~> 3.1.0)
10
+ gli (~> 2.17)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ ADB (0.5.6)
16
+ childprocess (>= 0.3.5)
17
+ addressable (2.8.1)
18
+ public_suffix (>= 2.0.2, < 6.0)
19
+ childprocess (4.1.0)
20
+ concurrent-ruby (1.2.2)
21
+ crack (0.4.5)
22
+ rexml
23
+ dante (0.2.0)
24
+ diff-lcs (1.5.0)
25
+ docile (1.4.0)
26
+ dry-configurable (0.1.7)
27
+ concurrent-ruby (~> 1.0)
28
+ dry-container (0.6.0)
29
+ concurrent-ruby (~> 1.0)
30
+ dry-configurable (~> 0.1, >= 0.1.3)
31
+ facets (3.1.0)
32
+ gli (2.21.0)
33
+ hashdiff (1.0.1)
34
+ json (2.6.3)
35
+ public_suffix (5.0.1)
36
+ rack (1.6.13)
37
+ rack-protection (1.5.5)
38
+ rack
39
+ rake (10.5.0)
40
+ rexml (3.2.5)
41
+ rspec (3.12.0)
42
+ rspec-core (~> 3.12.0)
43
+ rspec-expectations (~> 3.12.0)
44
+ rspec-mocks (~> 3.12.0)
45
+ rspec-core (3.12.1)
46
+ rspec-support (~> 3.12.0)
47
+ rspec-expectations (3.12.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.12.0)
50
+ rspec-mocks (3.12.4)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.12.0)
53
+ rspec-support (3.12.0)
54
+ rspec_junit_formatter (0.6.0)
55
+ rspec-core (>= 2, < 4, != 2.12.0)
56
+ simplecov (0.22.0)
57
+ docile (~> 1.1)
58
+ simplecov-html (~> 0.11)
59
+ simplecov_json_formatter (~> 0.1)
60
+ simplecov-html (0.12.3)
61
+ simplecov-json (0.2.3)
62
+ json
63
+ simplecov
64
+ simplecov_json_formatter (0.1.4)
65
+ sinatra (1.4.8)
66
+ rack (~> 1.5)
67
+ rack-protection (~> 1.4)
68
+ tilt (>= 1.3, < 3)
69
+ tilt (2.1.0)
70
+ webmock (2.3.2)
71
+ addressable (>= 2.3.6)
72
+ crack (>= 0.3.2)
73
+ hashdiff
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ bundler (~> 1.16.a)
80
+ rake (~> 10.0)
81
+ rspec (~> 3.5)
82
+ rspec_junit_formatter (~> 0.2)
83
+ simplecov (~> 0.14)
84
+ simplecov-json (~> 0.2)
85
+ sinatra (~> 1.4)
86
+ smartdust-client!
87
+ webmock (~> 2.1)
88
+
89
+ BUNDLED WITH
90
+ 1.17.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Anton Malinskiy
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ [![Build Status](https://travis-ci.org/Malinskiy/stf-client.svg?branch=master)](https://travis-ci.org/Malinskiy/stf-client)
2
+ [![Gem](https://img.shields.io/gem/v/stf-client.svg)](https://rubygems.org/gems/stf-client)
3
+ [![Gem](https://img.shields.io/gem/dt/stf-client.svg)](https://rubygems.org/gems/stf-client)
4
+
5
+ # Smartdust CLI client
6
+ ![smartdust-logo-text-2021.png](smartdust-logo-text-2021.png)
7
+ Automation client for connecting to Smartdust Lab devices via adb from a terminal.
8
+ Designed with the following scenario in mind:
9
+
10
+ 1. Connect to remote devices
11
+ 2. Do something with the device via adb (Instrumentation Test, adb install, etc)
12
+ 3. Disconnect from device
13
+
14
+ Due to it being a console tool, it's very easy to use it for test automation in a CI\CD pipeline, for example in Jenkins.
15
+
16
+ Allows for filtering by any device description parameter
17
+ as well as listing all available values of a given parameter
18
+ e.g. all unique names of devices in the lab instance.
19
+
20
+ ## Installation from source
21
+ ## Prequisities
22
+
23
+ - Ruby along with RubyGems installed - versions higher than 3.0.2 are not guaranteed to work
24
+ - This repository cloned
25
+
26
+ ## Installation
27
+ - Enter the directory where the repository is cloned
28
+ - adding "sudo" before the following commands might be needed:
29
+ - ```gem build smartdust-client.gemspec```
30
+ - ```gem install smartdust-client-1.0.0.gem``` (or different version, see output of the previous command)
31
+
32
+ - Run it by simply entering ```smartdust-client```
33
+
34
+ ## Usage
35
+
36
+ ```
37
+ NAME
38
+ stf-client - Smartphone Test Lab client
39
+
40
+ SYNOPSIS
41
+ stf-client [global options] command [command options] [arguments...]
42
+
43
+ GLOBAL OPTIONS
44
+ --help - Show this message
45
+ -t, --token=arg - Authorization token, can also be set by environment variable STF_TOKEN (default: none)
46
+ -u, --url=arg - URL to STF, can also be set by environment variable STF_URL (default: none)
47
+ -v, --[no-]verbose - Be verbose
48
+
49
+ COMMANDS
50
+ clean - Frees all devices that are assigned to current user in STF. Doesn't modify local adb
51
+ connect - Search for a device available in STF and attach it to local adb server
52
+ disconnect - Disconnect device(s) from local adb server and remove device(s) from user devices in STF
53
+ help - Shows a list of commands or help for one command
54
+ keys - Show available keys for filtering
55
+ values - Show known values for the filtering key
56
+
57
+ ENVIRONMENT VARIABLES
58
+ STF_TOKEN - Authorization token
59
+ STF_URL - URL to STF
60
+ ```
61
+ - Authorization token can be obtained from the Smartdust Lab web interface in Settings -> Keys
62
+ - When connecting with this tool for the first time, have the Smartdust Lab web interface open
63
+ to accept adding a new ADB key. This is necessary for every new machine that hasn't connected
64
+ yet to Remote Debug on a given Lab instance.
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ begin
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ rescue LoadError
6
+ end
7
+
8
+ task :default => :spec
9
+ task :test => :spec
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ require 'stf'
data/lib/di.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'dry-container'
2
+ require 'dante'
3
+ require 'stf/system/demonizer'
4
+
5
+ require 'stf/interactor/start_debug_session_interactor'
6
+ require 'stf/interactor/start_one_debug_session_interactor'
7
+ require 'stf/interactor/stop_debug_session_interactor'
8
+ require 'stf/interactor/stop_all_debug_sessions_interactor'
9
+ require 'stf/interactor/remove_all_user_devices_interactor'
10
+ require 'stf/interactor/get_keys_interactor'
11
+ require 'stf/interactor/get_values_interactor'
12
+ require 'stf/interactor/add_adb_public_key'
13
+ require 'stf/validate/uri_validator'
14
+ require 'stf/model/device_enhancer'
15
+
16
+ class DI
17
+ class << self
18
+ def init (opts = {})
19
+
20
+ c = Dry::Container.new
21
+ @@container = c
22
+
23
+ # one time object
24
+ c.register(:dante_runner,
25
+ -> {Dante::Runner.new('stf-client')})
26
+
27
+ # one time object because dante is one time
28
+ c.register(:demonizer,
29
+ -> do
30
+ Stf::Demonizer.new(c[:dante_runner],
31
+ log_path: opts[:log], pid_path: opts[:pid])
32
+ end)
33
+
34
+ c.register(:stf,
35
+ -> {Stf::Client.new(opts[:url], opts[:token])},
36
+ memoize: true)
37
+
38
+ c.register(:start_debug_session_interactor,
39
+ -> {Stf::StartDebugSessionInteractor.new},
40
+ memoize: true)
41
+
42
+ c.register(:start_one_debug_session_interactor,
43
+ -> {Stf::StartOneDebugSessionInteractor.new},
44
+ memoize: true)
45
+
46
+ c.register(:get_keys_interactor,
47
+ -> {Stf::GetKeysInteractor.new},
48
+ memoize: true)
49
+
50
+ c.register(:get_values_interactor,
51
+ -> {Stf::GetValuesInteractor.new},
52
+ memoize: true)
53
+
54
+ c.register(:stop_all_debug_sessions_interactor,
55
+ -> {Stf::StopAllDebugSessionsInteractor.new},
56
+ memoize: true)
57
+
58
+ c.register(:stop_debug_session_interactor,
59
+ -> {Stf::StopDebugSessionInteractor.new},
60
+ memoize: true)
61
+
62
+ c.register(:remove_all_user_devices_interactor,
63
+ -> {Stf::RemoveAllUserDevicesInteractor.new},
64
+ memoize: true)
65
+
66
+ c.register(:uri_validator,
67
+ -> {Stf::URIValidator.new},
68
+ memoize: true)
69
+
70
+ c.register(:add_adb_public_key_interactor,
71
+ -> {Stf::AddAdbPublicKeyInteractor.new},
72
+ memoize: true)
73
+ c.register(:device_enhancer,
74
+ -> {Stf::DeviceEnhancer.new},
75
+ memoize: true)
76
+ end
77
+
78
+ def [](what)
79
+ @@container.resolve(what)
80
+ end
81
+
82
+ def container
83
+ @@container
84
+ end
85
+ end
86
+ end
data/lib/stf/client.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'ostruct'
4
+
5
+ require 'stf/version'
6
+ require 'stf/log/log'
7
+
8
+ module Stf
9
+ class Client
10
+ include Log
11
+
12
+ def initialize(base_url, token)
13
+ @base_url = base_url
14
+ @token = token
15
+ end
16
+
17
+ def get_devices
18
+ response = execute '/api/v1/devices', Net::HTTP::Get
19
+ return response.devices
20
+ end
21
+
22
+ def get_device(serial)
23
+ response = execute "/api/v1/devices/#{serial}", Net::HTTP::Get
24
+ return response.device
25
+ end
26
+
27
+ def get_user
28
+ response = execute '/api/v1/user', Net::HTTP::Get
29
+ return response.user
30
+ end
31
+
32
+ def get_user_devices
33
+ response = execute '/api/v1/user/devices', Net::HTTP::Get
34
+ return response.devices
35
+ end
36
+
37
+ def add_device(serial)
38
+ response = execute '/api/v1/user/devices', Net::HTTP::Post, {serial: serial}.to_json
39
+ return response.success
40
+ end
41
+
42
+ def remove_device(serial)
43
+ response = execute "/api/v1/user/devices/#{serial}", Net::HTTP::Delete
44
+ return response.success
45
+ end
46
+
47
+ def start_debug(serial)
48
+ response = execute "/api/v1/user/devices/#{serial}/remoteConnect", Net::HTTP::Post
49
+ return response
50
+ end
51
+
52
+ def open_tunnel(provider_ip, port)
53
+ response = execute "/api/v1/tunnel", Net::HTTP::Post, {ipAddress: provider_ip, port: port}.to_json
54
+ return response
55
+ end
56
+
57
+ def destroy_tunnel(ip, port)
58
+ response = execute "/api/v1/tunnel", Net::HTTP::Delete, {ipAddress: ip, port: port}.to_json
59
+ return response.success
60
+ end
61
+
62
+ def stop_debug(serial)
63
+ response = execute "/api/v1/user/devices/#{serial}/remoteConnect", Net::HTTP::Delete
64
+ return response.success
65
+ end
66
+
67
+ def check_tunnel(provider_ip, port)
68
+ response = execute "/api/v1/tunnel/#{provider_ip}/#{port}", Net::HTTP::Get
69
+ return response
70
+ end
71
+
72
+ def add_adb_public_key(adb_public_key)
73
+ response = execute '/api/v1/user/adbPublicKeys', Net::HTTP::Post, {publickey: adb_public_key }.to_json
74
+ return response.success
75
+ end
76
+
77
+ private
78
+
79
+ def execute(relative_url, type, body='')
80
+ return execute_absolute @base_url + relative_url, type, body
81
+ end
82
+
83
+ def execute_absolute(url, type, body='', limit = 10)
84
+ raise ArgumentError, 'too many HTTP redirects' if limit == 0
85
+
86
+ uri = URI.parse(url)
87
+ http = Net::HTTP.new(uri.host, uri.port)
88
+ http.use_ssl = true if uri.scheme == 'https'
89
+ request = type.new(uri, 'Authorization' => "Bearer #{@token}", 'Content-Type' => 'application/json')
90
+ request.body = body
91
+ response = http.request(request)
92
+
93
+ case response
94
+ when Net::HTTPSuccess then
95
+ json = JSON.parse(response.body, object_class: OpenStruct)
96
+
97
+ logger.debug "API returned #{json}"
98
+ when Net::HTTPRedirection then
99
+ location = response['location']
100
+ logger.debug "redirected to #{location}"
101
+ return execute_absolute(location, type, body, limit - 1)
102
+ else
103
+ logger.error "API returned #{response.value}"
104
+ end
105
+
106
+ return json
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,20 @@
1
+ require 'di'
2
+
3
+ require 'stf/client'
4
+ require 'stf/log/log'
5
+
6
+ module Stf
7
+ class AddAdbPublicKeyInteractor
8
+ include Log
9
+
10
+ def execute(adb_public_key_location)
11
+ public_key = File.open(adb_public_key_location, 'rb', &:read)
12
+ success = DI[:stf].add_adb_public_key public_key
13
+ if success
14
+ logger.info "adb public key from '#{adb_public_key_location}' has been added"
15
+ elsif logger.error "Can't add public key from '#{adb_public_key_location}'"
16
+ return false
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require 'ADB'
2
+ require 'di'
3
+
4
+ require 'stf/client'
5
+ require 'stf/log/log'
6
+ require 'stf/model/device'
7
+
8
+ module Stf
9
+ class GetKeysInteractor
10
+ include Log
11
+ include ADB
12
+
13
+ def execute
14
+ devices = DI[:stf].get_devices
15
+
16
+ if devices.nil? || (devices.is_a?(Array) && devices.empty?)
17
+ logger.info 'No devices connected to STF'
18
+ return []
19
+ end
20
+
21
+ devices
22
+ .map {|d| Device.new(d)}
23
+ .flat_map {|d| d.getKeys}
24
+ .uniq
25
+ .sort
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ require 'di'
2
+ require 'ADB'
3
+
4
+ require 'stf/client'
5
+ require 'stf/log/log'
6
+ require 'stf/model/device'
7
+
8
+ module Stf
9
+ class GetValuesInteractor
10
+
11
+ include Log
12
+ include ADB
13
+
14
+ def execute(key)
15
+ devices = DI[:stf].get_devices
16
+
17
+ if devices.nil? || (devices.is_a?(Array) && devices.empty?)
18
+ logger.info r 'No devices connected to STF'
19
+ return []
20
+ end
21
+
22
+ devices
23
+ .map {|d| Device.new(d)}
24
+ .map {|d| d.getValue(key)}
25
+ .uniq
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ require 'di'
2
+
3
+ module Stf
4
+ class RemoveAllUserDevicesInteractor
5
+ def execute(opts = {})
6
+ DI[:demonizer].kill unless opts[:nokill]
7
+
8
+ devices = DI[:stf].get_user_devices
9
+ devices.each {|d| DI[:stf].remove_device d.serial}
10
+ end
11
+ end
12
+ end