fastlane icon indicating copy to clipboard operation
fastlane copied to clipboard

match+gym not automatically setting up provisioning profiles

Open tejassharma96 opened this issue 4 years ago â€ĸ 16 comments

New Issue Checklist

Issue Description

I'm trying to set up my pipeline to use fastlane match+gym to build my app, but consistently receive an error stating

error: "<TARGET NAME REDACTED>" requires a provisioning profile with the App Groups feature. Select a provisioning profile in the Signing & Capabilities editor. (in target '<TARGET NAME REDACTED>' from project '<PROJECT NAME REDACTED>')

I was expecting match to automatically set all of that up. When I go into my xcode project and manually set the provisioning profile for my targets to the profiles installed by match, and then run gym, it works as expected and successfully builds and archives the app. If I don't manually do that, it fails with the message above.

Based on http://docs.fastlane.tools/codesigning/xcode-project/, it seems like this might be intended behaviour. Would you be open to me creating a pull request to go through the provisioning profiles from match and update the targets via fastlane before running gym? I think it would be a much cleaner experience to be able to just say

lane :appstore-build do
  match(type: 'appstore')
  gym(export_method: 'app-store')
end
lane :enterprise-build do
  match(type: 'enterprise')
  gym(export_method: 'enterprise')
end

and have that work without needing to manually update provisioning profiles in xcode between running the two lanes.

I think I have pretty much everything I need to get that working and create a PR, except for the ability to get the certificate name (in order to update the codesigning identity) from match. Would appreciate any support on figuring out how to get that value from match, but if we can do that, then I would be happy to update the actions and create a PR to set that all up automatically without needing any intervention.

tejassharma96 avatar Apr 12 '22 22:04 tejassharma96

It seems like you have not included the output of fastlane env To make it easier for us help you resolve this issue, please update the issue to include the output of fastlane env :+1:

fastlane-bot avatar Apr 12 '22 22:04 fastlane-bot

I know that tagging you is a bit rude, so apologies in advance, but @joshdholtz you've been very helpful with my PRs in the past so would love to hear if this is something you would be interested in getting added to fastlane, and if you could help me with getting the certificate name. Thanks!

tejassharma96 avatar Apr 12 '22 22:04 tejassharma96

I am also facing exact same issue. Is your issue resloved? any recommendation how to solve this issue.? Can any one please help? MyInsuranceInfo

jenne045 avatar Apr 13 '22 06:04 jenne045

This works really well for us, thank you! Facing same issue here. Help is appreciated. MyCoverageInfo.com

jenne045 avatar Apr 15 '22 07:04 jenne045

When I quicklook the profiles I see this, and the value next to "Name" under certificates is the value that I want.
image

FastlaneCore::ProvisioningProfile doesn't seem to populate this value. I do see DeveloperCertificates as a key, but that references an array of StringIOs. When I run lines = profile['DeveloperCertificates'][0].readlines I get what looks like some encoded strings but haven't been able to decrypt those. I do see "iPhone Distribution: <Company Name redacted>." in lines[4], but no clue how to reliably parse that out :/

If you know what format those are and if there's a way to reliably parse those it'd be super helpful

tejassharma96 avatar Apr 15 '22 16:04 tejassharma96

Hi @tejassharma96 this is a really nice idea - having a logic to automatically setup profile in the project based on match result would probably make it easier to use fastlane. As you noticed though, leaving a decision about code signing for the users looks like an intended behaviour by design. I have a feeling that such a functionality should be opt-in, and I'm thinking if it shouldn't be turned off by default and enabled with an option switch in gym. Otherwise it could potentially be a breaking change and cause issues for the current users after update to newer version of fastlane. What do you think?

And as a side note, currently code signing settings in the project can be either handled manually in Xcode or changed with action http://docs.fastlane.tools/actions/update_project_provisioning/#update_project_provisioning just between match and gym call. Match adds environment variable with path to provisioning profile and it could be used in update_project_provisioning to set the profile. If you also need to change team, you could use update_project_team action.

lucgrabowski avatar Apr 21 '22 19:04 lucgrabowski

right I was thinking I could add a update_targets_automatically flag to match that defaults to false. I wrote a custom action that achieves this and was hoping we could just get it into fastlane instead, since it definitely feels like something fastlane could/should support. I'd argue that it should be in match over gym since just running match will automatically set up code signing for your xcode project/workspace and then you can build in xcode too vs just via gym. I'd also love to get https://github.com/fastlane/fastlane/pull/20187 merged in to support this. I'd be happy to create a draft PR of how it might look directly integrated into match once that gets merged.

I can't link to my action since it's in an internal repo, but I've copied it below:

