deliver •
snapshot •
frameit •
PEM •
sigh •
produce •
cert •
codes
-------
spaceship
============
[![Twitter: @KauseFx](https://img.shields.io/badge/contact-@KrauseFx-blue.svg?style=flat)](https://twitter.com/KrauseFx)
[![License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/KrauseFx/spaceship/blob/master/LICENSE)
[![Coverage Status](https://coveralls.io/repos/KrauseFx/spaceship/badge.svg?branch=master&t=ldL8gg)](https://coveralls.io/r/KrauseFx/spaceship?branch=master)
[![Gem](https://img.shields.io/gem/v/spaceship.svg?style=flat)](http://rubygems.org/gems/spaceship)
[![Codeship Status for KrauseFx/spaceship](https://img.shields.io/codeship/96bb1040-c2b6-0132-4c5b-22f8b41c2618/master.svg)](https://codeship.com/projects/73801)
Get in contact with the developers on Twitter: [@snatchev](https://twitter.com/snatchev/) and [@KrauseFx](https://twitter.com/KrauseFx)
-------
Why? •
Usage •
Installation •
Technical Details •
Need help?
-------
spaceship
is part of fastlane: connect all deployment tools into one streamlined workflow.
##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx)
# What's Spaceship?
> `spaceship` is named after the new Apple HQ in Cupertino. It allows you to seamlessly interact with the Apple Developer Portal using very simple Ruby code.
`spaceship` uses simple HTTPs requests without any sort of web scraping.
Using `spaceship`, the execution time of [sigh](https://github.com/KrauseFx/sigh) was reduced from over 1 minute to less than 5 seconds :rocket:
`spaceship` uses a combination of 3 different API endpoints, used by the Apple Developer Portal and Xcode. As no API offers everything we need, `spaceship` combines all APIs for you. [More details about the APIs](#technical-details).
Up until now, the [fastlane tools](https://fastlane.tools) used web scraping to interact with Apple's web services. By upgrading them to use `spaceship` all [fastlane tools](https://fastlane.tools) will be even faster and much more stable.
> No matter how many apps or profiles you have, `spaceship` **can** handle your scale.
Enough words, here is some code:
```ruby
Spaceship.login
# Create a new app
app = Spaceship.app.create!(bundle_id: "com.krausefx.app", name: "Spaceship App")
# Use an existing certificate
cert = Spaceship.certificate.production.all.first
# Create a new provisioning profile
profile = Spaceship.provisioning_profile.app_store.create!(bundle_id: app.bundle_id,
certificate: cert)
# Print the name and download the new profile
puts "Created Profile " + profile.name
profile.download
```
## Speed
How fast are tools using `spaceship` compared to web scraping?
![assets/SpaceshipRecording.gif](assets/SpaceshipRecording.gif)
# Installation
sudo gem install spaceship
# Usage
To quickly play around with `spaceship` launch `irb` in your terminal and execute `require "spaceship"`.
## Login
```ruby
Spaceship.login("felix@krausefx.com", "password")
Spaceship.select_team # call this method to let the user select a team
```
## Apps
```ruby
# Fetch all available apps
all_apps = Spaceship.app.all
# Find a specific app based on the bundle identifier
app = Spaceship.app.find("com.krausefx.app")
# Show the names of all your apps
Spaceship.app.all.each do |app|
puts app.name
end
# Create a new app
app = Spaceship.app.create!(bundle_id: "com.krausefx.app_name", name: "fastlane App")
```
## Certificates
```ruby
# Fetch all available certificates (includes signing and push profiles)
certificates = Spaceship.certificate.all
```
### Code Signing Certificates
```ruby
# Production identities
prod_certs = Spaceship.certificate.production.all
# Development identities
dev_certs = Spaceship.certificate.development.all
# Download a certificate
cert_content = prod_certs.first.download
```
### Push Certificates
```ruby
# Production push profiles
prod_push_certs = Spaceship.certificate.production_push.all
# Development push profiles
dev_push_certs = Spaceship.certificate.development_push.all
# Download a push profile
cert_content = dev_push_certs.first.download
```
### Create a Certificate
```ruby
# Create a new certificate signing request
csr, pkey = Spaceship.certificate.create_certificate_signing_request
# Use the signing request to create a new distribution certificate
Spaceship.certificate.production.create!(csr: csr)
# Use the signing request to create a new push certificate
Spaceship.certificate.production_push.create!(csr: csr, bundle_id: "com.krausefx.app")
```
## Provisioning Profiles
### Receiving profiles
```ruby
##### Finding #####
# Get all available provisioning profiles
profiles = Spaceship.provisioning_profile.all
# Get all App Store profiles
profiles_appstore = Spaceship.provisioning_profile.app_store.all
# Get all AdHoc profiles
profiles_adhoc = Spaceship.provisioning_profile.ad_hoc.all
# Get all Development profiles
profiles_dev = Spaceship.provisioning_profile.development.all
# Fetch all profiles for a specific app identifier for the App Store
filtered_profiles = Spaceship.provisioning_profile.app_store.find_by_bundle_id("com.krausefx.app")
##### Downloading #####
# Download a profile
profile_content = profiles.first.download
# Download a specific profile as file
my_profile = Spaceship.provisioning_profile.app_store.find_by_bundle_id("com.krausefx.app")
File.write("output.mobileprovision", my_profile.download)
```
### Create a Provisioning Profile
```ruby
# Choose the certificate to use
cert = Spaceship.certificate.production.all.first
# Create a new provisioning profile with a default name
# The name of the new profile is "com.krausefx.app AppStore"
profile = Spaceship.provisioning_profile.app_store.create!(bundle_id: "com.krausefx.app",
certificate: cert)
# AdHoc Profiles will add all devices by default
profile = Spaceship.provisioning_profile.ad_hoc.create!(bundle_id: "com.krausefx.app",
certificate: cert,
name: "Profile Name")
# Store the new profile on the filesystem
File.write("NewProfile.mobileprovision", profile.download)
```
### Repair a broken provisioning profile
```ruby
# Select all 'Invalid' or 'Expired' provisioning profiles
broken_profiles = Spaceship.provisioning_profile.all.find_all do |profile|
(profile.status == "Invalid" or profile.status == "Expired")
end
# Iterate over all broken profiles and repair them
broken_profiles.each do |profile|
profile.repair! # yes, that's all you need to repair a profile
end
# or to make the same thing, just more Ruby like:
Spaceship.provisioning_profile.all.find_all { |p| %w[Invalid Expired].include?p.status}.map(&:repair!)
```
## Devices
```ruby
all_devices = Spaceship.device.all
# Register a new device
Spaceship.device.create!(name: "Private iPhone 6", udid: "5814abb3...")
```
## Enterprise
```ruby
# Use the InHouse class to get all enterprise certificates
cert = Spaceship.certificate.in_house.all.first
# Create a new InHouse Enterprise distribution profile
profile = Spaceship.provisioning_profile.app_store.create!(bundle_id: "com.krausefx.*",
certificate: cert)
```
## Multiple Spaceships
Sometimes one `spaceship` just isn't enough. That's why this library has its own Spaceship Launcher to launch and use multiple `spaceships` at the same time :rocket:
```ruby
# Launch 2 spaceships
spaceship1 = Spaceship::Launcher.new("felix@krausefx.com", "password")
spaceship2 = Spaceship::Launcher.new("stefan@spaceship.airforce", "password")
# Fetch all registered devices from spaceship1
devices = spaceship1.device.all
# Iterate over the list of available devices
# and register each device from the first account also on the second one
devices.each do |device|
spaceship2.device.create!(name: device.name, udid: device.udid)
end
```
## More cool things you can do
```ruby
# Find a profile with a specific name
profile = Spaceship.provisioning_profile.development.all.find { |p| p.name == "Name" }
# Add all available devices to the profile
profile.devices = Spaceship.device.all
# Push the changes back to the Apple Developer Portal
profile.update!
# Get the currently used team_id
Spaceship.client.team_id
# We generally don't want to be destructive, but you can also delete things
# This method might fail for various reasons, e.g. app is already in the store
app = Spaceship.app.find("com.krausefx.app")
app.delete!
```
## Spaceship in use
The beta version of [sigh](https://github.com/KrauseFx/sigh) is already using `spaceship` to communicate with Apple's web services. You can see all relevant source code in [runner.rb](https://github.com/KrauseFx/sigh/blob/feature/spaceship/lib/sigh/spaceship/runner.rb).
## Full Documentation
The detailed documentation of all available classes is available on [RubyDoc](http://www.rubydoc.info/github/fastlane/spaceship/frames).
## Example Data
Some unnecessary information was removed, check out [provisioning_profile.rb](https://github.com/KrauseFx/spaceship/blob/master/lib/spaceship/provisioning_profile.rb) for all available attributes.
The example data below is a provisioning profile, containing a device, certificate and app.
```
#, ...],
@certificates=[
#
],
@id="72SRVUNAAA",
@uuid="43cda0d6-04a5-4964-89c0-a24b5f258aaa",
@expires=#,
@distribution_method="adhoc",
@name="com.krausefx.app AppStore",
@status="Active",
@platform="ios",
@app=#
>
>
```
# Technical Details
## HTTP Client
Up until now all [fastlane tools](https://fastlane.tools) used web scraping to interact with Apple's web services. `spaceship` uses a simple HTTP client only, resulting in much less overhead and extremely improved speed.
Advantages of `spaceship` (HTTP client) over web scraping:
- Blazing fast :rocket: 90% faster than previous methods
- No more overhead by loading images, HTML, JS and CSS files on each page load
- Great test coverage by stubbing server responses
- Resistant against design changes of the Apple Developer Portal
- Automatic re-trying of requests in case a timeout occurs
- By stubbing the `spaceship` objects it is possible to also implement tests for tools like [sigh](https://github.com/KrauseFx/sigh)
## API Endpoints
I won't go into too much technical details about the various API endpoints, but just to give you an idea:
- `https://idmsa.apple.com`: Used to authenticate to get a valid session
- `https://developerservices2.apple.com`:
- Get a detailed list of all available provisioning profiles
- This API returns the devices, certificates and app for each of the profiles
- Register new devices
- `https://developer.apple.com`:
- List all devices, certificates and apps
- Create new certificates, provisioning profiles and apps
- Delete certificates and apps
- Repair provisioning profiles
- Download provisioning profiles
- Team selection
`spaceship` uses all those API points to offer this seamless experience.
## Magic involved
`spaceship` does a lot of magic to get everything working so neatly:
- **Sensible Defaults**: You only have to provide the mandatory information (e.g. new provisioning profiles contain all devices by default)
- **Local Validation**: When pushing changes back to the Apple Dev Portal `spaceship` will make sure only valid data is sent to Apple (e.g. automatic repairing of provisioning profiles)
- **Various request/response types**: When working with the different API endpoints, `spaceship` has to deal with `JSON`, `XML`, `txt`, `plist` and sometimes even `HTML` responses and requests.
- **Automatic Pagination**: Even if you have thousands of apps, profiles or certificates, `spaceship` **can** handle your scale. It was heavily tested by first using `spaceship` to create hundreds of profiles and then accessing them using `spaceship`.
- **Session, Cookie and CSRF token**: All the security aspects are handled by `spaceship`.
- **Profile Magic**: Create and upload code signing requests, all managed by `spaceship`
- **Multiple Spaceship**: You can launch multiple `spaceships` with different Apple accounts to do things like syncing the registered devices.
# Credits
This project has been sponsored by [ZeroPush](https://zeropush.com). `spaceship` was developed by [@snatchev](https://twitter.com/snatchev/) and [@KrauseFx](https://twitter.com/KrauseFx).
# License
This project is licensed under the terms of the MIT license. See the LICENSE file.
# Contributing
1. Create an issue to start a discussion about your idea
2. Fork it (https://github.com/KrauseFx/fastlane/fork)
3. Create your feature branch (`git checkout -b my-new-feature`)
4. Commit your changes (`git commit -am 'Add some feature'`)
5. Push to the branch (`git push origin my-new-feature`)
6. Create a new Pull Request