Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# BetaBuilder, a gem for managing iOS ad-hoc builds

BetaBuilder is a simple collection of Rake tasks and utilities for managing and publishing Adhoc builds of your iOS apps.
BetaBuilder is a simple collection of Rake tasks and utilities for managing and publishing Adhoc builds of your iOS apps.

If you're looking for the OSX BetaBuilder app -- to which this gem owes most of the credit -- you can find it [here on Github](http://github.com/HunterHillegas/iOS-BetaBuilder).

Expand All @@ -26,22 +26,22 @@ At the top of your Rakefile, you'll need to require `rubygems` and the `betabuil

require 'rubygems'
require 'betabuilder'

Because BetaBuilder is a Rake task library, you do not need to define any tasks yourself. You simply need to configure BetaBuilder with some basic information about your project and it will generate the tasks for you. A sample configuration might look something like this:

BetaBuilder::Tasks.new do |config|
# your Xcode target name
config.target = "MyGreatApp"

# the Xcode configuration profile
config.configuration = "Adhoc"
config.configuration = "Adhoc"
end

Now, if you run `rake -T` in Terminal.app in the root of your project, the available tasks will be printed with a brief description of each one:

rake beta:build # Build the beta release of the app
rake beta:package # Package the beta release as an IPA file

If you use a custom Xcode build directory, rather than the default `${SRCROOT}/build` location, you can configure that too:

BetaBuilder::Tasks.new do |config|
Expand All @@ -59,7 +59,7 @@ To use a namespace other than "beta" for the generated tasks, simply pass in you

BetaBuilder::Tasks.new(:my_custom_namespace) do |config|
end

This lets you set up different sets of BetaBuilder tasks for different configurations in the same Rakefile (e.g. a production and staging build).

## Xcode 4 support
Expand All @@ -69,19 +69,19 @@ Betabuilder works with Xcode 4, but you may need to tweak your task configuratio
If you are using the Xcode derived data directory for your builds, then you will need to specify this. Betabuilder will then scan your build log to determine the path to the automatically generated build directory that Xcode is using for your project.

config.build_dir = :derived

This will become the default in 0.8.

If you wish to generate archives for your Xcode 4 project, you will need to enable this. This will become the default in future once Xcode 3 support is dropped (deprecated in 0.7):

config.xcode4_archive_mode = true

If you are working with an Xcode 4 workspace instead of a project file, you will need to configure this too:

config.workspace_path = "MyWorkspace.xcworkspace"
config.scheme = "My App Scheme"
config.app_name = "MyApp"

If you are using a workspace, then you must specify the scheme. You can still specify the build configuration (e.g. Release).