module Fastlane
  module Actions
    class EnhancedMatchAction < Action
      INFOPLIST_FILE_KEY = "INFOPLIST_FILE"
      BUNDLE_ID_KEY = "CFBundleIdentifier"
      TEAM_ID_KEY = "DEVELOPMENT_TEAM"
      CODE_SIGN_IDENTITY_KEY = "CODE_SIGN_IDENTITY"
      PROFILE_NAME_KEY = "PROVISIONING_PROFILE_SPECIFIER"
      CODE_SIGN_STYLE_KEY = "CODE_SIGN_STYLE"
      CODE_SIGN_STYLE_VALUE = "Manual"
      CERTIFICATE_DATA_KEY = "DeveloperCertificates"
      CERTIFICATE_NAME_KEY = "CN"

      def self.run(params)
        other_action.match(self.match_options(params).dup)

        if params[:update_targets_automatically]
          mapping = Actions.lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING]
          unless mapping
            UI.user_error!("Could not load the provisioning profile mapping from match")
            return
          end
          self.update_targets_automatically(params, mapping)
        end
      end

      def self.description
        "Enhanced match"
      end

      def self.authors
        ["Tejas Sharma"]
      end

      def self.details
        "Runs fastlane match and uses the provisioning profile mapping to automatically update the targets "\
        "in your xcodeproj files. You must provide the team id and code sign identity manually"\
      end

      def self.available_options
        Fastlane::Actions::MatchAction.available_options + [
          FastlaneCore::ConfigItem.new(key: :build_configurations,
                                       env_name: "ENHANCED_MATCH_BUILD_CONFIGURATIONS",
                                       optional: true,
                                       type: Array,
                                       description: "Specify build_configurations you want to update signing for. (default to all configurations)"),
          FastlaneCore::ConfigItem.new(key: :code_sign_identity,
                                       env_name: "ENHANCED_MATCH_CODE_SIGN_IDENTITY",
                                       type: String,
                                       description: "Code signing identity type (iPhone Developer, iPhone Distribution). If you don't provide a value for this, "\
                                                    "the action will attempt to parse the value from your provisioning profile",
                                       optional: true),
          FastlaneCore::ConfigItem.new(key: :update_targets_automatically,
                                       env_name: "ENHANCED_MATCH_UPDATE_TARGETS",
                                       description: "Specify whether the action should automatically update provisioning profiles for your targets."\
                                                    "You must pass in values for team_id and code_sign_identity when setting this to true",
                                       default_value: false,
                                       type: Boolean,
                                       optional: false)
        ]
      end

      def self.is_supported?(platform)
        [:ios, :mac].include?(platform)
      end

      def self.match_options(params)
        Fastlane::Actions::MatchAction.available_options.map { |i| [i.key, params[i.key]] }.to_h.compact
      end

      def self.update_targets_automatically(params, mapping)
        Helper::XcodeprojHelper.get_all_xcodeprojects.each do |proj|
          update_signing_for_targets(params, proj, mapping)
        end
      end

      # rubocop:disable Metrics/PerceivedComplexity
      def self.update_signing_for_targets(params, project, mapping)
        project.targets.each do |target|
          target.build_configurations.each do |config|
            if params[:build_configurations] && !params[:build_configurations].include?(config.name)
              UI.verbose("Skipping #{config.name} not selected (#{params[:build_configurations].join(',')})")
              next
            end

            bundle_id = self.get_bundle_id(config)
            unless bundle_id
              UI.verbose("Skipping #{config.name} for #{target.name}, no bundle id")
              next
            end
            unless mapping.key?(bundle_id)
              UI.verbose("Skipping #{config.name} for #{target.name}, bundle id #{bundle_id} not included in match mapping")
              next
            end
            team_id = params[:team_id] || get_team_id(params, bundle_id)
            unless team_id
              UI.user_error!("No value provided for :team_id, and we were unable to automatically detect a value based on the provisioning profile.")
              return nil
            end
            code_sign_identity = params[:code_sign_identity] || self.get_code_sign_identity(params, bundle_id)
            unless code_sign_identity
              UI.user_error!("No value provided for :code_sign_identity, and we were unable to automatically detect a value based on the provisioning profile.")
              return nil
            end

            set_build_setting(config, CODE_SIGN_STYLE_KEY, CODE_SIGN_STYLE_VALUE)
            UI.verbose("Set Code Sign Style to: Manual for target: #{target.name} for build configuration: #{config.name}")

            set_build_setting(config, CODE_SIGN_IDENTITY_KEY, code_sign_identity)
            UI.verbose("Set Code Sign Identity to: #{code_sign_identity} for target: #{target.name} for build configuration: #{config.name}")

            set_build_setting(config, TEAM_ID_KEY, team_id)
            UI.verbose("Set Team ID to: #{params[:team_id]} for target: #{target.name} for build configuration: #{config.name}")

            set_build_setting(config, PROFILE_NAME_KEY, mapping[bundle_id])
            UI.verbose("Set Provisioning Profile Name to: #{mapping[bundle_id]} for target: #{target.name} for build configuration: #{config.name}")

            UI.important("Updated code signing values for target: #{target.name} for build configuration: #{config.name}")
          end
        end
        project.save
      end
      # rubocop:enable Metrics/PerceivedComplexity

      def self.set_build_setting(configuration, name, value)
        # Iterate over any keys that start with this name
        # This will also set keys that have filtering like [sdk=iphoneos*]
        keys = configuration.build_settings.keys.select { |key| key.to_s.match(/#{name}.*/) }
        keys.each do |key|
          configuration.build_settings[key] = value
        end

        # Explicitly set the key with value if keys don't exist
        configuration.build_settings[name] = value
      end

      def self.get_bundle_id(build_configuration)
        infoplist_path = build_configuration.build_settings[INFOPLIST_FILE_KEY]
        unless infoplist_path && File.exist?(infoplist_path)
          return nil
        end

        plist_data = Xcodeproj::Plist.read_from_path(infoplist_path)
        return plist_data[BUNDLE_ID_KEY]
      end

      def self.get_team_id(params, bundle_id)
        require 'match'
        env_variable_name = Match::Utils.environment_variable_name_profile_name(app_identifier: bundle_id,
                                                                                type: Match.profile_type_sym(params[:type]),
                                                                                platform: params[:platform])
        return ENV[env_variable_name]
      end

      def self.get_code_sign_identity(params, bundle_id)
        require 'match'
        require 'openssl'
        env_variable_name = Match::Utils.environment_variable_name_profile_path(app_identifier: bundle_id,
                                                                                type: Match.profile_type_sym(params[:type]),
                                                                                platform: params[:platform])
        path = ENV[env_variable_name]
        unless path && File.exist?(path)
          return nil
        end

        profile = FastlaneCore::ProvisioningProfile.parse(path)
        cert = OpenSSL::X509::Certificate.new(profile[CERTIFICATE_DATA_KEY].first.string)
        cert_info = cert.subject.to_s.gsub(/\s*subject=\s*/, "").tr("/", "\n")
        values = cert_info.split("\n")
                          .map { |x| x.split(/=+/) if x.include?("=") }
                          .compact
                          .to_h
        return values[CERTIFICATE_NAME_KEY]
      end
    end
  end
end

tejassharma96 avatar Apr 21 '22 20:04 tejassharma96

hey @lucgrabowski apologies for the tag, but was hoping to follow up on this - is this something you might be interested in including in fastlane? ofc it wouldn't look exactly like this since I'd be able to integrate it better with internal fastlane stuff, but something similar.

tejassharma96 avatar May 09 '22 01:05 tejassharma96

getting eyes on https://github.com/fastlane/fastlane/pull/20187 as an intermediary (so I can update get_code_sign_identity above) would also be great

tejassharma96 avatar May 09 '22 01:05 tejassharma96

Hi @tejassharma96, I really like the idea. @joshdholtz, could you take a look at the discussion and the code in the comment above and say what you think about such an action?

lucgrabowski avatar May 10 '22 14:05 lucgrabowski

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Please make sure to update to the latest fastlane version and check if that solves the issue. Let us know if that works for you by adding a comment :+1:

Friendly reminder: contributions are always welcome! Check out CONTRIBUTING.md for more information on how to help with fastlane and feel free to tackle this issue yourself :muscle:

This issue will be auto-closed if there is no reply within 1 month.

fastlane-bot avatar Jul 08 '22 17:07 fastlane-bot

pls no close fastlane bot. @joshdholtz have you had a chance to take a look at this?

tejassharma96 avatar Jul 08 '22 17:07 tejassharma96

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Please make sure to update to the latest fastlane version and check if that solves the issue. Let us know if that works for you by adding a comment :+1:

Friendly reminder: contributions are always welcome! Check out CONTRIBUTING.md for more information on how to help with fastlane and feel free to tackle this issue yourself :muscle:

This issue will be auto-closed if there is no reply within 1 month.

fastlane-bot avatar Sep 08 '22 17:09 fastlane-bot

is this just destined to be auto closed @lucgrabowski @joshdholtz ? I thought it was a cool idea 😓

tejassharma96 avatar Sep 08 '22 17:09 tejassharma96

Ah sorry! This got lost in my notifications â˜šī¸

I love this idea and I think it would be a great action to have! I did something similar in a few of my projects in the past. I'm totally happy to accept a PR for this 🙌

joshdholtz avatar Sep 08 '22 17:09 joshdholtz

awesome! glad to hear it :) I'll see if I can get something up this weekend!

tejassharma96 avatar Sep 09 '22 18:09 tejassharma96

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Please make sure to update to the latest fastlane version and check if that solves the issue. Let us know if that works for you by adding a comment :+1:

Friendly reminder: contributions are always welcome! Check out CONTRIBUTING.md for more information on how to help with fastlane and feel free to tackle this issue yourself :muscle:

This issue will be auto-closed if there is no reply within 1 month.

fastlane-bot avatar Oct 10 '22 17:10 fastlane-bot

sshhhhhh bot, we have a pull request

tejassharma96 avatar Oct 10 '22 21:10 tejassharma96