Skip to content

Instantly share code, notes, and snippets.

@jhass
Last active June 9, 2017 08:20
Show Gist options
  • Save jhass/6aae5eebe2a7b0601a8f60ef28995482 to your computer and use it in GitHub Desktop.
Save jhass/6aae5eebe2a7b0601a8f60ef28995482 to your computer and use it in GitHub Desktop.
Update all connected devices and running emulators with the latest version of your app(s) from HockeyApp (install Ruby & bundler first)
#!/usr/bin/ruby
require "bundler/inline"
gemfile(ARGV.any? {|arg| arg == "--install"}) do
source "https://rubygems.org"
gem "http", "~> 2.2"
gem "ADB", "~> 0.5.6"
gem "slop", "~> 4.5"
end
module ADB
extend self
end
module HockeyApp
API_BASE = "https://rink.hockeyapp.net/api/2/"
API_TOKEN = ENV["HOCKEYAPP_API_TOKEN"]
module_function
def apps
get("apps")["apps"]
.select {|app| app["platform"] == "Android" }
.map {|app| App.new app }
end
def versions(app)
get("apps/#{app.app_id}/app_versions?include_build_urls=true")["app_versions"]
.select {|version| version["build_url"] }
.map {|version| Version.new app, version }
end
def get(endpoint)
HTTP.follow.headers("X-HockeyAppToken" => API_TOKEN).get(URI.join(API_BASE, endpoint)).parse
end
def download(url, local_path)
File.open(local_path, "w") do |file|
HTTP.follow.headers("X-HockeyAppToken" => API_TOKEN).get(url).body.each do |chunk|
file.write chunk
end
end
end
end
class App
attr_reader :app_id, :package
def self.all
HockeyApp.apps
end
def initialize(data)
@app_id = data["public_identifier"]
@package = data["bundle_identifier"]
end
def latest_version
versions.sort_by(&:code).last
end
def find_version(code)
version = versions.find {|version| version.code == code }
abort "#{@package} has no version #{code}" unless version
version
end
def versions
versions = HockeyApp.versions(self)
abort "No versions for #{@package}!" if versions.empty?
versions
end
end
class Version
attr_reader :app, :code
def initialize(app, data)
@app = app
@code = data["version"]
@download_url = data["build_url"]
end
def download_to(path)
local_path = File.join(path, "#{@app.package}-#{@code}.apk")
HockeyApp.download(@download_url, local_path)
local_path
end
end
class Device
attr_reader :serial
def self.all
ADB.devices.map {|serial| new serial }
end
def initialize(serial)
@serial = serial
end
def install(local_path)
ADB.install local_path, nil, serial: @serial
end
def uninstall(package)
ADB.uninstall package, serial: @serial
end
end
abort "Please set HOCKEYAPP_API_TOKEN" if HockeyApp::API_TOKEN.nil? || HockeyApp::API_TOKEN.empty?
opts = Slop.parse do |o|
o.bool "--install", "Install script dependencies"
o.array "-d", "--devices", "Limit actions to given devices instead of all"
o.array "-p", "--packages", "Limit actions to given apps (given by their package names)"
o.string "-v", "--version", "Install specific version instead of latest"
o.on "--help" do
puts o
exit
end
end
puts "Fetching devices"
devices = Device.all
abort "No devices connected!" if devices.empty?
unless opts[:devices].empty?
opts[:devices].each do |serial|
abort "Device #{serial} not connected!" if devices.none? {|device| device.serial == serial }
end
devices.select! {|device| opts[:device].include? device.serial }
end
puts "Fetching apps"
apps = App.all
abort "No apps configured in your HockeyApp account!" if apps.empty?
unless opts[:packages].empty?
opts[:packages].each do |package|
abort "App #{package} not found in HockeyApp!" if apps.none? {|app| app.package == package }
end
apps.select! {|app| opts[:packages].include? app.package }
end
puts "Fetching app versions"
if opts[:version]
versions = apps.map {|app| app.find_version(opts[:version]) }
else
versions = apps.map(&:latest_version)
end
Dir.mktmpdir do |download_folder|
versions.each do |version|
puts "Downloading #{version.app.package} #{version.code}"
local_path = version.download_to download_folder
devices.each do |device|
puts "Installing #{version.app.package} #{version.code} to #{device.serial}"
begin
device.install local_path
rescue ADB::ADBError => e # Failed retry with trying to uninstall first
begin
device.uninstall version.app.package
device.install local_path
rescue ADB::ADBError
raise e
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment