diff --git a/.github/workflows/sync-code-signing.yml b/.github/workflows/sync-code-signing.yml new file mode 100644 index 00000000..616b9d76 --- /dev/null +++ b/.github/workflows/sync-code-signing.yml @@ -0,0 +1,52 @@ +name: iOS Code Signing + +on: + workflow_dispatch: + inputs: + force: + description: Force regenerate profiles + required: true + default: "true" + type: choice + options: + - "true" + - "false" + +env: + RUBY_VERSION: "3.2" + APP_STORE_TEAM_ID: ${{ secrets.APP_STORE_TEAM_ID }} + ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} + ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} + ASC_KEY_PATH: fastlane/AuthKey.p8 + SPACESHIP_CONNECT_API_IN_HOUSE: "false" + MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} + MATCH_FORCE: ${{ inputs.force }} + +permissions: + contents: read + +jobs: + sync-appstore-profiles: + runs-on: macos-latest + timeout-minutes: 20 + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ${{ env.RUBY_VERSION }} + + - name: Write App Store Connect API key + env: + ASC_KEY_CONTENT: ${{ secrets.ASC_KEY_CONTENT }} + run: | + printf '%s' "$ASC_KEY_CONTENT" | base64 -D > "$ASC_KEY_PATH" + + - name: Sync App Store profiles + run: bundle exec fastlane sync_appstore_profiles diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 65c5eacc..75b16e5e 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,6 +1,9 @@ XCODE_PROJ = "DevLog.xcodeproj" APP_IDENTIFIER = "opfic.DevLog" +WIDGET_IDENTIFIER = "opfic.DevLog.DevLogWidget" +APP_IDENTIFIERS = [APP_IDENTIFIER, WIDGET_IDENTIFIER] TARGET_NAME = "DevLog" +WIDGET_TARGET_NAME = "DevLogWidgetExtension" TESTFLIGHT_BUILD_OUTPUT_DIRECTORY = File.expand_path("testflight_build", __dir__) TESTFLIGHT_IPA_OUTPUT_PATH = File.join(TESTFLIGHT_BUILD_OUTPUT_DIRECTORY, "#{TARGET_NAME}.ipa") @@ -76,23 +79,32 @@ platform :ios do match( api_key: api_key, type: "appstore", + app_identifier: APP_IDENTIFIERS, readonly: ENV["CI"] == "true" ) if ENV["CI"] == "true" - provisioning_profile_specifier = lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING][APP_IDENTIFIER].to_s - UI.user_error!("Missing App Store provisioning profile mapping for #{APP_IDENTIFIER}") if provisioning_profile_specifier.empty? - - update_code_signing_settings( - use_automatic_signing: false, - path: XCODE_PROJ, - sdk: "iphoneos*", - team_id: ENV["APP_STORE_TEAM_ID"], - targets: [TARGET_NAME], - build_configurations: ["Release"], - code_sign_identity: "Apple Distribution", - profile_name: provisioning_profile_specifier - ) + profile_mapping = lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING] + signing_targets = { + TARGET_NAME => APP_IDENTIFIER, + WIDGET_TARGET_NAME => WIDGET_IDENTIFIER + } + + signing_targets.each do |target_name, app_identifier| + provisioning_profile_specifier = profile_mapping[app_identifier].to_s + UI.user_error!("Missing App Store provisioning profile mapping for #{app_identifier}") if provisioning_profile_specifier.empty? + + update_code_signing_settings( + use_automatic_signing: false, + path: XCODE_PROJ, + sdk: "iphoneos*", + team_id: ENV["APP_STORE_TEAM_ID"], + targets: [target_name], + build_configurations: ["Release"], + code_sign_identity: "Apple Distribution", + profile_name: provisioning_profile_specifier + ) + end end build_app( @@ -117,6 +129,19 @@ platform :ios do build_for_store end + lane :sync_appstore_profiles do + api_key = asc_api_key + match_force = ENV.fetch("MATCH_FORCE", "false") == "true" + + match( + api_key: api_key, + type: "appstore", + app_identifier: APP_IDENTIFIERS, + force: match_force, + readonly: false + ) + end + lane :upload_testflight_build do api_key = asc_api_key # lane_context는 같은 fastlane 실행 내에서만 유지되므로, 별도 CI step에서는 고정 ipa 경로를 사용한다. diff --git a/fastlane/Matchfile b/fastlane/Matchfile index 8787d38d..c2577129 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -3,7 +3,10 @@ git_url(ENV["MATCH_GIT_URL"]) storage_mode("git") git_branch("main") type("appstore") -app_identifier(["opfic.DevLog"]) +app_identifier([ + "opfic.DevLog", + "opfic.DevLog.DevLogWidget" +]) team_id(ENV["APP_STORE_TEAM_ID"]) readonly(ENV["CI"] == "true")