Compare commits

..

9 Commits

Author SHA1 Message Date
e167bd983f ios: use generic xcode signing selector
Some checks failed
TestFlight Release / testflight (push) Failing after 19s
2026-06-25 21:25:13 -07:00
e4dd91564f ios: unlock signing keychain before build
Some checks failed
TestFlight Release / testflight (push) Failing after 17s
2026-06-25 21:20:31 -07:00
3bfde476a6 ios: use single identity signing p12
Some checks failed
TestFlight Release / testflight (push) Failing after 16s
2026-06-25 21:18:54 -07:00
b8676027db ios: trust Apple root in CI signing keychain
Some checks failed
TestFlight Release / testflight (push) Failing after 8s
2026-06-25 21:12:53 -07:00
d36d2c60a3 ios: install Apple WWDR intermediate in CI
Some checks failed
TestFlight Release / testflight (push) Failing after 18s
2026-06-25 21:11:01 -07:00
3d7031bb40 ios: avoid default keychain mutation in ci
Some checks failed
TestFlight Release / testflight (push) Failing after 17s
2026-06-25 21:08:32 -07:00
fa9b725c77 ios: expose signing keychain to xcodebuild
Some checks failed
TestFlight Release / testflight (push) Failing after 9s
2026-06-25 21:07:38 -07:00
a88987d08d ios: pin distribution signing identity
Some checks failed
TestFlight Release / testflight (push) Failing after 15s
2026-06-25 21:05:26 -07:00
e137ea1077 ios: bootstrap signing with existing certificate
Some checks failed
TestFlight Release / testflight (push) Failing after 17s
2026-06-25 21:03:43 -07:00
4 changed files with 269 additions and 56 deletions

View File

@@ -89,11 +89,17 @@ jobs:
printf '%s' "${APPSTORE_CERTIFICATES_FILE_BASE64}" | base64 --decode > ios/build/secrets/appstore-signing.p12 printf '%s' "${APPSTORE_CERTIFICATES_FILE_BASE64}" | base64 --decode > ios/build/secrets/appstore-signing.p12
printf '%s' "${APPSTORE_PROVISIONING_PROFILE_BASE64}" | base64 --decode > "${HOME}/Library/MobileDevice/Provisioning Profiles/Sybil_AppStore_CI.mobileprovision" printf '%s' "${APPSTORE_PROVISIONING_PROFILE_BASE64}" | base64 --decode > "${HOME}/Library/MobileDevice/Provisioning Profiles/Sybil_AppStore_CI.mobileprovision"
curl -fsSL https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer -o ios/build/secrets/AppleWWDRCAG3.cer
security create-keychain -p "${keychain_password}" "${keychain_path}" security create-keychain -p "${keychain_password}" "${keychain_path}"
security set-keychain-settings -lut 21600 "${keychain_path}" security set-keychain-settings -lut 21600 "${keychain_path}"
security unlock-keychain -p "${keychain_password}" "${keychain_path}" security unlock-keychain -p "${keychain_password}" "${keychain_path}"
security list-keychains -d user -s "${keychain_path}" $(security list-keychains -d user | sed 's/[ "]//g') security list-keychains -d user -s "${keychain_path}" $(security list-keychains -d user | sed 's/[ "]//g')
security import ios/build/secrets/AppleWWDRCAG3.cer \
-k "${keychain_path}" \
-T /usr/bin/codesign \
-T /usr/bin/security \
-T /usr/bin/xcodebuild
security import ios/build/secrets/appstore-signing.p12 \ security import ios/build/secrets/appstore-signing.p12 \
-k "${keychain_path}" \ -k "${keychain_path}" \
-P "${APPSTORE_CERTIFICATES_PASSWORD}" \ -P "${APPSTORE_CERTIFICATES_PASSWORD}" \
@@ -101,6 +107,11 @@ jobs:
-T /usr/bin/security \ -T /usr/bin/security \
-T /usr/bin/xcodebuild -T /usr/bin/xcodebuild
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${keychain_password}" "${keychain_path}" security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${keychain_password}" "${keychain_path}"
security find-identity -v -p codesigning "${keychain_path}"
{
echo "SYBIL_SIGNING_KEYCHAIN_PATH=${keychain_path}"
echo "SYBIL_SIGNING_KEYCHAIN_PASSWORD=${keychain_password}"
} >> "${GITHUB_ENV}"
- name: Build and upload to TestFlight - name: Build and upload to TestFlight
working-directory: ios working-directory: ios
@@ -116,6 +127,10 @@ jobs:
run: | run: |
set -euo pipefail set -euo pipefail
security unlock-keychain -p "${SYBIL_SIGNING_KEYCHAIN_PASSWORD}" "${SYBIL_SIGNING_KEYCHAIN_PATH}"
security list-keychains -d user -s "${SYBIL_SIGNING_KEYCHAIN_PATH}" $(security list-keychains -d user | sed 's/[ "]//g')
security find-identity -v -p codesigning "${SYBIL_SIGNING_KEYCHAIN_PATH}"
SYBIL_VERSION_TAG="${TAG_NAME}" bundle exec fastlane ios beta SYBIL_VERSION_TAG="${TAG_NAME}" bundle exec fastlane ios beta
- name: Locate IPA - name: Locate IPA

