apple_receipt 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a381d5ac7037e9d434c0e0af717672f32d84f157b1086753aaf93e01951a2b42
4
- data.tar.gz: f2722e6a2b589703f451035b4a34c2aa97aedbaa469e362443448c50d7bfcb3c
3
+ metadata.gz: f3234acf1d422b851c6e997c905ee974df3be03d446431d894498bf1f0e7e61e
4
+ data.tar.gz: db024ff11f3ae9a78a98275c9feb9f7fef8d536949a895866ad760a16d3eb185
5
5
  SHA512:
6
- metadata.gz: e4f99e107fd8623e73bcc07ce0e095f6c1c44c345207355f915d0e49d31957325fb838869b5d4176907e23307901431be6d28e9ead4b7c81fde6003ee6bad39d
7
- data.tar.gz: 5be1092bdcc3577e4d581284d6c998c8293e02aab9632fea8dee360c9c6b442e4bf6bcb7be1852c0a83534c86a97399ffac97797f0e312e4ec6cab0db6f87f57
6
+ metadata.gz: b7fdcd0d79dee2feedc4c95e8aebd124b58790fc0e12c916fe0a2f948db329eb4a859f299f09507019ee49e8f76809f0e630987d17cfac1267989f0765223cee
7
+ data.tar.gz: 551a6353876874102e79864b16dca85487054ed6d975a927b2b9ed69bbe22b5228e8aae820c5da249d8627a470e90694dd5ab7e0020bebc7a80f44525ad520f6
@@ -1,2 +1,6 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.5
3
+
4
+ Metrics/BlockLength:
5
+ Exclude:
6
+ - spec/**/*
@@ -3,3 +3,5 @@ language: ruby
3
3
  rvm:
4
4
  - 2.5.0
5
5
  before_install: gem install bundler -v 1.16.1
