line_change 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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