-
-
Save dblandin/9650678 to your computer and use it in GitHub Desktop.
RubyMotion Configuration and Deploy/Release
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AppProperties | |
COMPANY = 'name' | |
NAME = 'name' | |
VERSION = '1.0.0' | |
def company | |
COMPANY | |
end | |
def name | |
NAME | |
end | |
def version | |
VERSION | |
end | |
def seed_id | |
ENV['APPLE_SEED_ID'] | |
end | |
def dev_version | |
[version, short_git_hash].join('.') | |
end | |
def short_git_hash | |
`git log --pretty=format:'%h' -n 1` | |
end | |
def frameworks | |
%w[Accelerate AVFoundation CFNetwork CoreData CoreGraphics | |
CoreImage CoreMedia CoreVideo CoreText CoreTelephony | |
MobileCoreServices AdSupport Foundation MessageUI | |
OpenGLES QuartzCore Security StoreKit SystemConfiguration | |
UIKit AssetsLibrary] | |
end | |
def libraries | |
%w[/usr/lib/libsqlite3.0.dylib /usr/lib/libz.1.2.5.dylib | |
/usr/lib/libxml2.dylib /usr/lib/libicucore.dylib] | |
end | |
def dev_certificate | |
ENV['DEVELOPMENT_CERTIFICATE_NAME'] | |
end | |
def dev_profile_path | |
ENV['DEVELOPMENT_PROVISIONING_PROFILE_PATH'] | |
end | |
def adhoc_certificate | |
ENV['ADHOC_CERTIFICATE_NAME'] | |
end | |
def adhoc_profile_path | |
ENV['ADHOC_PROVISIONING_PROFILE_PATH'] | |
end | |
def release_certificate | |
ENV['RELEASE_CERTIFICATE_NAME'] | |
end | |
def release_profile_path | |
ENV['RELEASE_PROVISIONING_PROFILE_PATH'] | |
end | |
def dev_entitlements | |
entitlements = { | |
'aps-environment' => 'development', | |
'get-task-allow' => true | |
} | |
unless ENV['TRAVIS'] | |
entitlements = entitlements.merge('keychain-access-groups' => [keychain_access_group]) | |
end | |
entitlements | |
end | |
def adhoc_entitlements | |
release_entitlements | |
end | |
def release_entitlements | |
{ 'aps-environment' => 'production', | |
'get-task-allow' => false, | |
'keychain-access-groups' => [keychain_access_group] } | |
end | |
def keychain_access_group | |
[seed_id, identifier].join('.') | |
end | |
def identifier | |
[company, name].join('.') | |
end | |
def sdk_version | |
'7.1' | |
end | |
def deployment_target | |
'7.0' | |
end | |
def prerendered_icon | |
true | |
end | |
def devices | |
%i[iphone] | |
end | |
def orientations | |
%i[portrait] | |
end | |
def info_plist | |
{ | |
'CFBundleDevelopmentRegion' => 'en', | |
'CFBundleURLTypes' => [url_scheme], | |
'UIAppFonts' => fonts, | |
'UILaunchImageFile' => 'Default', | |
'UIStatusBarHidden' => true, | |
'UIViewControllerBasedStatusBarAppearance' => false | |
} | |
end | |
def fonts | |
%w[helveticaneue-light.otf icons.ttf] | |
end | |
def url_scheme | |
{ 'CFBundleURLName' => identifier, | |
'CFBundleURLSchemes' => [name] } | |
end | |
def icons | |
Dir.glob('resources/Icon*.png').map{ |icon| icon.split('/').last } | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'net/http' | |
require 'net/http/post/multipart' | |
namespace 'crittercism' do | |
desc 'Submit .dSYM to Crittercism' | |
task upload: ['deploy:zip_dsym'] do | |
p 'Submitting to Crittercism...' | |
crittercism = App.config.crittercism | |
App.fail 'A value for app.crittercism.app_id is mandatory' unless crittercism.app_id | |
App.fail 'A value for app.crittercism.api_key is mandatory' unless crittercism.api_key | |
base_url = 'https://app.crittercism.com/api_beta' | |
endpoint_url = "#{base_url}/dsym/#{crittercism.app_id}" | |
app_dsym_zip = "#{App.config.app_bundle('iPhoneOS').sub(/\.app$/, '.dSYM')}.zip" | |
uri = URI.parse(endpoint_url) | |
File.open(app_dsym_zip) do |dsym| | |
http = Net::HTTP.new(uri.host, uri.port) | |
http.use_ssl = true | |
request = Net::HTTP::Post::Multipart.new(uri.path, | |
'dsym' => UploadIO.new(dsym, 'application/zip', 'dsym.zip'), | |
'key' => crittercism.api_key | |
) | |
response = http.request(request) | |
p "Crittercism: #{response.code} #{response.message}" | |
p response.body unless /^2\d+/.match("#{response.code}") | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# usage: rake deploy [notes="Some notes"] [replace="True|False"] [notify="True|False"] [quiet=1|true|"anything"] | |
# rake release | |
desc 'Deploy to TestFlight/Crittercism' | |
task :deploy do | |
['slack:starting', 'deploy:archive', 'testflight:upload', 'crittercism:upload', 'slack:finished'].each do |task| | |
Rake::Task[task].invoke | |
raise "#{task} failed!" if $?.exitstatus.nonzero? | |
end | |
end | |
desc 'Release to App Store' | |
task :release do | |
['deploy:archive:distribution', 'crittercism:upload'].each do |task| | |
Rake::Task[task].invoke | |
raise "#{task} failed!" if $?.exitstatus.nonzero? | |
end | |
end | |
namespace :deploy do | |
desc 'Update release notes' | |
task :notes do | |
file = File.new('notes', 'a+') | |
system("$EDITOR #{file.path}") | |
end | |
desc 'Prepare archive' | |
task :archive do | |
p 'Preparing archive...' | |
App.config_without_setup.deploy_mode = true | |
Rake::Task['archive'].invoke | |
end | |
namespace :archive do | |
desc 'Prepare release archive' | |
task :distribution do | |
p 'Preparing release archive...' | |
App.config_without_setup.deploy_mode = true | |
Rake::Task['archive:distribution'].invoke | |
end | |
end | |
desc 'Zip .dSYM file' | |
task :zip_dsym do | |
p 'Zipping .dSYM file...' | |
App.config_without_setup.deploy_mode = true | |
app_dsym = App.config.app_bundle('iPhoneOS').sub(/\.app$/, '.dSYM') | |
app_dsym_zip = app_dsym + '.zip' | |
if !File.exist?(app_dsym_zip) or File.mtime(app_dsym) > File.mtime(app_dsym_zip) | |
Dir.chdir(File.dirname(app_dsym)) do | |
sh "/usr/bin/zip -q -r '#{File.basename(app_dsym)}.zip' '#{File.basename(app_dsym)}'" | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TestFlightConfig | |
attr_accessor :api_token, :team_token, :app_token, :distribution_lists | |
end | |
class CrittercismConfig | |
attr_accessor :app_id, :api_key | |
end | |
module Motion; module Project; class Config | |
attr_accessor :deploy_mode | |
variable :testflight, :crittercism | |
def testflight | |
yield testflight_config if block_given? && deploy? | |
testflight_config | |
end | |
def crittercism | |
yield crittercism_config if block_given? && deploy? | |
crittercism_config | |
end | |
def testflight_config | |
@testflight_config ||= TestFlightConfig.new | |
end | |
def crittercism_config | |
@crittercism_config ||= CrittercismConfig.new | |
end | |
def deploy? | |
!!@deploy_mode | |
end | |
end; end; end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
$:.unshift('/Library/RubyMotion/lib') | |
require 'motion/project/template/ios' | |
require 'bundler' | |
Dir.glob('./config/*.rb').each { |file| require file } | |
if ARGV.join(' ') =~ /spec/ | |
Bundler.require :default, :development, :spec | |
elsif ARGV.join(' ') =~ /testflight/ | |
Bundler.require :default, :development | |
elsif ARGV.join(' ') =~ /archive/ | |
Bundler.require :default | |
else | |
Bundler.require :default, :development | |
require 'sugarcube' | |
require 'sugarcube-repl' | |
end | |
Dir.glob('lib/tasks/*.rake').each { |file| import file } | |
environment_variables = Dotenv.load | |
Motion::Require.all(Dir.glob('lib/**/*.rb') + Dir.glob('app/**/*.rb')) | |
Motion::Project::App.setup do |app| | |
properties = AppProperties.new | |
app.detect_dependencies = true | |
app.name = properties.name | |
app.seed_id = properties.seed_id | |
app.identifier = properties.identifier | |
app.short_version = properties.version | |
app.sdk_version = properties.sdk_version | |
app.deployment_target = properties.deployment_target | |
app.interface_orientations = properties.orientations | |
app.testflight do |config| | |
config.api_token = ENV['TESTFLIGHT_API_TOKEN'] | |
config.team_token = ENV['TESTFLIGHT_TEAM_TOKEN'] | |
config.app_token = ENV['TESTFLIGHT_APP_TOKEN'] | |
config.distribution_lists = %w[app internal] | |
end | |
app.development do | |
app.version = properties.dev_version | |
if app.deploy? | |
p 'Setting testflight options...' | |
app.info_plist['ENV_DEPLOY'] = 'True' | |
app.codesign_certificate = properties.adhoc_certificate | |
app.provisioning_profile = properties.adhoc_profile_path | |
app.crittercism do |config| | |
config.app_id = ENV['DEVELOPMENT_CRITTERCISM_APP_ID'] | |
config.api_key = ENV['DEVELOPMENT_CRITTERCISM_API_KEY'] | |
end | |
properties.adhoc_entitlements.each { |key, value| app.entitlements[key] = value } | |
else | |
p 'Setting development options...' | |
app.codesign_certificate = properties.dev_certificate | |
app.provisioning_profile = properties.dev_profile_path | |
properties.dev_entitlements.each { |key, value| app.entitlements[key] = value } | |
end | |
end | |
app.release do | |
p 'Setting release options...' | |
app.version = properties.version | |
app.codesign_certificate = properties.release_certificate | |
app.provisioning_profile = properties.release_profile_path | |
app.crittercism do |config| | |
config.app_id = ENV['PRODUCTION_CRITTERCISM_APP_ID'] | |
config.api_key = ENV['PRODUCTION_CRITTERCISM_API_KEY'] | |
end | |
properties.release_entitlements.each { |key, value| app.entitlements[key] = value } | |
end | |
app.icons = properties.icons | |
app.prerendered_icon = properties.prerendered_icon | |
app.archs['iPhoneOS'] = %w[armv7] | |
app.libs = properties.libraries | |
app.frameworks = properties.frameworks | |
# Extra plist | |
properties.info_plist.each { |key, value| app.info_plist[key] = value } | |
environment_variables.each { |key, value| app.info_plist["ENV_#{key}"] = value } | |
app.pods do | |
# Logging | |
pod 'CocoaLumberjack', '~> 1.8.1' | |
# Networking | |
pod 'AFNetworking', '~> 1.3.3' | |
pod 'Reachability', '~> 3.1.1' | |
pod 'RestKit', '~> 0.22.0' | |
pod 'SDWebImage', '~> 3.5.4' | |
# Analytics / Crash Reporting | |
pod 'CrittercismSDK', '~> 4.3.1' | |
# Other | |
pod 'MapBox', '~> 1.1.0' | |
pod 'SZTextView', '~> 1.1.1' | |
pod 'SSKeychain', '~> 1.2.1' | |
pod 'TSMessages', '~> 0.9.6' | |
pod 'MultiDelegate', '~> 0.0.2' | |
pod 'UIImage-Resize', '~> 1.0.1' | |
pod 'GHMarkdownParser', '~> 0.0.1' | |
pod 'NSDate+TimeAgo', git: '[email protected]:kevinlawler/NSDate-TimeAgo.git', :tag => 'v1.0.3' | |
pod 'JazzHands', '~> 0.0.4' | |
pod 'Slash', '~> 0.1.2' | |
pod 'Harpy', '~> 2.5.2' | |
end | |
app.vendor_project 'vendor/lumberjack_patch', :static | |
app.vendor_project 'vendor/OpenInChrome', :static | |
app.vendor_project 'vendor/DACircularProgress', :static | |
app.vendor_project 'vendor/Pods/CrittercismSDK/CrittercismSDK', :static | |
app.vendor_project 'vendor/Pods/MapBox/Proj4', :static, products: %w[libProj4.a] | |
end | |
Motion::SettingsBundle.setup do |app| | |
properties = AppProperties.new | |
app.toggle 'Awesomeness', key: 'settings-awesomeness', default: true | |
app.toggle 'Coolness', key: 'settings-coolness', default: true | |
app.title 'app', key: 'settings-version', default: properties.version | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'json' | |
require 'net/http' | |
namespace :slack do | |
@application = ENV['APP_NAME'] | |
@branch = `git rev-parse --abbrev-ref HEAD`.chomp | |
@deployer = `git config user.name`.chomp | |
@git_sha = `git rev-parse --short HEAD`.chomp | |
@repo_url = ENV['GIT_REPO_URL'] | |
def post_to_slack(payload) | |
return if ENV['quiet'] | |
slack_subdomain = ENV['SLACK_SUBDOMAIN'] | |
slack_token = ENV['SLACK_WEBHOOKS_TOKEN'] | |
uri = URI.parse("https://#{slack_subdomain}.slack.com/services/hooks/incoming-webhook?token=#{slack_token}") | |
http = Net::HTTP.new(uri.host, uri.port) | |
http.use_ssl = true | |
request = Net::HTTP::Post.new(uri.request_uri) | |
request.set_form_data(payload: payload.merge(default_payload).to_json) | |
response = http.request(request) | |
p "Slack: #{response.code} #{response.message}" | |
p response.body unless /^2\d+/.match("#{response.code}") | |
end | |
def default_payload | |
{ channel: '#general', username: 'deploybot', icon_emoji: ':rocket:' } | |
end | |
def release_notes | |
{ fallback: 'Releases Notes', fields: [{ title: 'Release Notes', value: notes }] } | |
end | |
def notes | |
@notes ||= File.read('notes') if File.exists?('notes') | |
end | |
def sha_link | |
"#{@repo_url}/commit/#{@git_sha}" | |
end | |
def branch_link | |
"#{@repo_url}/tree/#{@branch}" | |
end | |
desc 'Begin deploy notifications to Slack' | |
task starting: ['deploy:notes'] do | |
p "Sending Slack 'Deploying' notification..." | |
payload = { text: "#{@deployer} is deploying #{@application} (<#{branch_link}|#{@branch}> / <#{sha_link}|#{@git_sha}>) to TestFlight" } | |
post_to_slack(payload) | |
@start_time = Time.now | |
end | |
task :finished do | |
p "Sending Slack 'Deployed' notification..." | |
elapsed_time = Time.now.to_i - @start_time.to_i | |
payload = { text: "#{@deployer} deployed #{@application} successfully in #{elapsed_time} seconds." } | |
payload.merge!(attachments: [release_notes]) if notes | |
post_to_slack(payload) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'net/http' | |
require 'net/http/post/multipart' | |
namespace :testflight do | |
desc 'Submit archive to TestFlight' | |
task upload: ['deploy:notes', 'deploy:zip_dsym'] do | |
p 'Submitting to TestFlight...' | |
testflight = App.config.testflight | |
App.fail 'A value for app.testflight.api_token is mandatory' unless testflight.api_token | |
App.fail 'A value for app.testflight.team_token is mandatory' unless testflight.team_token | |
distribution_lists = (testflight.distribution_lists ? testflight.distribution_lists.join(',') : nil) | |
notes = File.read('notes') if File.exists?('notes') | |
notify = ENV['notify'] || 'False' | |
replace = ENV['replace'] || 'False' | |
App.fail 'Submission notes must be provided.' if notes.nil? || notes.empty? | |
endpoint_url = 'https://testflightapp.com/api/builds.json' | |
app_dsym_zip = "#{App.config.app_bundle('iPhoneOS').sub(/\.app$/, '.dSYM')}.zip" | |
archive = App.config.archive | |
uri = URI.parse(endpoint_url) | |
File.open(app_dsym_zip) do |dsym| | |
http = Net::HTTP.new(uri.host, uri.port) | |
http.use_ssl = true | |
request = Net::HTTP::Post::Multipart.new(uri.path, | |
'file' => UploadIO.new(archive, 'application/octet-stream', 'archive.ipa'), | |
'dsym' => UploadIO.new(dsym, 'application/zip', 'dsym.zip'), | |
'api_token' => testflight.api_token, | |
'team_token' => testflight.team_token, | |
'notes' => notes, | |
'notify' => notify, | |
'replace' => replace, | |
'distribution_lists' => distribution_lists | |
) | |
response = http.request(request) | |
p "TestFlight: #{response.code} #{response.message}" | |
p response.body unless /^2\d+/.match("#{response.code}") | |
end | |
end | |
end |
This is great! I just created one for New Relic (which I will share once I test it).
However I have the same question as @pbraswell, where do we put these files?
@dblandin what is the folder structure for this awesome util
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This looks like it will definitely work for what I want, but I'm a little unsure of exactly how all this gets assembled. The Rake file obviously replaces the one generated by the 'motion' command to start a new project. Does the rest go in /lib? What environmental variables need to be set and how is that best done for a ruby in motion project?
TIA!,
Peter