6
+ notifications:
7
+ email: false
@@ -0,0 +1,7 @@
1
+ # Apple Receipt changelog
2
+
3
+ ## v0.2.0
4
+
5
+ - Added ISC license [\#5](https://github.com/koenrh/apple_receipt/pull/5)
6
+ - Added missing dependencies ([\#3](https://github.com/koenrh/apple_receipt/pull/3) and [\#4](https://github.com/koenrh/apple_receipt/pull/4))
7
+ - Added support for 'version 2' receipts ([\#1](https://github.com/koenrh/apple_receipt/pull/1))
@@ -23,7 +23,7 @@ include:
23
23
  Examples of unacceptable behavior by participants include:
24
24
 
25
25
  * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
26
+ advances
27
27
  * Trolling, insulting/derogatory comments, and personal or political attacks
28
28
  * Public or private harassment
29
29
  * Publishing others' private information, such as a physical or electronic
@@ -67,8 +67,5 @@ members of the project's leadership.
67
67
 
68
68
  ## Attribution
69
69
 
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/
70
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/),
71
+ [version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html).
@@ -1,21 +1,15 @@
1
- The MIT License (MIT)
1
+ ISC License
2
2
 
3
- Copyright (c) 2018 Koen Rouwhorst
3
+ Copyright (c) 2018, Koen Rouwhorst
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose
6
+ with or without fee is hereby granted, provided that the above copyright notice
7
+ and this permission notice appear in all copies.
11
8
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
13
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
14
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
15
+ THIS SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,15 @@
1
- # AppleReceipt
1
+ # Apple Receipt
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/apple_receipt.svg)](https://badge.fury.io/rb/apple_receipt)
3
4
  [![Build Status](https://travis-ci.org/koenrh/apple_receipt.svg?branch=master)](https://travis-ci.org/koenrh/apple_receipt)
5
+ [![Dependency Status](https://beta.gemnasium.com/badges/github.com/koenrh/apple_receipt.svg)](https://beta.gemnasium.com/projects/github.com/koenrh/apple_receipt)
4
6
 
5
- This gem allows you to to locally/cryptographically verify Apple receipts.
7
+ This gem allows you to read and verify Apple receipts. It was originally built
8
+ to locally (server-side) verify the validity of receipts that are embedded in
9
+ [Status Update Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html#//apple_ref/doc/uid/TP40008267-CH7-SW13).
10
+ These receipts have a different format than [documented](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW2)
11
+ App Store receipts you might be familiar with, which are [PKCS #7](https://tools.ietf.org/html/rfc2315)
12
+ containers with a payload (receipt data) encoded using [ASN.1](https://www.itu.int/itu-t/recommendations/rec.aspx?rec=X.690).
6
13
 
7
14
  ## Installation
8
15
 
@@ -25,39 +32,74 @@ Or install it yourself as:
25
32
  ```ruby
26
33
  require 'apple_receipt'
27
34
 
28
- # check validity (certificate chain, and signature)
35
+ # Check receipt's validity (certificate chain, and signature)
29
36
  receipt_raw = File.read('./receipt.txt')
30
37
  receipt = AppleReceipt::Receipt.new(receipt_raw)
31
38
  receipt.valid?
32
39
  # => true
33
40
 
34
- # read purchase info
41
+ # Read receipt's data (data in example shortened for brevity)
35
42
  receipt.purchase_info
36
- # => {"original-purchase-date-pst"=>"2017-12-23 09:03:53 America/Los_Angeles",
37
- # "quantity"=>"1",
38
- # "unique-vendor-identifier"=>"D895D8DB-AEDF-4530-B7E5-E0C9A9A394B6",
39
- # "original-purchase-date-ms"=>"1514048633000",
40
- # "expires-date-formatted"=>"2018-01-23 17:03:44 Etc/GMT",
41
- # "is-in-intro-offer-period"=>"false",
42
- # "purchase-date-ms"=>"1514048624000",
43
- # "expires-date-formatted-pst"=>"2018-01-23 09:03:44 America/Los_Angeles",
44
- # "is-trial-period"=>"false",
45
- # "item-id"=>"1190360447",
46
- # "unique-identifier"=>"fed543dc24065fa2ab23ef08b0b44c0a0c9ed375",
47
- # "original-transaction-id"=>"160000408504141",
48
- # "expires-date"=>"1516727024000",
49
- # "app-item-id"=>"947936149",
50
- # "transaction-id"=>"160000408504141",
51
- # "bvrs"=>"7000",
52
- # "web-order-line-item-id"=>"160000091314729",
53
- # "version-external-identifier"=>"825366855",
54
- # "bid"=>"com.foo.bar",
55
- # "product-id"=>"com.foo.bar.monthly",
56
- # "purchase-date"=>"2017-12-23 17:03:44 Etc/GMT",
57
- # "purchase-date-pst"=>"2017-12-23 09:03:44 America/Los_Angeles",
58
- # "original-purchase-date"=>"2017-12-23 17:03:53 Etc/GMT"}
43
+ # => {
44
+ # "quantity"=>"1",
45
+ # "expires-date-formatted"=>"2018-01-23 17:03:44 Etc/GMT",
46
+ # "is-in-intro-offer-period"=>"false",
47
+ # "is-trial-period"=>"false",
48
+ # "item-id"=>"1190360447",
49
+ # "app-item-id"=>"947936149",
50
+ # "transaction-id"=>"160000408504141",
51
+ # "web-order-line-item-id"=>"160000011000001",
52
+ # "bid"=>"com.foo.bar",
53
+ # "product-id"=>"com.foo.bar.monthly",
54
+ # "purchase-date"=>"2017-12-23 17:03:44 Etc/GMT",
55
+ # "original-purchase-date"=>"2017-12-23 17:03:53 Etc/GMT"
56
+ # }
59
57
  ```
60
58
 
59
+ ## Apple receipts
60
+
61
+ A receipt is encoded as base64, and is formatted as a [NeXTSTEP](https://en.wikipedia.org/wiki/Property_list#NeXTSTEP)
62
+ dictionary:
63
+
64
+ ```
65
+ {
66
+ "signature" = "[base64-encoded signature]";
67
+ "purchase-info" = "[base64-encoded purchase data]";
68
+ "pod" = "[integer]";
69
+ "signing-status" = "0";
70
+ }
71
+ ```
72
+
73
+ ### Signature
74
+
75
+ The `signature` entry contains base64-encoded binary data, which has the following
76
+ layout:
77
+
78
+ - **1 byte** - Receipt version (e.g. version 3).
79
+ - **128 bytes** (version 2) or **256 bytes** (version 3) - Signature.
80
+ - **4 bytes** - Length (in number of bytes) of the certificate.
81
+ - **N bytes** - DER-encoded certificate.
82
+
83
+ The version 2 and 3 receipt certificates are signed, respectively, by:
84
+
85
+ - **Apple iTunes Store Certification Authority** (version 2)
86
+ - Serial: 26 (`0x1a`)
87
+ - Subject: `C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple iTunes Store Certification Authority`
88
+ - **Apple Worldwide Developer Relations Certification Authority** (version 3)
89
+ - Serial: 134752589830791184 (`0x1debcc4396da010`)
90
+ - Subject: `C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority`
91
+
92
+ Both certificates chain up to:
93
+
94
+ - **Apple Root CA**
95
+ - Serial: 2 (`0x2`)
96
+ - Subject: `C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple Root CA`
97
+
98
+ ### Purchase info
99
+
100
+ The `purchase-info` entry contains a base64-encoded NeXTSTEP dictionary that contains
101
+ the actual receipt data (purchase info).
102
+
61
103
  ## Contributing
62
104
 
63
105
  Bug reports and pull requests are welcome on [GitHub](https://github.com/koenrh/apple_receipt).
@@ -67,4 +109,4 @@ code of conduct.
67
109
 
68
110
  ## License
69
111
 
70
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
112
+ The gem is available as open source under the terms of the [ISC License](https://opensource.org/licenses/ISC).
@@ -5,6 +5,7 @@ lib = File.expand_path('../lib', __FILE__)
5
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
  require 'apple_receipt/version'
7
7
 
8
+ # rubocop:disable Metrics/BlockLength
8
9
  Gem::Specification.new do |spec|
9
10
  spec.name = 'apple_receipt'
10
11
  spec.version = AppleReceipt::VERSION
@@ -30,8 +31,13 @@ Gem::Specification.new do |spec|
30
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
32
  spec.require_paths = ['lib']
32
33
 
34
+ spec.add_dependency 'json', '~> 2.0'
35
+ spec.add_dependency 'openssl', '~> 2.0'
36
+
33
37
  spec.add_development_dependency 'bundler', '~> 1.16'
34
38
  spec.add_development_dependency 'minitest', '~> 5.0'
35
- spec.add_development_dependency 'rake', '~> 10.0'
39
+ spec.add_development_dependency 'mocha', '~> 1.3'
40
+ spec.add_development_dependency 'rake', '~> 12.0'
36
41
  spec.add_development_dependency 'rubocop', '~> 0.50'
37
42
  end
43
+ # rubocop:enable Metrics/BlockLength
@@ -11,17 +11,20 @@ module AppleReceipt
11
11
  class Receipt
12
12
  def initialize(raw_receipt)
13
13
  receipt_decoded = Base64.decode64(raw_receipt)
14
- @version, @signature, @cert, @data = ReceiptParser.parse(receipt_decoded)
14
+ @version,
15
+ @signature,
16
+ @certificate,
17
+ @data = ReceiptParser.parse(receipt_decoded)
15
18
  end
16
19
 
17
20
  def purchase_info
18
- NextStepParser.parse(data)
21
+ @purchase_info ||= NextStepParser.parse(data)
19
22
  end
20
23
 
21
24
  def valid?
22
25
  Validator.new(self).valid?
23
26
  end
24
27
 
25
- attr_reader :version, :signature, :cert, :data
28
+ attr_reader :version, :signature, :certificate, :data
26
29
  end
27
30
  end
@@ -3,6 +3,7 @@
3
3
  require 'base64'
4
4
  require 'openssl'
5
5
  require 'json'
6
+ require 'set'
6
7
 
7
8
  require 'apple_receipt/next_step_parser'
8
9
 
@@ -11,24 +12,38 @@ module AppleReceipt
11
12
  module ReceiptParser
12
13
  module_function
13
14
 
14
- def bla(input)
15
+ SIGNATURE_LENGTH_MAPPING = {
16
+ 2 => 128,
17
+ 3 => 256
18
+ }.freeze
19
+
20
+ def parse(input)
15
21
  receipt_hash = NextStepParser.parse(input)
22
+
23
+ unless Set['signature', 'purchase-info'].subset?(receipt_hash.keys.to_set)
24
+ raise ArgumentError, 'Missing required fields'
25
+ end
26
+
16
27
  signature_decoded = Base64.decode64(receipt_hash['signature'])
17
- data = Base64.decode64(receipt_hash['purchase-info'])
28
+ data_decoded = Base64.decode64(receipt_hash['purchase-info'])
18
29
 
19
- sig = StringIO.new(signature_decoded)
20
- [sig, data]
30
+ version, signature, receipt_cert = read_signature(signature_decoded)
31
+ [version, signature, receipt_cert, data_decoded]
21
32
  end
22
33
 
23
- def parse(input)
24
- sig, data = bla(input)
25
-
34
+ def read_signature(signature_decoded)
35
+ sig = StringIO.new(signature_decoded)
26
36
  version = sig.read(1).unpack('C').first # 8-bit unsigned (unsigned char)
27
- signature = sig.read(256)
37
+
38
+ unless SIGNATURE_LENGTH_MAPPING.keys.include?(version)
39
+ raise ArgumentError, "Unsupported receipt version: #{version}"
40
+ end
41
+
42
+ signature = sig.read(SIGNATURE_LENGTH_MAPPING[version])
28
43
  cert_size = sig.read(4).unpack('L>')[0] # 32-bit unsigned, big-endian
29
44
  receipt_cert = OpenSSL::X509::Certificate.new(sig.read(cert_size))
30
45
 
31
- [version, signature, receipt_cert, data]
46
+ [version, signature, receipt_cert]
32
47
  end
33
48
  end
34
49
  end
@@ -5,27 +5,44 @@ require 'openssl'
5
5
  module AppleReceipt
6
6
  # Validator allows one to check the validity of a receipt.
7
7
  class Validator
8
- def initialize(receipt)
9
- root_cert_pem = File.read('./certificates/AppleIncRootCertificate.cer')
10
- root_cert = OpenSSL::X509::Certificate.new(root_cert_pem)
8
+ INTERMEDIATE_CERT_MAPPING = {
9
+ 3 => 'AppleWorldwideDeveloperRelationsCertificationAuthority',
10
+ 2 => 'AppleITunesStoreCertificationAuthority'
11
+ }.freeze
11
12
 
12
- intermediate_cert_pem = File.read('./certificates/AppleWWDRCA.cer')
13
- intermediate_cert = OpenSSL::X509::Certificate.new(intermediate_cert_pem)
13
+ def initialize(receipt, certificates: [])
14
+ populate_certificate_store(receipt.version, certificates)
15
+ @receipt = receipt
16
+ end
17
+
18
+ def populate_certificate_store(version, provided_certificates)
19
+ if provided_certificates.any?
20
+ add_certificates(provided_certificates)
21
+ else
22
+ add_named_certificate('AppleRootCA')
23
+ add_named_certificate(INTERMEDIATE_CERT_MAPPING[version])
24
+ end
25
+ end
14
26
 
15
- store.add_cert(root_cert)
16
- store.add_cert(intermediate_cert)
27
+ def add_named_certificate(name)
28
+ cert_file = File.read("./certificates/#{name}.cer")
29
+ store.add_cert(OpenSSL::X509::Certificate.new(cert_file))
30
+ end
17
31
 
18
- @receipt = receipt
32
+ def add_certificates(certificates)
33
+ certificates.each do |cert|
34
+ store.add_cert(cert)
35
+ end
19
36
  end
20
37
 
21
38
  def valid?
22
- store.verify(receipt.cert) &&
39
+ store.verify(receipt.certificate) &&
23
40
  public_key.verify(OpenSSL::Digest::SHA1.new,
24
41
  receipt.signature, signed_data)
25
42
  end
26
43
 
27
44
  def public_key
28
- receipt.cert.public_key
45
+ receipt.certificate.public_key
29
46
  end
30
47
 
31
48
  def signed_data
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppleReceipt
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apple_receipt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koen Rouwhorst
@@ -10,6 +10,34 @@ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2018-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: openssl
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,20 +66,34 @@ dependencies:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
68
  version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: rake
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
87
  - - "~>"
46
88
  - !ruby/object:Gem::Version
47
- version: '10.0'
89
+ version: '12.0'
48
90
  type: :development
49
91
  prerelease: false
50
92
  version_requirements: !ruby/object:Gem::Requirement
51
93
  requirements:
52
94
  - - "~>"
53
95
  - !ruby/object:Gem::Version
54
- version: '10.0'
96
+ version: '12.0'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: rubocop
57
99
  requirement: !ruby/object:Gem::Requirement
@@ -76,16 +118,16 @@ files:
76
118
  - ".gitignore"
77
119
  - ".rubocop.yml"
78
120
  - ".travis.yml"
121
+ - CHANGELOG.md
79
122
  - CODE_OF_CONDUCT.md
80
123
  - Gemfile
81
124
  - LICENSE.txt
82
125
  - README.md
83
126
  - Rakefile
84
127
  - apple_receipt.gemspec
85
- - bin/console
86
- - bin/setup
87
- - certificates/AppleIncRootCertificate.cer
88
- - certificates/AppleWWDRCA.cer
128
+ - certificates/AppleITunesStoreCertificationAuthority.cer
129
+ - certificates/AppleRootCA.cer
130
+ - certificates/AppleWorldwideDeveloperRelationsCertificationAuthority.cer
89
131
  - lib/apple_receipt.rb
90
132
  - lib/apple_receipt/next_step_parser.rb
91
133
  - lib/apple_receipt/receipt.rb
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'apple_receipt'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here