line_change 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c65a4310c8b322ca8e83d877813c75ca8a36f52e
4
+ data.tar.gz: 481470d559308aaa3680db3913c1f8cd1622160e
5
+ SHA512:
6
+ metadata.gz: 42b74ce653c6975c2c4b886eb1ca67f161cad14ac815c0f6fad8acdcf2c91149430febd4d42e5ab0579cec6d119312699dc7d1de3ad624d163635559f1f33d8a
7
+ data.tar.gz: 039c55389ff4bee0524ea3c294edddb66e3244dd8bd7c5f832a935fabed3d0a54ada8820635de66910c2af4d67e0c7c4bd74eec25df43a00e988ece04513de63
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,23 @@
1
+ script: "bundle exec rake spec"
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1
6
+ - 2.2.0-preview1
7
+ - ruby-head
8
+ - jruby
9
+ - rbx
10
+
11
+ env:
12
+ - LINE_CHANGE_CONFIG=spec/support/config/default.yml
13
+ - LINE_CHANGE_CONFIG=spec/support/config/installer.yml
14
+ - LINE_CHANGE_CONFIG=spec/support/config/config_with_path.yml
15
+
16
+ matrix:
17
+ allow_failures:
18
+ - rvm: 2.2.0-preview1
19
+ - rvm: ruby-head
20
+ - rvm: jruby
21
+ - rvm: rbx
22
+ fast_finish: true
23
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in line_change.gemspec
4
+ gemspec
5
+
6
+ gem 'pry'
7
+ gem 'did_you_mean'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Trevor John and Yuki Nishijima
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,147 @@
1
+ # LineChange [![Build Status](https://travis-ci.org/is-devteam/line_change.svg?branch=master)](https://travis-ci.org/is-devteam/line_change)
2
+
3
+ Easy apk upload tasks for HockeyApp.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'line_change'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install line_change
20
+
21
+ Then add the following line to your Rakefile. If you don't have a Rakefile, just create it.
22
+
23
+ ```ruby
24
+ require 'line_change/tasks'
25
+ ```
26
+
27
+ Then you should be able to run the install task:
28
+
29
+ ```
30
+ $ rake line_change:install
31
+ Generating a new config file: /path/to/your/project/config/line_change.yml
32
+ ```
33
+
34
+ ## Deployment
35
+
36
+ You need to provide your API key for HockeyApp. Open `config/line_change.yml` and set your API key to `api_key`. Also put at least one environment you want to upload an apk to:
37
+
38
+ ```yaml
39
+ api_key: <your-api-key-for-HockeyApp>
40
+ apps:
41
+ staging:
42
+ app_id: <app-id-for-staging-app>
43
+ path: '/path/to/apk/file/appname-staging.apk'
44
+ production:
45
+ app_id: <app-id-for-production-app>
46
+ path: '/path/to/apk/file/appname-production.apk'
47
+ ```
48
+
49
+ You are all set! Now you have a rake task for each environment:
50
+
51
+ ```
52
+ $ rake -T
53
+ rake line_change:production # Uploads apk to production (app_id: <app-id-for-production-app>)
54
+ rake line_change:staging # Uploads apk to staging (app_id: <app-id-for-staging-app>)
55
+ ```
56
+
57
+ And just execute one of the commands when you want to upload an apk file.
58
+
59
+ ```
60
+ $ rake line_change:staging
61
+ Uploading /path/to/apk/file/appname-staging.apk to HockeyApp... Done!
62
+
63
+ Response from HockeyApp:
64
+ version : 42
65
+ shortversion : 0.4.2-42
66
+ title : Your App Name
67
+ timestamp : 123581321
68
+ appsize : 3141592
69
+ notes : <p>Build number 42</p>
70
+ mandatory : false
71
+ external : false
72
+ device_family :
73
+ id : 42
74
+ app_id : 27182
75
+ minimum_os_version : 4.2
76
+ public_url : https://rink.hockeyapp.net/apps/<app-id-for-staging-app>
77
+ build_url : https://rink.hockeyapp.net/api/2/apps/<app-id-for-staging-app>/app_versions/42?format=apk&avtoken=<some-hash-value>
78
+ config_url : https://rink.hockeyapp.net/manage/apps/27182/app_versions/42
79
+ restricted_to_tags : false
80
+ status : 2
81
+ tags : []
82
+ created_at : 2014-10-23T14:03:45Z
83
+ updated_at : 2014-10-23T14:03:46Z
84
+ ```
85
+
86
+ ### Path Pattern
87
+ You can also set a pattern to `path`:
88
+
89
+ ```yaml
90
+ ...
91
+ apps:
92
+ beta:
93
+ ...
94
+ path: '/path/to/apk/file/appname-staging-*.apk'
95
+ ```
96
+
97
+ Then LineChange will look for the most recent modified file (based on **mtime**) from the list of files that match the pattern.
98
+
99
+ ```
100
+ $ rake beta
101
+ Uploading /path/to/apk/file/appname-staging-42.apk to HockeyApp...
102
+ ...
103
+ ```
104
+
105
+ ## Usage without Rake
106
+
107
+ You can also use LineChange without Rake. For example, you can use `#install` method if you want to install LineChange programatically:
108
+
109
+ ```ruby
110
+ require 'line_change'
111
+
112
+ LineChange.install
113
+ ```
114
+
115
+ It also provides `#deploy` method that uploads an apk file to HockeyApp:
116
+
117
+ ```ruby
118
+ require 'line_change'
119
+
120
+ app_id = '<App id for production app>'
121
+ path_to_apk = '/path/to/apk/file/appname-production.apk'
122
+
123
+ LineChange.deploy(app_id, path_to_apk)
124
+ ```
125
+
126
+ ## Supported Versions
127
+
128
+ * MRI 2.0.0, 2.1.x, 2.2.0-preview1 and ruby-head.
129
+
130
+ ## TODO
131
+
132
+ * Add the ability to write ERB in the config.
133
+ * Add an option to change `status`, `notify`, `notes` and `notes_type` from the config.
134
+ * Add support for other parameters like `private` and `dsym` (see [their doc](http://support.hockeyapp.net/kb/api/api-apps#upload-app)).
135
+ * Add support for JRuby and Rubinius.
136
+
137
+ ## Contributing
138
+
139
+ 1. Fork it ( https://github.com/is-devteam/line_change/fork )
140
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
141
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
142
+ 4. Push to the branch (`git push origin my-new-feature`)
143
+ 5. Create a new Pull Request
144
+
145
+ ## Copyright
146
+
147
+ Copyright © 2014 InSite Applications, LLC. See MIT-LICENSE for further details.
@@ -0,0 +1,33 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => "spec:all"
9
+
10
+ namespace :spec do
11
+ %w(
12
+ default
13
+ installer
14
+ config_with_path
15
+ ).each do |config_file|
16
+ desc "Run Tests against config: #{config_file}.yml"
17
+ task config_file do
18
+ sh "LINE_CHANGE_CONFIG=spec/support/config/#{config_file}.yml bundle exec rake spec"
19
+ end
20
+ end
21
+
22
+ desc "Run Tests against all configs"
23
+ task :all do
24
+ %w(
25
+ default
26
+ installer
27
+ config_with_path
28
+ ).each do |config_file|
29
+ sh "LINE_CHANGE_CONFIG=spec/support/config/#{config_file}.yml bundle exec rake spec"
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+ require "line_change/version"
3
+ require "line_change/errors"
4
+ require "line_change/configuration"
5
+ require "line_change/deploy"
6
+
7
+ module LineChange
8
+ DEFAULT_CONFIG = 'config/line_change.yml'.freeze
9
+
10
+ def self.config_path
11
+ @config_path ||= ENV['LINE_CHANGE_CONFIG'] || File.expand_path(DEFAULT_CONFIG)
12
+ end
13
+
14
+ def self.config_dir
15
+ @config_dir ||= File.dirname(config_path)
16
+ end
17
+
18
+ def self.configuration
19
+ @configuration ||= Configuration.new(YAML.load(open(config_path).read))
20
+ end
21
+
22
+ def self.deploy(app_id, apk_path)
23
+ Deploy.new(app_id, apk_path).start
24
+ end
25
+
26
+ def self.install
27
+ if Configuration.exists?
28
+ puts "You already have a config file in #{config_path}!"
29
+ else
30
+ puts "Generating a new config file: #{config_path}"
31
+ Configuration.create_config(config_dir, config_path)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,55 @@
1
+ module LineChange
2
+ class Configuration
3
+ attr_reader :api_key
4
+
5
+ def initialize(config)
6
+ @raw_apps = config['apps'] || config[:apps]
7
+ @api_key = config['api_key'] || config[:api_key]
8
+ end
9
+
10
+ def apps
11
+ @apps ||= Array(@raw_apps).map{|app_name, app_config| App.new(app_name, app_config) }
12
+ end
13
+
14
+ def self.exists?
15
+ File.exist?(LineChange.config_path)
16
+ end
17
+
18
+ def self.create_config(config_dir, config_path)
19
+ unless File.exist?(config_dir)
20
+ FileUtils.mkdir_p(config_dir)
21
+ end
22
+
23
+ FileUtils.cp("#{__dir__}/templates/line_change.yml", config_path)
24
+ end
25
+
26
+ class App
27
+ attr_reader :name
28
+ alias env name
29
+
30
+ def initialize(name, config)
31
+ @name, @config = name.to_s, normalize(config)
32
+ end
33
+
34
+ def app_id
35
+ @config[:app_id] || @config["app_id"]
36
+ end
37
+
38
+ def path
39
+ @config[:path] || @config["path"]
40
+ end
41
+
42
+ private
43
+
44
+ def normalize(config)
45
+ if config.is_a?(Hash)
46
+ config
47
+ elsif config.is_a?(String) || config.is_a?(Symbol)
48
+ {app_id: config.to_s}
49
+ else
50
+ raise 'Wrong config format: #{config}'
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,59 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'json'
4
+
5
+ require 'line_change/connection/response_handler'
6
+
7
+ module LineChange
8
+ class Connection
9
+
10
+ APK_MIME_TYPE = 'application/vnd.android.package-archive'.freeze
11
+ API_KEY_HEADER = 'X-HockeyAppToken'.freeze
12
+
13
+ Faraday::Response.register_middleware response_handler: ResponseHandler
14
+
15
+ def initialize(adapters = [Faraday.default_adapter], logging = false)
16
+ @conn = Faraday.new(url: 'https://rink.hockeyapp.net') do |faraday|
17
+ faraday.request :multipart
18
+ faraday.request :url_encoded
19
+ faraday.request :json
20
+
21
+ faraday.response :logger if logging
22
+ faraday.response :json
23
+ faraday.response :response_handler
24
+
25
+ faraday.adapter *adapters
26
+ end
27
+ end
28
+
29
+ def upload(path, id)
30
+ conn.post(url(id), body(path), API_KEY_HEADER => LineChange.configuration.api_key)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :conn
36
+
37
+ def url(id)
38
+ "/api/2/apps/#{id}/app_versions/upload"
39
+ end
40
+
41
+ def body(path)
42
+ {
43
+ status: 2,
44
+ notify: 0,
45
+ notes: notes(path),
46
+ notes_type: 0,
47
+ ipa: Faraday::UploadIO.new(path, APK_MIME_TYPE)
48
+ }
49
+ end
50
+
51
+ def notes(path)
52
+ if /^(\d+)/ =~ File.basename(path)
53
+ "Build number #{$1}"
54
+ else
55
+ "Build file #{File.basename(path)}"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ module LineChange
2
+ class Connection
3
+ class ResponseHandler < Faraday::Response::Middleware
4
+ ClientErrorStatuses = (400...500).freeze
5
+ ServerErrorStatuses = (500...600).freeze
6
+
7
+ def on_complete(env)
8
+ case env[:status]
9
+ when 404
10
+ raise LineChange::ResourceNotFound, response_values(env)
11
+ when 405
12
+ raise LineChange::MethodNotAllowed, response_values(env)
13
+ when 415
14
+ raise LineChange::UnsupportedMediaType, response_values(env)
15
+ when 422
16
+ raise LineChange::UnprocessableEntity, response_values(env)
17
+ when ClientErrorStatuses
18
+ raise LineChange::ClientError, response_values(env)
19
+ when ServerErrorStatuses
20
+ raise LineChange::ServerError, response_values(env)
21
+ end
22
+ end
23
+
24
+ def response_values(env)
25
+ {status: env.status, headers: env.response_headers, body: env.body}
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ require 'line_change/connection'
2
+
3
+ module LineChange
4
+ class Deploy
5
+ attr_reader :id, :path
6
+
7
+ def initialize(id, path)
8
+ @id, @path = id, path
9
+ end
10
+
11
+ def start
12
+ print "Uploading #{most_recent_modified_file_path} to HockeyApp... "
13
+
14
+ connection.upload(most_recent_modified_file_path, id).tap do |response|
15
+ puts "Done!" "\n\n"
16
+ puts "Response from HockeyApp:"
17
+
18
+ response.body.each do |key, value|
19
+ puts "#{' ' * 4}" "#{key.ljust(19)}: #{value}"
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def most_recent_modified_file_path
27
+ @most_recent_modified_file_path ||= begin
28
+ file = Dir[path].map{|file_path| File.open(file_path) }.sort_by(&:mtime).last
29
+
30
+ if file
31
+ file.path
32
+ else
33
+ raise FileNotFound, "No such file or directory: #{path}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def connection
39
+ @connection ||= Connection.new
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ module LineChange
2
+ class Error < StandardError; end
3
+
4
+ # API errors
5
+ class APIError < Error
6
+ attr_reader :status, :headers, :body
7
+
8
+ def initialize(args)
9
+ @status, @headers, @body = args[:status], args[:headers], parse_json(args[:body])
10
+
11
+ message =
12
+ body['message'] ||
13
+ (body['errors'] && convert_hash_to_message(body['errors'])) ||
14
+ body
15
+
16
+ super(message)
17
+ end
18
+
19
+ private
20
+
21
+ def parse_json(body)
22
+ JSON.parse(body.to_s)
23
+ rescue JSON::ParserError
24
+ body
25
+ end
26
+
27
+ def convert_hash_to_message(hash)
28
+ hash.map{|_| _.join(' ').capitalize }.join('. ')
29
+ end
30
+ end
31
+
32
+ class ServerError < APIError; end
33
+ class ClientError < APIError; end
34
+
35
+ # 500
36
+ class InternalServerError < ServerError; end
37
+
38
+ # 404
39
+ class ResourceNotFound < ClientError; end
40
+
41
+ # 405
42
+ class MethodNotAllowed < ClientError; end
43
+
44
+ # 415
45
+ class UnsupportedMediaType < ClientError; end
46
+
47
+ # 422
48
+ class UnprocessableEntity < ClientError; end
49
+
50
+ class FileNotFound < Error; end
51
+ end
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+ require 'line_change'
3
+
4
+ namespace :line_change do
5
+ LineChange.configuration.apps.each do |app|
6
+ desc "Uploads apk to #{app.env} (app_id: #{app.app_id})"
7
+
8
+ if app.path
9
+ task(app.env){ LineChange.deploy(app.app_id, app.path) }
10
+ else
11
+ task(app.env, :apk_path){|_, args| LineChange.deploy(app.app_id, args[:apk_path]) }
12
+ end
13
+ end if LineChange::Configuration.exists?
14
+
15
+ desc 'Creates a config file in config/line_change.yml'
16
+ task :install do
17
+ LineChange.install
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ api_key: # Your api_key should come here.
2
+ apps:
3
+ # staging:
4
+ # app_id: staging_app_id
5
+ # path: path/to/uploadable/staging/file
6
+ #
7
+ # production:
8
+ # app_id: production_app_id
9
+ # path: path/to/uploadable/production/file
@@ -0,0 +1,3 @@
1
+ module LineChange
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'line_change/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "line_change"
8
+ spec.version = LineChange::VERSION
9
+ spec.authors = ["IS Dev team"]
10
+ spec.email = ["devteam@isapp.com"]
11
+ spec.summary = %q{Easy apk upload tasks for HockeyApp.}
12
+ spec.description = %q{LineChange provides useful rake tasks for app deployment.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "faraday"
22
+ spec.add_dependency "faraday_middleware"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec"
27
+ end
@@ -0,0 +1,78 @@
1
+ describe LineChange::Configuration, if: default? do
2
+ describe '#apps' do
3
+ subject { configuration.apps.first }
4
+
5
+ context 'with string hash' do
6
+ let(:configuration) { LineChange::Configuration.new("apps" => {"prod" => "prod-app-id"}) }
7
+
8
+ it "accesses the apps" do
9
+ expect(subject.env).to eq("prod")
10
+ expect(subject.app_id).to eq("prod-app-id")
11
+ end
12
+ end
13
+
14
+ context 'with symbol hash' do
15
+ let(:configuration) { LineChange::Configuration.new(apps: {prod: "prod-app-id"}) }
16
+
17
+ it "accesses the apps" do
18
+ expect(subject.env).to eq("prod")
19
+ expect(subject.app_id).to eq("prod-app-id")
20
+ end
21
+ end
22
+
23
+ context 'with multiple environments' do
24
+ let(:configuration) do
25
+ LineChange::Configuration.new(apps: {
26
+ staging: {
27
+ app_id: "staging-app-id",
28
+ path: "/path/to/staging/apk"
29
+ },
30
+ production: {
31
+ app_id: "production-app-id",
32
+ path: "/path/to/production/apk"
33
+ }
34
+ })
35
+ end
36
+
37
+ describe "first app config" do
38
+ subject { configuration.apps[0] }
39
+
40
+ it "accesses the apps" do
41
+ expect(subject.env).to eq("staging")
42
+ expect(subject.app_id).to eq("staging-app-id")
43
+ expect(subject.path).to eq("/path/to/staging/apk")
44
+ end
45
+ end
46
+
47
+ describe "second app config" do
48
+ subject { configuration.apps[1] }
49
+
50
+ it "accesses the apps" do
51
+ expect(subject.env).to eq("production")
52
+ expect(subject.app_id).to eq("production-app-id")
53
+ expect(subject.path).to eq("/path/to/production/apk")
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#api_key' do
60
+ subject { configuration.api_key }
61
+
62
+ context 'with string hash' do
63
+ let(:configuration) { LineChange::Configuration.new({'api_key' => 'bar'}) }
64
+
65
+ it "accesses the api key" do
66
+ expect(subject).to eq('bar')
67
+ end
68
+ end
69
+
70
+ context 'with symbol hash' do
71
+ let(:configuration) { LineChange::Configuration.new({api_key: 'bar'}) }
72
+
73
+ it "accesses the api key" do
74
+ expect(subject).to eq('bar')
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,127 @@
1
+ describe LineChange::Connection, if: default? do
2
+ describe '#upload' do
3
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
4
+ let(:adapters) { [:test, stubs] }
5
+ let(:app_id) { 'app_id' }
6
+ let(:api_key) { 'api key' }
7
+ let(:apk_path) { '/path/app.apk' }
8
+ let(:apk_file) { double(:apk_file, to_s: 'apk_file') }
9
+
10
+ before do
11
+ allow(Faraday::UploadIO).to receive(:new) { apk_file }
12
+ end
13
+
14
+ subject { LineChange::Connection.new(adapters).upload(apk_path, app_id) }
15
+
16
+ context 'when success' do
17
+ before do
18
+ stubs.post("/api/2/apps/#{app_id}/app_versions/upload") do |env|
19
+ @request_body = env.body
20
+ [200, {}, response_body.to_json]
21
+ end
22
+
23
+ allow(LineChange.configuration).to receive(:api_key) { api_key }
24
+ end
25
+
26
+ let(:response_body) { {'success' => true} }
27
+
28
+ it 'post the request' do
29
+ expect(subject).to be_success
30
+ end
31
+
32
+ it 'makes the request with the api key header' do
33
+ expect(subject.env.request_headers[LineChange::Connection::API_KEY_HEADER]).to eq api_key
34
+ end
35
+
36
+ it 'makes the request with the body' do
37
+ subject
38
+
39
+ expect(@request_body).to eq('ipa=apk_file&notes=Build+file+app.apk&notes_type=0&notify=0&status=2')
40
+ end
41
+ end
42
+
43
+ context 'when failure' do
44
+ before do
45
+ stubs.post("/api/2/apps/#{app_id}/app_versions/upload") { response }
46
+ end
47
+
48
+ context 'when status is 404' do
49
+ let(:response_body) do
50
+ {
51
+ "errors" => {
52
+ "app" => ["not found"]
53
+ }
54
+ }
55
+ end
56
+
57
+ let(:response) { [404, {}, response_body.to_json] }
58
+
59
+ it 'raises LineChange::NotFound' do
60
+ expect { subject }.to raise_error(LineChange::ResourceNotFound) do |error|
61
+ expect(error.message).to eq("App not found")
62
+ expect(error.status).to eq(404)
63
+ expect(error.headers).to eq({})
64
+ expect(error.body).to eq(response_body)
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'when status is 405' do
70
+ let(:response) { [405, {}, '<html><h1>405 Not Allowed</h1></html>'] }
71
+
72
+ it 'raises LineChange::NotFound' do
73
+ expect { subject }.to raise_error(LineChange::MethodNotAllowed) do |error|
74
+ expect(error.message).to eq("<html><h1>405 Not Allowed</h1></html>")
75
+ expect(error.status).to eq(405)
76
+ expect(error.headers).to eq({})
77
+ expect(error.body).to eq("<html><h1>405 Not Allowed</h1></html>")
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'when status is 415' do
83
+ let(:response) { [415, {}, '<html>h1>415 Unsupported Media Type</h1></html>'] }
84
+
85
+ it 'raises LineChange::NotFound' do
86
+ expect { subject }.to raise_error(LineChange::UnsupportedMediaType)
87
+ end
88
+ end
89
+
90
+ context 'when status is 422' do
91
+ let(:response_body) do
92
+ {
93
+ "status" => "error",
94
+ "message" => "Bundle version is blank."
95
+ }
96
+ end
97
+
98
+ let(:response) { [422, {}, response_body.to_json] }
99
+
100
+ it 'raises LineChange::UnprocessableEntity' do
101
+ expect { subject }.to raise_error(LineChange::UnprocessableEntity) do |error|
102
+ expect(error.message).to eq("Bundle version is blank.")
103
+ expect(error.status).to eq(422)
104
+ expect(error.headers).to eq({})
105
+ expect(error.body).to eq(response_body)
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'when status is other client error status' do
111
+ let(:response) { [403, {}, ''] }
112
+
113
+ it 'raises LineChange::NotFound' do
114
+ expect { subject }.to raise_error(LineChange::ClientError)
115
+ end
116
+ end
117
+
118
+ context 'when status is 500' do
119
+ let(:response) { [500, {}, ''] }
120
+
121
+ it 'raises LineChange::ServerError' do
122
+ expect { subject }.to raise_error(LineChange::ServerError)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,42 @@
1
+ describe LineChange::Deploy, if: default? do
2
+ describe '#start' do
3
+ let(:app_id) { 'app id' }
4
+ let(:connection) { instance_double(LineChange::Connection) }
5
+ let(:response) { double(:response, body: {'great' => 'result!'}) }
6
+
7
+ subject { LineChange::Deploy.new(app_id, apk_path) }
8
+
9
+ before do
10
+ allow(LineChange::Connection).to receive(:new).and_return(connection)
11
+ allow(connection).to receive(:upload) { response }
12
+ end
13
+
14
+ context "when file exists" do
15
+ let(:apk_path) { "spec/support/fixtures/*.txt" }
16
+
17
+ it "uploads the path to the connection" do
18
+ without_output { subject.start }
19
+
20
+ expect(connection).to have_received(:upload).with("spec/support/fixtures/newest.txt", app_id)
21
+ end
22
+
23
+ it "outputs progress and result" do
24
+ output =
25
+ "Uploading spec/support/fixtures/newest.txt to HockeyApp... Done!\n" \
26
+ "\n" \
27
+ "Response from HockeyApp:\n" \
28
+ " great : result!\n"
29
+
30
+ expect { subject.start }.to output(output).to_stdout
31
+ end
32
+ end
33
+
34
+ context "when file doesn't exist" do
35
+ let(:apk_path) { "does/not/exist" }
36
+
37
+ it "raises an error" do
38
+ expect { subject.start }.to raise_error(LineChange::FileNotFound)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+
2
+ describe LineChange, if: default? do
3
+ describe '.configuration' do
4
+ subject { LineChange.configuration }
5
+
6
+ it 'provides a getter for api_key' do
7
+ expect(subject.api_key).to eq('api_key')
8
+ end
9
+
10
+ it 'provides a getter for apps' do
11
+ expect(subject.apps.first.name).to eq("production")
12
+ end
13
+ end
14
+
15
+ describe '.deploy' do
16
+ let(:app_id) { 'app_id' }
17
+ let(:apk_path) { 'apk_path' }
18
+ let(:deploy) { instance_double(LineChange::Deploy) }
19
+
20
+ subject { LineChange.deploy(app_id, apk_path) }
21
+
22
+ before do
23
+ allow(LineChange::Deploy).to receive(:new).with(app_id, apk_path).and_return(deploy)
24
+ allow(deploy).to receive(:start)
25
+ end
26
+
27
+ it "instantiates a deploy object with the api key" do
28
+ subject
29
+
30
+ expect(LineChange::Deploy).to have_received(:new).with(app_id, apk_path)
31
+ end
32
+
33
+ it "deploys the apk with the app id" do
34
+ subject
35
+
36
+ expect(deploy).to have_received(:start)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,104 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'pry'
5
+ require 'did_you_mean'
6
+ require 'line_change'
7
+
8
+ def default?
9
+ LineChange.config_path.end_with?('default.yml')
10
+ end
11
+
12
+ Dir["#{__dir__}/support/**/*.rb"].each { |f| require f }
13
+
14
+ # This file was generated by the `rspec --init` command. Conventionally, all
15
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
16
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
17
+ # file to always be loaded, without a need to explicitly require it in any files.
18
+ #
19
+ # Given that it is always loaded, you are encouraged to keep this file as
20
+ # light-weight as possible. Requiring heavyweight dependencies from this file
21
+ # will add to the boot time of your test suite on EVERY test run, even for an
22
+ # individual file that may not need all of that loaded. Instead, consider making
23
+ # a separate helper file that requires the additional dependencies and performs
24
+ # the additional setup, and require it from the spec files that actually need it.
25
+ #
26
+ # The `.rspec` file also contains a few flags that are not defaults but that
27
+ # users commonly want.
28
+ #
29
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
30
+ RSpec.configure do |config|
31
+ config.include StdoutHelper
32
+
33
+ # rspec-expectations config goes here. You can use an alternate
34
+ # assertion/expectation library such as wrong or the stdlib/minitest
35
+ # assertions if you prefer.
36
+ config.expect_with :rspec do |expectations|
37
+ # This option will default to `true` in RSpec 4. It makes the `description`
38
+ # and `failure_message` of custom matchers include text for helper methods
39
+ # defined using `chain`, e.g.:
40
+ # be_bigger_than(2).and_smaller_than(4).description
41
+ # # => "be bigger than 2 and smaller than 4"
42
+ # ...rather than:
43
+ # # => "be bigger than 2"
44
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
45
+ end
46
+
47
+ # rspec-mocks config goes here. You can use an alternate test double
48
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
49
+ config.mock_with :rspec do |mocks|
50
+ # Prevents you from mocking or stubbing a method that does not exist on
51
+ # a real object. This is generally recommended, and will default to
52
+ # `true` in RSpec 4.
53
+ mocks.verify_partial_doubles = true
54
+ end
55
+
56
+ # The settings below are suggested to provide a good initial experience
57
+ # with RSpec, but feel free to customize to your heart's content.
58
+ =begin
59
+ # These two settings work together to allow you to limit a spec run
60
+ # to individual examples or groups you care about by tagging them with
61
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
62
+ # get run.
63
+ config.filter_run :focus
64
+ config.run_all_when_everything_filtered = true
65
+
66
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
67
+ # For more details, see:
68
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
69
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
70
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
71
+ config.disable_monkey_patching!
72
+
73
+ # This setting enables warnings. It's recommended, but in some cases may
74
+ # be too noisy due to issues in dependencies.
75
+ config.warnings = true
76
+
77
+ # Many RSpec users commonly either run the entire suite or an individual
78
+ # file, and it's useful to allow more verbose output when running an
79
+ # individual spec file.
80
+ if config.files_to_run.one?
81
+ # Use the documentation formatter for detailed output,
82
+ # unless a formatter has already been configured
83
+ # (e.g. via a command-line flag).
84
+ config.default_formatter = 'doc'
85
+ end
86
+
87
+ # Print the 10 slowest examples and example groups at the
88
+ # end of the spec run, to help surface which specs are running
89
+ # particularly slow.
90
+ config.profile_examples = 10
91
+
92
+ # Run specs in random order to surface order dependencies. If you find an
93
+ # order dependency and want to debug it, you can fix the order by providing
94
+ # the seed, which is printed after each run.
95
+ # --seed 1234
96
+ config.order = :random
97
+
98
+ # Seed global randomization in this process using the `--seed` CLI option.
99
+ # Setting this allows you to use `--seed` to deterministically reproduce
100
+ # test failures related to randomization by passing the same `--seed` value
101
+ # as the one that triggered the failure.
102
+ Kernel.srand config.seed
103
+ =end
104
+ end
@@ -0,0 +1,9 @@
1
+ api_key: api_key
2
+ apps:
3
+ staging:
4
+ app_id: staging_id
5
+ path: path/to/uploadable/staging/file
6
+
7
+ production:
8
+ app_id: production_id
9
+ path: path/to/uploadable/production/file
@@ -0,0 +1,3 @@
1
+ api_key: api_key
2
+ apps:
3
+ production: production_id
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ module StdoutHelper
2
+ def without_output
3
+ org, $stdout = $stdout, double(:stdout, write: nil)
4
+ yield
5
+ ensure
6
+ $stdout = org
7
+ end
8
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+ require 'line_change/tasks'
3
+
4
+ describe 'hockeyapp task' do
5
+ context 'basic config', if: LineChange.config_path.end_with?('default.yml') do
6
+ let(:app_id) { 'production_id' }
7
+ let(:apk_path) { 'path' }
8
+
9
+ describe 'production' do
10
+ it 'uploads an apk to production' do
11
+ allow(LineChange).to receive(:deploy)
12
+
13
+ Rake::Task['line_change:production'].invoke(apk_path)
14
+
15
+ expect(LineChange).to have_received(:deploy).with(app_id, apk_path)
16
+ end
17
+ end
18
+ end
19
+
20
+ context 'config with path', if: LineChange.config_path.end_with?('config_with_path.yml') do
21
+ describe 'staging' do
22
+ let(:app_id) { 'staging_id' }
23
+ let(:apk_path) { 'path/to/uploadable/staging/file' }
24
+
25
+ it 'uploads an apk to production' do
26
+ allow(LineChange).to receive(:deploy)
27
+
28
+ Rake::Task['line_change:staging'].invoke
29
+
30
+ expect(LineChange).to have_received(:deploy).with(app_id, apk_path)
31
+ end
32
+ end
33
+
34
+ describe 'production' do
35
+ let(:app_id) { 'production_id' }
36
+ let(:apk_path) { 'path/to/uploadable/production/file' }
37
+
38
+ it 'uploads an apk to production' do
39
+ allow(LineChange).to receive(:deploy)
40
+
41
+ Rake::Task['line_change:production'].invoke
42
+
43
+ expect(LineChange).to have_received(:deploy).with(app_id, apk_path)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'install task', if: LineChange.config_path.end_with?('installer.yml') do
49
+ before do
50
+ Rake::Task['line_change:install'].reenable
51
+ end
52
+
53
+ after do
54
+ File.delete(LineChange.config_path) if File.exist?(LineChange.config_path)
55
+ end
56
+
57
+ context "when config file doesn't exist" do
58
+ it 'generates an exmaple config file' do
59
+ without_output { Rake::Task['line_change:install'].invoke }
60
+
61
+ expect(File).to exist(LineChange.config_path)
62
+ expect(open(File.expand_path("lib/line_change/templates/line_change.yml")).read). to eq(open(LineChange.config_path).read)
63
+ end
64
+
65
+ it "tells it is creating a new config file" do
66
+ expect do
67
+ Rake::Task['line_change:install'].invoke
68
+ end.to output("Generating a new config file: #{LineChange.config_path}\n").to_stdout
69
+ end
70
+ end
71
+
72
+ context "when config file already exists" do
73
+ before do
74
+ without_output { Rake::Task['line_change:install'].invoke }
75
+ Rake::Task['line_change:install'].reenable
76
+ end
77
+
78
+ it "doesn't do anything" do
79
+ sleep(1)
80
+
81
+ expect do
82
+ without_output { Rake::Task['line_change:install'].invoke }
83
+ end.not_to change { File.open(LineChange.config_path).mtime }
84
+ end
85
+
86
+ it "tells it is skipping execution" do
87
+ expect do
88
+ Rake::Task['line_change:install'].invoke
89
+ end.to output("You already have a config file in #{LineChange.config_path}!\n").to_stdout
90
+ end
91
+ end
92
+ end
93
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: line_change
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - IS Dev team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: LineChange provides useful rake tasks for app deployment.
84
+ email:
85
+ - devteam@isapp.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/line_change.rb
98
+ - lib/line_change/configuration.rb
99
+ - lib/line_change/connection.rb
100
+ - lib/line_change/connection/response_handler.rb
101
+ - lib/line_change/deploy.rb
102
+ - lib/line_change/errors.rb
103
+ - lib/line_change/tasks.rb
104
+ - lib/line_change/templates/line_change.yml
105
+ - lib/line_change/version.rb
106
+ - line_change.gemspec
107
+ - spec/configuration_spec.rb
108
+ - spec/connection_spec.rb
109
+ - spec/deploy_spec.rb
110
+ - spec/line_change_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/support/config/config_with_path.yml
113
+ - spec/support/config/default.yml
114
+ - spec/support/fixtures/newest.txt
115
+ - spec/support/fixtures/old.txt
116
+ - spec/support/helpers/stdout_helper.rb
117
+ - spec/tasks/line_change_spec.rb
118
+ homepage: ''
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.2.2
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Easy apk upload tasks for HockeyApp.
142
+ test_files:
143
+ - spec/configuration_spec.rb
144
+ - spec/connection_spec.rb
145
+ - spec/deploy_spec.rb
146
+ - spec/line_change_spec.rb
147
+ - spec/spec_helper.rb
148
+ - spec/support/config/config_with_path.yml
149
+ - spec/support/config/default.yml
150
+ - spec/support/fixtures/newest.txt
151
+ - spec/support/fixtures/old.txt
152
+ - spec/support/helpers/stdout_helper.rb
153
+ - spec/tasks/line_change_spec.rb