View File

@@ -5,6 +5,10 @@ FASTLANE_HIDE_CHANGELOG=1
SYBIL_APP_STORE_APPLE_ID=6759442828 SYBIL_APP_STORE_APPLE_ID=6759442828
SYBIL_PROVIDER_PUBLIC_ID=c043d167-ad88-4036-84ea-76c223f1b1b2 SYBIL_PROVIDER_PUBLIC_ID=c043d167-ad88-4036-84ea-76c223f1b1b2
SYBIL_PROVISIONING_PROFILE_SPECIFIER=Sybil AppStore CI SYBIL_PROVISIONING_PROFILE_SPECIFIER=Sybil AppStore CI
SYBIL_CODE_SIGN_IDENTITY=Apple Distribution: James Magahern (DQQH5H6GBD)
SYBIL_XCODE_CODE_SIGN_IDENTITY=Apple Distribution
SYBIL_SIGNING_CERTIFICATE_ID=
SYBIL_SIGNING_KEYCHAIN=
# App Store Connect API key settings for TestFlight upload and signing setup. # App Store Connect API key settings for TestFlight upload and signing setup.
APP_STORE_CONNECT_API_KEY_ID= APP_STORE_CONNECT_API_KEY_ID=

View File

@@ -42,6 +42,17 @@ certificate and provisioning profile values into the repository secrets listed
above. The workflow uses the `Sybil AppStore CI` provisioning profile name by above. The workflow uses the `Sybil AppStore CI` provisioning profile name by
default. default.
Fastlane keeps two signing names separate. `SYBIL_CODE_SIGN_IDENTITY` is the
exact certificate common name used when exporting a local p12 for secrets, while
`SYBIL_XCODE_CODE_SIGN_IDENTITY` defaults to the generic `Apple Distribution`
selector that Xcode uses during archive/export.
If the Apple team has reached the Distribution certificate limit, set
`SYBIL_SIGNING_CERTIFICATE_ID` to the portal id for a certificate whose private
key exists in the local login keychain before running `create_ci_signing`. The
lane will export the local identity and create the provisioning profile against
that existing certificate instead of creating another Distribution certificate.
If `create_ci_signing` fails with an expired or missing agreement error, the If `create_ci_signing` fails with an expired or missing agreement error, the
Apple Developer Program account holder must accept the current agreements in Apple Developer Program account holder must accept the current agreements in
App Store Connect before new certificates or provisioning profiles can be App Store Connect before new certificates or provisioning profiles can be

View File

