Some checks failed
TestFlight / testflight (push) Failing after 19s
codesign resolves signing identities through the user keychain search list (first match wins) and ignores --keychain for the lookup. This runner hosts another project whose keychain holds the same Apple Distribution identity, so when that keychain is locked, codesign fails with errSecInternalComponent no matter how correctly our own keychain is set up. Prepend the fresh CI keychain to the search list for the build and always delete it afterward, which restores the original list. Also drop the runner-diagnostics step, the GitHub-hosted Ruby PATH export, and the duplicate CODE_SIGN_IDENTITY param. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
165 lines
5.1 KiB
Ruby
165 lines
5.1 KiB
Ruby
require "shellwords"
|
|
|
|
default_platform(:ios)
|
|
|
|
APP_IDENTIFIER = "net.buzzert.sybil2"
|
|
SCHEME = "Sybil"
|
|
TEAM_ID = "DQQH5H6GBD"
|
|
PROFILE_NAME = "Sybil AppStore CI"
|
|
CI_KEYCHAIN_NAME = "sybil_ci_keychain"
|
|
CI_KEYCHAIN_PASSWORD = "sybil-ci-keychain-password"
|
|
CI_KEYCHAIN_DB_PATH = File.expand_path("~/Library/Keychains/#{CI_KEYCHAIN_NAME}-db")
|
|
IOS_ROOT = File.expand_path("..", __dir__)
|
|
PROJECT_FILE = File.join(IOS_ROOT, "Sybil.xcodeproj")
|
|
PROJECT_SPEC = File.join(IOS_ROOT, "project.yml")
|
|
APP_PROJECT_SPEC = File.join(IOS_ROOT, "Apps/Sybil/project.yml")
|
|
|
|
def present?(value)
|
|
!value.to_s.strip.empty?
|
|
end
|
|
|
|
def ci?
|
|
present?(ENV["CI"])
|
|
end
|
|
|
|
def release_version
|
|
tag = ENV["SYBIL_VERSION_TAG"]
|
|
tag = ENV["GITHUB_REF_NAME"] if !present?(tag)
|
|
tag = ENV["GITHUB_REF"].to_s.sub(%r{\Arefs/tags/}, "") if !present?(tag)
|
|
tag = sh("git describe --tags --abbrev=0").strip if !present?(tag)
|
|
match = tag.to_s.match(%r{\Arelease/ios/v(\d+\.\d+\.\d+)\z})
|
|
|
|
unless match
|
|
UI.user_error!("Release tag must look like release/ios/v1.2.3; got #{tag.inspect}")
|
|
end
|
|
|
|
match[1]
|
|
end
|
|
|
|
# App Store Connect requires CFBundleVersion to be unique and strictly
|
|
# increasing app-wide (not just per marketing version), so we derive it from
|
|
# the monotonic CI run number rather than querying TestFlight (that query can
|
|
# lag behind builds still processing and hand back a colliding value).
|
|
def build_number
|
|
value = present?(ENV["SYBIL_BUILD_NUMBER"]) ? ENV["SYBIL_BUILD_NUMBER"] : ENV["GITHUB_RUN_NUMBER"]
|
|
|
|
unless value.to_s.match?(/\A\d+\z/)
|
|
UI.user_error!("Build number must come from SYBIL_BUILD_NUMBER/GITHUB_RUN_NUMBER; got #{value.inspect}")
|
|
end
|
|
|
|
value.to_i
|
|
end
|
|
|
|
def stamp_marketing_version(version)
|
|
contents = File.read(APP_PROJECT_SPEC)
|
|
updated = contents.sub(/^(\s*MARKETING_VERSION:\s*).*/, "\\1\"#{version}\"")
|
|
|
|
if updated == contents
|
|
UI.user_error!("Could not find MARKETING_VERSION in #{APP_PROJECT_SPEC}")
|
|
end
|
|
|
|
File.write(APP_PROJECT_SPEC, updated)
|
|
end
|
|
|
|
platform :ios do
|
|
private_lane :app_store_api_key do
|
|
app_store_connect_api_key(
|
|
key_id: ENV.fetch("APP_STORE_CONNECT_KEY_ID"),
|
|
issuer_id: ENV.fetch("APP_STORE_CONNECT_ISSUER_ID"),
|
|
key_content: ENV.fetch("APP_STORE_CONNECT_KEY_CONTENT"),
|
|
is_key_content_base64: true
|
|
)
|
|
end
|
|
|
|
# CI signs headlessly, so match needs a fresh unlocked keychain to import
|
|
# into. codesign resolves identities through the user keychain *search list*
|
|
# (first match wins; the --keychain flag does not restrict the lookup), and
|
|
# other projects' keychains on this runner hold the same identity but are
|
|
# usually locked — so ours must come first. delete_keychain in the beta
|
|
# lane's ensure removes both the keychain and its search-list entry, which
|
|
# also keeps our (later locked) copy from shadowing those other projects.
|
|
private_lane :prepare_ci_keychain do
|
|
next unless ci?
|
|
|
|
delete_keychain(name: CI_KEYCHAIN_NAME) if File.file?(CI_KEYCHAIN_DB_PATH)
|
|
create_keychain(
|
|
name: CI_KEYCHAIN_NAME,
|
|
password: CI_KEYCHAIN_PASSWORD,
|
|
unlock: true,
|
|
timeout: 3600,
|
|
add_to_search_list: false
|
|
)
|
|
|
|
others = sh("security list-keychains -d user", log: false)
|
|
.scan(/"([^"]+)"/)
|
|
.flatten
|
|
.reject { |path| path.include?(CI_KEYCHAIN_NAME) }
|
|
sh("security list-keychains -d user -s #{([CI_KEYCHAIN_DB_PATH] + others).shelljoin}")
|
|
|
|
ENV["MATCH_KEYCHAIN_NAME"] = CI_KEYCHAIN_NAME
|
|
ENV["MATCH_KEYCHAIN_PASSWORD"] = CI_KEYCHAIN_PASSWORD
|
|
end
|
|
|
|
private_lane :sync_signing do |options|
|
|
match(
|
|
type: "appstore",
|
|
readonly: options.fetch(:readonly),
|
|
app_identifier: APP_IDENTIFIER,
|
|
team_id: TEAM_ID,
|
|
profile_name: PROFILE_NAME,
|
|
git_url: ENV.fetch("MATCH_GIT_URL"),
|
|
git_branch: "master",
|
|
git_full_name: "Sybil Release Bot",
|
|
git_user_email: "james.magahern@me.com",
|
|
api_key: options.fetch(:api_key)
|
|
)
|
|
end
|
|
|
|
desc "Create or update match signing assets"
|
|
lane :setup_signing do
|
|
sync_signing(api_key: app_store_api_key, readonly: false)
|
|
end
|
|
|
|
desc "Build and upload to TestFlight"
|
|
lane :beta do
|
|
prepare_ci_keychain
|
|
|
|
api_key = app_store_api_key
|
|
|
|
version = release_version
|
|
stamp_marketing_version(version)
|
|
sh("xcodegen", "--spec", PROJECT_SPEC)
|
|
|
|
increment_version_number(version_number: version, xcodeproj: PROJECT_FILE)
|
|
increment_build_number(build_number: build_number, xcodeproj: PROJECT_FILE)
|
|
|
|
sync_signing(api_key: api_key, readonly: true)
|
|
|
|
build_app(
|
|
project: PROJECT_FILE,
|
|
scheme: SCHEME,
|
|
export_method: "app-store",
|
|
xcargs: [
|
|
"DEVELOPMENT_TEAM=#{TEAM_ID.shellescape}",
|
|
"CODE_SIGN_STYLE=Manual",
|
|
"CODE_SIGN_IDENTITY=Apple\\ Distribution",
|
|
"PROVISIONING_PROFILE_SPECIFIER=#{PROFILE_NAME.shellescape}"
|
|
].join(" "),
|
|
export_options: {
|
|
signingStyle: "manual",
|
|
teamID: TEAM_ID,
|
|
provisioningProfiles: {
|
|
APP_IDENTIFIER => PROFILE_NAME
|
|
}
|
|
}
|
|
)
|
|
|
|
upload_to_testflight(
|
|
api_key: api_key,
|
|
skip_waiting_for_build_processing: true
|
|
)
|
|
ensure
|
|
delete_keychain(name: CI_KEYCHAIN_NAME) if ci? && File.file?(CI_KEYCHAIN_DB_PATH)
|
|
end
|
|
end
|