diff --git a/.gitignore b/.gitignore index 9bffd5214..5ab00c4b7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,13 @@ apps/ios/*.xcworkspace/ apps/ios/.swiftpm/ # fastlane (iOS) +apps/ios/fastlane/README.md apps/ios/fastlane/report.xml apps/ios/fastlane/Preview.html apps/ios/fastlane/screenshots/ apps/ios/fastlane/test_output/ apps/ios/fastlane/logs/ + +# fastlane build artifacts (local) +apps/ios/*.ipa +apps/ios/*.dSYM.zip diff --git a/apps/ios/README.md b/apps/ios/README.md index d1dbb991a..f1e384562 100644 --- a/apps/ios/README.md +++ b/apps/ios/README.md @@ -25,4 +25,4 @@ cd apps/ios fastlane lanes ``` -See `apps/ios/fastlane/README.md` for App Store Connect auth + upload lanes. +See `apps/ios/fastlane/SETUP.md` for App Store Connect auth + upload lanes. diff --git a/apps/ios/fastlane/.env.example b/apps/ios/fastlane/.env.example index 2b7c7668b..7f2c61333 100644 --- a/apps/ios/fastlane/.env.example +++ b/apps/ios/fastlane/.env.example @@ -13,6 +13,9 @@ # ASC_ISSUER_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # ASC_KEY_CONTENT=BASE64_P8_CONTENT +# Code signing +# IOS_DEVELOPMENT_TEAM=XXXXXXXXXX + # Deliver toggles (off by default) # DELIVER_METADATA=1 # DELIVER_SCREENSHOTS=1 diff --git a/apps/ios/fastlane/Fastfile b/apps/ios/fastlane/Fastfile index c09b4fcbc..a727692d0 100644 --- a/apps/ios/fastlane/Fastfile +++ b/apps/ios/fastlane/Fastfile @@ -1,50 +1,78 @@ default_platform(:ios) +def load_env_file(path) + return unless File.exist?(path) + + File.foreach(path) do |line| + stripped = line.strip + next if stripped.empty? || stripped.start_with?("#") + + key, value = stripped.split("=", 2) + next if key.nil? || key.empty? || value.nil? + + ENV[key] = value if ENV[key].nil? || ENV[key].strip.empty? + end +end + platform :ios do private_lane :asc_api_key do + load_env_file(File.join(__dir__, ".env")) + + api_key = nil + key_path = ENV["APP_STORE_CONNECT_API_KEY_PATH"] if key_path && !key_path.strip.empty? - return app_store_connect_api_key(path: key_path) - end - - p8_path = ENV["ASC_KEY_PATH"] - if p8_path && !p8_path.strip.empty? + api_key = app_store_connect_api_key(path: key_path) + else + p8_path = ENV["ASC_KEY_PATH"] + if p8_path && !p8_path.strip.empty? key_id = ENV["ASC_KEY_ID"] issuer_id = ENV["ASC_ISSUER_ID"] UI.user_error!("Missing ASC_KEY_ID or ASC_ISSUER_ID for ASC_KEY_PATH auth.") if [key_id, issuer_id].any? { |v| v.nil? || v.strip.empty? } - return app_store_connect_api_key( + api_key = app_store_connect_api_key( key_id: key_id, issuer_id: issuer_id, key_filepath: p8_path ) + else + key_id = ENV["ASC_KEY_ID"] + issuer_id = ENV["ASC_ISSUER_ID"] + key_content = ENV["ASC_KEY_CONTENT"] + + UI.user_error!("Missing App Store Connect API key. Set APP_STORE_CONNECT_API_KEY_PATH (json) or ASC_KEY_PATH (p8) or ASC_KEY_ID/ASC_ISSUER_ID/ASC_KEY_CONTENT.") if [key_id, issuer_id, key_content].any? { |v| v.nil? || v.strip.empty? } + + is_base64 = key_content.include?("BEGIN PRIVATE KEY") ? false : true + + api_key = app_store_connect_api_key( + key_id: key_id, + issuer_id: issuer_id, + key_content: key_content, + is_key_content_base64: is_base64 + ) + end end - key_id = ENV["ASC_KEY_ID"] - issuer_id = ENV["ASC_ISSUER_ID"] - key_content = ENV["ASC_KEY_CONTENT"] - - UI.user_error!("Missing App Store Connect API key. Set APP_STORE_CONNECT_API_KEY_PATH (json) or ASC_KEY_PATH (p8) or ASC_KEY_ID/ASC_ISSUER_ID/ASC_KEY_CONTENT.") if [key_id, issuer_id, key_content].any? { |v| v.nil? || v.strip.empty? } - - is_base64 = key_content.include?("BEGIN PRIVATE KEY") ? false : true - - app_store_connect_api_key( - key_id: key_id, - issuer_id: issuer_id, - key_content: key_content, - is_key_content_base64: is_base64 - ) + api_key end desc "Build + upload to TestFlight" lane :beta do api_key = asc_api_key + team_id = ENV["IOS_DEVELOPMENT_TEAM"] + UI.user_error!("Missing IOS_DEVELOPMENT_TEAM (Apple Team ID). Add it to fastlane/.env or export it in your shell.") if team_id.nil? || team_id.strip.empty? + build_app( project: "Clawdis.xcodeproj", scheme: "Clawdis", export_method: "app-store", - clean: true + clean: true, + xcargs: "DEVELOPMENT_TEAM=#{team_id} -allowProvisioningUpdates", + export_xcargs: "-allowProvisioningUpdates", + export_options: { + signingStyle: "automatic" + } ) upload_to_testflight( diff --git a/apps/ios/fastlane/README.md b/apps/ios/fastlane/README.md deleted file mode 100644 index 47e0a35f1..000000000 --- a/apps/ios/fastlane/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# fastlane (Clawdis iOS) - -Install fastlane (recommended via Homebrew): - -```bash -brew install fastlane -``` - -Configure App Store Connect auth: - -- Recommended: set `ASC_KEY_PATH` to the downloaded `.p8` path + set `ASC_KEY_ID` and `ASC_ISSUER_ID`. -- Alternative: set `APP_STORE_CONNECT_API_KEY_PATH` to a JSON key file path. -- Alternative: set `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_KEY_CONTENT` (base64 p8). - -Common lanes: - -```bash -cd apps/ios -fastlane beta - -# Upload metadata/screenshots only when explicitly enabled: -DELIVER_METADATA=1 fastlane metadata -DELIVER_METADATA=1 DELIVER_SCREENSHOTS=1 fastlane metadata -``` diff --git a/apps/ios/fastlane/SETUP.md b/apps/ios/fastlane/SETUP.md new file mode 100644 index 000000000..d7ab2bc7a --- /dev/null +++ b/apps/ios/fastlane/SETUP.md @@ -0,0 +1,31 @@ +# fastlane setup (Clawdis iOS) + +Install: + +```bash +brew install fastlane +``` + +Create an App Store Connect API key: + +- App Store Connect → Users and Access → Keys → App Store Connect API → Generate API Key +- Download the `.p8`, note the **Issuer ID** and **Key ID** + +Create `apps/ios/fastlane/.env` (gitignored): + +```bash +ASC_KEY_ID=YOUR_KEY_ID +ASC_ISSUER_ID=YOUR_ISSUER_ID +ASC_KEY_PATH=/absolute/path/to/AuthKey_XXXXXXXXXX.p8 + +# Code signing (Apple Team ID / App ID Prefix) +IOS_DEVELOPMENT_TEAM=YOUR_TEAM_ID +``` + +Run: + +```bash +cd apps/ios +fastlane beta +``` +