@@ -1,9 +1,13 @@
require "dotenv" require "dotenv"
require "base64" require "base64"
require "fileutils" require "fileutils"
require "json"
require "net/http"
require "open3" require "open3"
require "openssl"
require "securerandom" require "securerandom"
require "shellwords" require "shellwords"
require "uri"
require "yaml" require "yaml"
Dotenv.load(File.expand_path("../.env", __dir__)) Dotenv.load(File.expand_path("../.env", __dir__))
@@ -15,6 +19,8 @@ TEAM_ID = ENV.fetch("FASTLANE_TEAM_ID", "DQQH5H6GBD")
APP_STORE_APPLE_ID = ENV.fetch("SYBIL_APP_STORE_APPLE_ID", "6759442828") APP_STORE_APPLE_ID = ENV.fetch("SYBIL_APP_STORE_APPLE_ID", "6759442828")
PROVIDER_PUBLIC_ID = ENV.fetch("SYBIL_PROVIDER_PUBLIC_ID", "c043d167-ad88-4036-84ea-76c223f1b1b2") PROVIDER_PUBLIC_ID = ENV.fetch("SYBIL_PROVIDER_PUBLIC_ID", "c043d167-ad88-4036-84ea-76c223f1b1b2")
PROFILE_SPECIFIER = ENV["SYBIL_PROVISIONING_PROFILE_SPECIFIER"].to_s.strip.empty? ? "Sybil AppStore CI" : ENV["SYBIL_PROVISIONING_PROFILE_SPECIFIER"] PROFILE_SPECIFIER = ENV["SYBIL_PROVISIONING_PROFILE_SPECIFIER"].to_s.strip.empty? ? "Sybil AppStore CI" : ENV["SYBIL_PROVISIONING_PROFILE_SPECIFIER"]
SIGNING_CERTIFICATE_NAME = ENV["SYBIL_CODE_SIGN_IDENTITY"].to_s.strip.empty? ? "Apple Distribution: James Magahern (DQQH5H6GBD)" : ENV["SYBIL_CODE_SIGN_IDENTITY"]
XCODE_CODE_SIGN_IDENTITY = ENV["SYBIL_XCODE_CODE_SIGN_IDENTITY"].to_s.strip.empty? ? "Apple Distribution" : ENV["SYBIL_XCODE_CODE_SIGN_IDENTITY"]
IOS_ROOT = File.expand_path("..", __dir__) IOS_ROOT = File.expand_path("..", __dir__)
PROJECT_FILE = File.join(IOS_ROOT, "Sybil.xcodeproj") PROJECT_FILE = File.join(IOS_ROOT, "Sybil.xcodeproj")
PROJECT_SPEC = File.join(IOS_ROOT, "project.yml") PROJECT_SPEC = File.join(IOS_ROOT, "project.yml")
@@ -72,9 +78,178 @@ def release_version
end end
def xcode_build_setting(key, value) def xcode_build_setting(key, value)
"#{key.to_s.shellescape}=#{value.to_s.shellescape}"
end
def env_line(key, value)
"#{key}=#{value.to_s.shellescape}" "#{key}=#{value.to_s.shellescape}"
end end
def base64url(value)
Base64.urlsafe_encode64(value).delete("=")
end
def integer_to_fixed_bytes(integer, length)
hex = integer.to_s(16)
hex = "0#{hex}" if hex.length.odd?
[hex].pack("H*").rjust(length, "\0")[-length, length]
end
def app_store_connect_private_key
key_path = ENV["APP_STORE_CONNECT_API_KEY_PATH"]
key_content = ENV["APP_STORE_CONNECT_API_KEY_CONTENT"]
pem = if present?(key_path)
File.read(key_path)
elsif present?(key_content)
ENV["APP_STORE_CONNECT_API_KEY_CONTENT_BASE64"].to_s == "true" ? Base64.decode64(key_content) : key_content
end
UI.user_error!("App Store Connect API key content is required") unless present?(pem)
OpenSSL::PKey::EC.new(pem)
end
def app_store_connect_jwt
key_id = ENV["APP_STORE_CONNECT_API_KEY_ID"]
issuer_id = ENV["APP_STORE_CONNECT_API_ISSUER_ID"]
issuer_id = ENV["APP_STORE_CONNECT_API_KEY_ISSUER_ID"] unless present?(issuer_id)
UI.user_error!("App Store Connect API key id and issuer id are required") unless present?(key_id) && present?(issuer_id)
header = { alg: "ES256", kid: key_id, typ: "JWT" }
payload = { iss: issuer_id, iat: Time.now.to_i, exp: Time.now.to_i + 600, aud: "appstoreconnect-v1" }
unsigned = [base64url(header.to_json), base64url(payload.to_json)].join(".")
asn1_signature = app_store_connect_private_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(unsigned))
signature_sequence = OpenSSL::ASN1.decode(asn1_signature)
raw_signature = signature_sequence.value.map { |part| integer_to_fixed_bytes(part.value, 32) }.join
[unsigned, base64url(raw_signature)].join(".")
end
def app_store_connect_request(method, path, payload = nil)
uri = URI("https://api.appstoreconnect.apple.com#{path}")
request_class = Net::HTTP.const_get(method.to_s.capitalize)
request = request_class.new(uri)
request["Authorization"] = "Bearer #{app_store_connect_jwt}"
if payload
request["Content-Type"] = "application/json"
request.body = payload.to_json
end
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
return {} if response.is_a?(Net::HTTPSuccess) && response.body.to_s.empty?
return JSON.parse(response.body) if response.is_a?(Net::HTTPSuccess)
UI.user_error!("App Store Connect API request failed: #{method.to_s.upcase} #{path}\n#{response.body}")
end
def bundle_id_resource_id
response = app_store_connect_request(
:get,
"/v1/bundleIds?filter[identifier]=#{URI.encode_www_form_component(APP_IDENTIFIER)}&limit=1"
)
id = response.fetch("data", []).first&.fetch("id", nil)
UI.user_error!("Could not find App Store Connect bundle id resource for #{APP_IDENTIFIER}") unless present?(id)
id
end
def recreate_app_store_profile(certificate_id)
existing = app_store_connect_request(
:get,
"/v1/profiles?filter[name]=#{URI.encode_www_form_component(PROFILE_SPECIFIER)}&limit=200"
)
existing.fetch("data", []).each do |profile|
app_store_connect_request(:delete, "/v1/profiles/#{profile.fetch("id")}")
end
payload = {
data: {
type: "profiles",
attributes: {
name: PROFILE_SPECIFIER,
profileType: "IOS_APP_STORE"
},
relationships: {
bundleId: {
data: { type: "bundleIds", id: bundle_id_resource_id }
},
certificates: {
data: [{ type: "certificates", id: certificate_id }]
}
}
}
}
response = app_store_connect_request(:post, "/v1/profiles", payload)
profile_content = response.dig("data", "attributes", "profileContent")
UI.user_error!("App Store Connect profile response did not include profileContent") unless present?(profile_content)
profile_path = File.join(SIGNING_OUTPUT_DIR, "Sybil_AppStore_CI.mobileprovision")
File.binwrite(profile_path, Base64.decode64(profile_content))
install_dir = File.expand_path("~/Library/MobileDevice/Provisioning Profiles")
FileUtils.mkdir_p(install_dir)
FileUtils.cp(profile_path, File.join(install_dir, "Sybil_AppStore_CI.mobileprovision"))
profile_path
end
def signing_identity_p12(p12_path, p12_password)
work_dir = File.join(SIGNING_OUTPUT_DIR, "single-identity")
FileUtils.rm_rf(work_dir)
FileUtils.mkdir_p(work_dir)
all_pem = File.join(work_dir, "all.pem")
stdout, stderr, status = Open3.capture3(
"openssl", "pkcs12",
"-in", p12_path,
"-nodes",
"-passin", "pass:#{p12_password}",
"-out", all_pem
)
UI.user_error!("Could not inspect exported p12\n#{stderr}\n#{stdout}") unless status.success?
records = File.read(all_pem).split(/(?=Bag Attributes\n)/)
cert_record = records.find do |record|
record.include?("friendlyName: #{SIGNING_CERTIFICATE_NAME}") && record.include?("-----BEGIN CERTIFICATE-----")
end
UI.user_error!("Could not find #{SIGNING_CERTIFICATE_NAME} certificate in exported p12") unless cert_record
local_key_id = cert_record[/localKeyID: (.+)/, 1].to_s.strip
UI.user_error!("Could not resolve localKeyID for #{SIGNING_CERTIFICATE_NAME}") unless present?(local_key_id)
key_record = records.find do |record|
record.include?("localKeyID: #{local_key_id}") && record.include?("-----BEGIN PRIVATE KEY-----")
end
UI.user_error!("Could not find private key for #{SIGNING_CERTIFICATE_NAME}") unless key_record
cert_path = File.join(work_dir, "identity.cer.pem")
key_path = File.join(work_dir, "identity.key.pem")
File.write(cert_path, cert_record[/-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m])
File.write(key_path, key_record[/-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----/m])
root_cer = File.join(work_dir, "AppleIncRootCertificate.cer")
wwdr_cer = File.join(work_dir, "AppleWWDRCAG3.cer")
root_pem = File.join(work_dir, "AppleIncRootCertificate.pem")
wwdr_pem = File.join(work_dir, "AppleWWDRCAG3.pem")
chain_pem = File.join(work_dir, "chain.pem")
run_silent("curl", "-fsSL", "https://www.apple.com/appleca/AppleIncRootCertificate.cer", "-o", root_cer, error_message: "Could not download Apple root certificate")
run_silent("curl", "-fsSL", "https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer", "-o", wwdr_cer, error_message: "Could not download Apple WWDR G3 certificate")
run_silent("openssl", "x509", "-inform", "DER", "-in", root_cer, "-out", root_pem, error_message: "Could not convert Apple root certificate")
run_silent("openssl", "x509", "-inform", "DER", "-in", wwdr_cer, "-out", wwdr_pem, error_message: "Could not convert Apple WWDR G3 certificate")
File.write(chain_pem, File.read(wwdr_pem) + File.read(root_pem))
single_p12_path = File.join(work_dir, "appstore-signing.p12")
run_silent(
"openssl", "pkcs12", "-export",
"-inkey", key_path,
"-in", cert_path,
"-certfile", chain_pem,
"-name", SIGNING_CERTIFICATE_NAME,
"-out", single_p12_path,
"-passout", "pass:#{p12_password}",
error_message: "Could not create single-identity p12"
)
FileUtils.cp(single_p12_path, p12_path)
ensure
FileUtils.rm_rf(work_dir) if present?(work_dir)
end
def app_store_connect_key_options def app_store_connect_key_options
key_id = ENV["APP_STORE_CONNECT_API_KEY_ID"] key_id = ENV["APP_STORE_CONNECT_API_KEY_ID"]
issuer_id = ENV["APP_STORE_CONNECT_API_ISSUER_ID"] issuer_id = ENV["APP_STORE_CONNECT_API_ISSUER_ID"]
@@ -121,79 +296,82 @@ platform :ios do
FileUtils.rm_rf(SIGNING_OUTPUT_DIR) FileUtils.rm_rf(SIGNING_OUTPUT_DIR)
FileUtils.mkdir_p(SIGNING_OUTPUT_DIR) FileUtils.mkdir_p(SIGNING_OUTPUT_DIR)
keychain_path = File.join(SIGNING_OUTPUT_DIR, "sybil_ci_signing.keychain-db") cert_id = ENV["SYBIL_SIGNING_CERTIFICATE_ID"].to_s
keychain_password = SecureRandom.base64(24) keychain_path = nil
keychain_password = nil
p12_path = File.join(SIGNING_OUTPUT_DIR, "appstore-signing.p12")
p12_password = ENV["SYBIL_CI_P12_PASSWORD"].to_s p12_password = ENV["SYBIL_CI_P12_PASSWORD"].to_s
if p12_password.empty? if p12_password.empty?
p12_password = SecureRandom.base64(24) p12_password = SecureRandom.base64(24)
UI.important("Generated a p12 password for CI secrets.") UI.important("Generated a p12 password for CI secrets.")
end end
run_silent(
"security", "create-keychain", "-p", keychain_password, keychain_path,
error_message: "Could not create temporary signing keychain"
)
run_silent(
"security", "set-keychain-settings", "-lut", "21600", keychain_path,
error_message: "Could not configure temporary signing keychain"
)
run_silent(
"security", "unlock-keychain", "-p", keychain_password, keychain_path,
error_message: "Could not unlock temporary signing keychain"
)
run_silent(
"security", "list-keychains", "-d", "user", "-s", keychain_path, *user_keychains,
error_message: "Could not add temporary signing keychain to the user search list"
)
begin begin
cert( if present?(cert_id)
api_key: api_key, UI.message("Using existing signing certificate id #{cert_id}")
development: false, export_keychain = ENV["SYBIL_SIGNING_KEYCHAIN"].to_s
force: true, export_keychain = File.expand_path("~/Library/Keychains/login.keychain-db") unless present?(export_keychain)
generate_apple_certs: true, run_silent(
keychain_password: keychain_password, "security", "export", "-k", export_keychain, "-t", "identities", "-f", "pkcs12", "-P", p12_password, "-o", p12_path,
keychain_path: keychain_path, error_message: "Could not export the local CI signing identity"
output_path: SIGNING_OUTPUT_DIR, )
platform: "ios" else
) keychain_path = File.join(SIGNING_OUTPUT_DIR, "sybil_ci_signing.keychain-db")
keychain_password = SecureRandom.base64(24)
run_silent(
"security", "create-keychain", "-p", keychain_password, keychain_path,
error_message: "Could not create temporary signing keychain"
)
run_silent(
"security", "set-keychain-settings", "-lut", "21600", keychain_path,
error_message: "Could not configure temporary signing keychain"
)
run_silent(
"security", "unlock-keychain", "-p", keychain_password, keychain_path,
error_message: "Could not unlock temporary signing keychain"
)
run_silent(
"security", "list-keychains", "-d", "user", "-s", keychain_path, *user_keychains,
error_message: "Could not add temporary signing keychain to the user search list"
)
cert_id = lane_context[SharedValues::CERT_CERTIFICATE_ID] cert(
UI.user_error!("Could not resolve generated certificate id") unless present?(cert_id) api_key: api_key,
development: false,
force: true,
generate_apple_certs: true,
keychain_password: keychain_password,
keychain_path: keychain_path,
output_path: SIGNING_OUTPUT_DIR,
platform: "ios"
)
sigh( cert_id = lane_context[SharedValues::CERT_CERTIFICATE_ID]
api_key: api_key, UI.user_error!("Could not resolve generated certificate id") unless present?(cert_id)
app_identifier: APP_IDENTIFIER, run_silent(
cert_id: cert_id, "security", "export", "-k", keychain_path, "-t", "identities", "-f", "pkcs12", "-P", p12_password, "-o", p12_path,
filename: "Sybil_AppStore_CI.mobileprovision", error_message: "Could not export the generated CI signing identity"
force: true, )
output_path: SIGNING_OUTPUT_DIR, end
platform: "ios",
provisioning_name: PROFILE_SPECIFIER
)
profile_path = lane_context[SharedValues::SIGH_PROFILE_PATH]
UI.user_error!("Could not resolve generated provisioning profile path") unless present?(profile_path) && File.exist?(profile_path)
p12_path = File.join(SIGNING_OUTPUT_DIR, "appstore-signing.p12")
run_silent(
"security", "export", "-k", keychain_path, "-t", "identities", "-f", "pkcs12", "-P", p12_password, "-o", p12_path,
error_message: "Could not export the CI signing identity"
)
UI.user_error!("Could not find exported p12 at #{p12_path}") unless File.exist?(p12_path) UI.user_error!("Could not find exported p12 at #{p12_path}") unless File.exist?(p12_path)
signing_identity_p12(p12_path, p12_password)
profile_path = recreate_app_store_profile(cert_id)
UI.user_error!("Could not resolve generated provisioning profile path") unless present?(profile_path) && File.exist?(profile_path)
secrets_path = File.join(SIGNING_OUTPUT_DIR, "ci-secrets.env") secrets_path = File.join(SIGNING_OUTPUT_DIR, "ci-secrets.env")
File.write( File.write(
secrets_path, secrets_path,
[ [
"APPSTORE_CERTIFICATES_FILE_BASE64=#{Base64.strict_encode64(File.binread(p12_path))}", env_line("APPSTORE_CERTIFICATES_FILE_BASE64", Base64.strict_encode64(File.binread(p12_path))),
"APPSTORE_CERTIFICATES_PASSWORD=#{p12_password}", env_line("APPSTORE_CERTIFICATES_PASSWORD", p12_password),
"APPSTORE_PROVISIONING_PROFILE_BASE64=#{Base64.strict_encode64(File.binread(profile_path))}", env_line("APPSTORE_PROVISIONING_PROFILE_BASE64", Base64.strict_encode64(File.binread(profile_path))),
"SYBIL_PROVISIONING_PROFILE_SPECIFIER=#{PROFILE_SPECIFIER}" env_line("SYBIL_PROVISIONING_PROFILE_SPECIFIER", PROFILE_SPECIFIER)
].join("\n") + "\n" ].join("\n") + "\n"
) )
ensure ensure
system("security", "delete-keychain", keychain_path, out: File::NULL, err: File::NULL) if File.exist?(keychain_path) system("security", "delete-keychain", keychain_path, out: File::NULL, err: File::NULL) if present?(keychain_path) && File.exist?(keychain_path)
end end
UI.success("Created CI signing files in #{SIGNING_OUTPUT_DIR}") UI.success("Created CI signing files in #{SIGNING_OUTPUT_DIR}")
@@ -233,8 +411,12 @@ platform :ios do
xcode_build_setting("CODE_SIGN_STYLE", "Manual"), xcode_build_setting("CODE_SIGN_STYLE", "Manual"),
xcode_build_setting("DEVELOPMENT_TEAM", TEAM_ID), xcode_build_setting("DEVELOPMENT_TEAM", TEAM_ID),
xcode_build_setting("PROVISIONING_PROFILE_SPECIFIER", PROFILE_SPECIFIER), xcode_build_setting("PROVISIONING_PROFILE_SPECIFIER", PROFILE_SPECIFIER),
xcode_build_setting("CODE_SIGN_IDENTITY", "Apple Distribution") xcode_build_setting("CODE_SIGN_IDENTITY", XCODE_CODE_SIGN_IDENTITY)
].join(" ") ]
if present?(ENV["SYBIL_SIGNING_KEYCHAIN_PATH"])
xcode_args << xcode_build_setting("OTHER_CODE_SIGN_FLAGS", "--keychain #{ENV.fetch("SYBIL_SIGNING_KEYCHAIN_PATH")}")
end
xcode_args = xcode_args.join(" ")
ipa_path = build_app( ipa_path = build_app(
project: PROJECT_FILE, project: PROJECT_FILE,
@@ -252,6 +434,7 @@ platform :ios do
provisioningProfiles: { provisioningProfiles: {
APP_IDENTIFIER => PROFILE_SPECIFIER APP_IDENTIFIER => PROFILE_SPECIFIER
}, },
signingCertificate: XCODE_CODE_SIGN_IDENTITY,
teamID: TEAM_ID, teamID: TEAM_ID,
manageAppVersionAndBuildNumber: false, manageAppVersionAndBuildNumber: false,
uploadSymbols: true, uploadSymbols: true,