## Automatic deployment with deployment strategies
Expand All @@ -98,8 +98,8 @@ TestFlight provides an upload API and betabuilder uses that to provide a `:testf
tf.api_token = "YOUR_API_TOKEN"
tf.team_token = "YOUR_TEAM_TOKEN"
end
Now, instead of using the `beta:package` task, you can run the `beta:deploy` task instead. This task will run the package task as a dependency and upload the generated IPA file to TestFlight.

Now, instead of using the `beta:package` task, you can run the `beta:deploy` task instead. This task will run the package task as a dependency and upload the generated IPA file to TestFlight.

You will be prompted to enter the release notes for the build; TestFlight requires these to inform your testers of what has changed in this build. Alternatively, if you have a way of generating the release notes automatically (for instance, using a CHANGELOG file or a git log command), you can specify a block that will be called at runtime - you can do whatever you want in this block, as long as you return a string which will be used as the release notes, e.g.

Expand All @@ -109,14 +109,30 @@ You will be prompted to enter the release notes for the build; TestFlight requir
# return release notes here
end
end

Finally, you can also specify an array of distribution lists that you want to allow access to the build:

config.deploy_using(:testflight) do |tf|
...
tf.distribution_lists = %w{Testers Internal}
end

### Deploying to HockeyApp

Similar to TestFlight you can also use [HockeyApp](http://hockeyapp.net), you'll need your API token and your App ID.

Example:

config.deploy_using(:hockeyapp) do |puck|
puck.api_token = "YOUR_API_TOKEN"
puck.app_id = "YOUR_APP_ID"
puck.allow_download = true

puck.generate_release_notes do
# return release notes here
end
end

### Deploying to your own server

BetaBuilder also comes with a rather rudimentary web-based deployment task that uses SCP, so you will need SSH access to your server and appropriate permissions to use it. This works in the same way as the original iOS-BetaBuilder GUI app by generating a HTML template and manifest file that can be uploaded to a directly on your server. It includes links to install the app automatically on the device or download the IPA file.
Expand All @@ -128,7 +144,7 @@ You will to configure betabuilder to use the `web` deployment strategy with some
web.remote_host = "myserver.com"
web.remote_directory = "/remote/path/to/deployment/directory"
end

The `deploy_to` setting specifies the URL that your app will be published to. The `remote_host` setting is the SSH host that will be used to copy the files to your server using SCP. Finally, the `remote_directory` setting is the path to the location to your server that files will be uploaded to. You will need to configure any virtual hosts on your server to make this work.

## License
Expand Down
4 changes: 2 additions & 2 deletions lib/beta_builder/deployment_strategies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ def prepare
private

def self.strategies
{:web => Web, :testflight => TestFlight}
{:web => Web, :testflight => TestFlight, :hockeyapp => HockeyApp}
end
end
end

require 'beta_builder/deployment_strategies/web'
require 'beta_builder/deployment_strategies/testflight'

require 'beta_builder/deployment_strategies/hockeyapp'
118 changes: 118 additions & 0 deletions lib/beta_builder/deployment_strategies/hockeyapp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require 'rest_client'
require 'json'
require 'tmpdir'
require 'fileutils'

# # Deployment strategy for [HockeyApp](http://hockeyapp.net)
#
# Example API upload:
#
# curl \
# -F "status=2" \
# -F "notify=1" \
# -F "notes=Some new features and fixed bugs." \
# -F "notes_type=0" \
# -F "ipa=@hockeyapp.ipa" \
# -F "dsym=@hockeyapp.dSYM.zip" \
# -H "X-HockeyAppToken: 4567abcd8901ef234567abcd8901ef23" \
# https://rink.hockeyapp.net/api/2/apps/1234567890abcdef1234567890abcdef/app_versions
#
module BetaBuilder
module DeploymentStrategies
class HockeyApp < Strategy

def endpoint(app_id)
"https://rink.hockeyapp.net/api/2/apps/#{app_id}/app_versions"
end

def extended_configuration_for_strategy
proc do
def generate_release_notes(&block)
self.release_notes = block if block
end
end
end

# 1: Don't allow users to download or install the version
# 2: Available for download or installation
def status_flag(allow_download = false)
allow_download ? 2 : 1
end

# 0 - Don't notify testers
# 1 - Notify all testers that can install this app
def notify_flag(notify = false)
notify ? 1 : 0
end

def deploy
release_notes = get_notes
payload = {
:status => status_flag(@configuration.allow_download)
:ipa => File.new(@configuration.ipa_path, 'rb'),
:notes => release_notes,
:notify => notify_flag(@configuration.notify),
}
api_token = @configuration.api_token

puts "Uploading build to Hockey App..."
if @configuration.verbose
puts "ipa path: #{@configuration.ipa_path}"
puts "release notes: #{release_notes}"
puts payload.inspect
end

if @configuration.dry_run
puts '** Dry Run - No action here! **'
puts payload.inspect
return
end

begin
response = RestClient.post(endpoint(@configuration.app_id), payload, {:accept => :json, "X-HockeyAppToken" => api_token})
rescue => e
response = e.response
end

if (response.code == 201) || (response.code == 200)
puts "Upload complete."
else
puts "Upload failed. (#{response})"
end
end

private

def get_notes
notes = @configuration.release_notes_text
notes || get_notes_using_editor || get_notes_using_prompt
end

def get_notes_using_editor
return unless (editor = ENV["EDITOR"])

dir = Dir.mktmpdir
begin
filepath = "#{dir}/release_notes"
system("#{editor} #{filepath}")
@configuration.release_notes = File.read(filepath)
ensure
rm_rf(dir)
end
end

def get_notes_using_prompt
puts "Enter the release notes for this build (hit enter twice when done):\n"
@configuration.release_notes = gets_until_match(/\n{2}$/).strip
end

def gets_until_match(pattern, string = "")
if (string += STDIN.gets) =~ pattern
string
else
gets_until_match(pattern, string)
end
end
end
end
end