Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 36 additions & 46 deletions exportoptionsgenerator/codesign_group_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,32 @@ package exportoptionsgenerator
import (
"github.com/bitrise-io/go-utils/v2/log"
"github.com/bitrise-io/go-xcode/certificateutil"
"github.com/bitrise-io/go-xcode/export"
"github.com/bitrise-io/go-xcode/exportoptions"
"github.com/bitrise-io/go-xcode/profileutil"
codesigngroup "github.com/bitrise-io/go-xcode/v2/exportoptionsgenerator/internal/codesigngroup"
"github.com/bitrise-io/go-xcode/v2/plistutil"
)

// CodeSignGroupProvider ...
type CodeSignGroupProvider interface {
DetermineCodesignGroup(certificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel, defaultProfile *profileutil.ProvisioningProfileInfoModel, bundleIDEntitlementsMap map[string]plistutil.PlistData, exportMethod exportoptions.Method, teamID string, xcodeManaged bool) (*export.IosCodeSignGroup, error)
DetermineCodesignGroup(certificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel, defaultProfile *profileutil.ProvisioningProfileInfoModel, bundleIDEntitlementsMap map[string]plistutil.PlistData, exportMethod exportoptions.Method, teamID string, xcodeManaged bool) (*codesigngroup.Ios, error)
}

type codeSignGroupProvider struct {
logger log.Logger
logger log.Logger
printer *codesigngroup.Printer
}

// NewCodeSignGroupProvider ...
func NewCodeSignGroupProvider(logger log.Logger) CodeSignGroupProvider {
return &codeSignGroupProvider{logger: logger}
return &codeSignGroupProvider{
logger: logger,
printer: codesigngroup.NewPrinter(logger),
}
}

// DetermineCodesignGroup ....
func (g codeSignGroupProvider) DetermineCodesignGroup(certificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel, defaultProfile *profileutil.ProvisioningProfileInfoModel, bundleIDEntitlementsMap map[string]plistutil.PlistData, exportMethod exportoptions.Method, teamID string, xcodeManaged bool) (*export.IosCodeSignGroup, error) {
func (g codeSignGroupProvider) DetermineCodesignGroup(certificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel, defaultProfile *profileutil.ProvisioningProfileInfoModel, bundleIDEntitlementsMap map[string]plistutil.PlistData, exportMethod exportoptions.Method, teamID string, xcodeManaged bool) (*codesigngroup.Ios, error) {
g.logger.Println()
g.logger.Printf("Target Bundle ID - Entitlements map")
var bundleIDs []string
Expand All @@ -38,9 +42,6 @@ func (g codeSignGroupProvider) DetermineCodesignGroup(certificates []certificate
g.logger.Printf("%s: %s", bundleID, entitlementKeys)
}

g.logger.Println()
g.logger.Printf("Resolving CodeSignGroups...")

g.logger.Debugf("Installed certificates:")
for _, certInfo := range certificates {
g.logger.Debugf(certInfo.String())
Expand All @@ -51,76 +52,65 @@ func (g codeSignGroupProvider) DetermineCodesignGroup(certificates []certificate
g.logger.Debugf(profileInfo.String(certificates...))
}

g.logger.Printf("Resolving CodeSignGroups...")
codeSignGroups := export.CreateSelectableCodeSignGroups(certificates, profiles, bundleIDs)
g.logger.Println()
g.logger.Printf("Resolving code signing groups...")
codeSignGroups := codesigngroup.BuildFilterableList(certificates, profiles, bundleIDs)
if len(codeSignGroups) == 0 {
g.logger.Errorf("Failed to find code signing groups for specified export method (%s)", exportMethod)
}

g.logger.Debugf("\nGroups:")
for _, group := range codeSignGroups {
g.logger.Debugf(group.String())
}
g.logger.Debugf("%s", g.printer.ListToDebugString(codeSignGroups))

if len(bundleIDEntitlementsMap) > 0 {
g.logger.Warnf("Filtering CodeSignInfo groups for target capabilities")
g.logger.Printf("Filtering code signing groups for target capabilities")

codeSignGroups = export.FilterSelectableCodeSignGroups(codeSignGroups, export.CreateEntitlementsSelectableCodeSignGroupFilter(convertToV1PlistData(bundleIDEntitlementsMap)))
codeSignGroups = codesigngroup.Filter(codeSignGroups, codesigngroup.CreateEntitlementsSelectableCodeSignGroupFilter(convertToV1PlistData(bundleIDEntitlementsMap)))

g.logger.Debugf("\nGroups after filtering for target capabilities:")
for _, group := range codeSignGroups {
g.logger.Debugf(group.String())
}
g.logger.Debugf("%s", g.printer.ListToDebugString(codeSignGroups))
}

g.logger.Warnf("Filtering CodeSignInfo groups for export method")
g.logger.Printf("Filtering code signing groups for export method %s", exportMethod)

codeSignGroups = export.FilterSelectableCodeSignGroups(codeSignGroups, export.CreateExportMethodSelectableCodeSignGroupFilter(exportMethod))
codeSignGroups = codesigngroup.Filter(codeSignGroups, codesigngroup.CreateExportMethodSelectableCodeSignGroupFilter(exportMethod))

g.logger.Debugf("\nGroups after filtering for export method:")
for _, group := range codeSignGroups {
g.logger.Debugf(group.String())
}
g.logger.Debugf("%s", g.printer.ListToDebugString(codeSignGroups))

if teamID != "" {
g.logger.Warnf("ExportDevelopmentTeam specified: %s, filtering CodeSignInfo groups...", teamID)
g.logger.Printf("Development team specified: %s, filtering groups...", teamID)

codeSignGroups = export.FilterSelectableCodeSignGroups(codeSignGroups, export.CreateTeamSelectableCodeSignGroupFilter(teamID))
codeSignGroups = codesigngroup.Filter(codeSignGroups, codesigngroup.CreateTeamSelectableCodeSignGroupFilter(teamID))

g.logger.Debugf("\nGroups after filtering for team ID:")
for _, group := range codeSignGroups {
g.logger.Debugf(group.String())
}
g.logger.Debugf("%s", g.printer.ListToDebugString(codeSignGroups))
}

if !xcodeManaged {
g.logger.Warnf("App was signed with NON Xcode managed profile when archiving,\n" +
"only NOT Xcode managed profiles are allowed to sign when exporting the archive.\n" +
"Removing Xcode managed CodeSignInfo groups")
g.logger.Printf("App was signed with NON Xcode managed profile when archiving,\n" +
"only NON Xcode managed profiles are allowed to sign when exporting the archive.\n" +
"Removing Xcode managed code signing groups")

codeSignGroups = export.FilterSelectableCodeSignGroups(codeSignGroups, export.CreateNotXcodeManagedSelectableCodeSignGroupFilter())
codeSignGroups = codesigngroup.Filter(codeSignGroups, codesigngroup.CreateNotXcodeManagedSelectableCodeSignGroupFilter())

g.logger.Debugf("\nGroups after filtering for NOT Xcode managed profiles:")
for _, group := range codeSignGroups {
g.logger.Debugf(group.String())
}
g.logger.Debugf("\nGroups after filtering for NON Xcode managed profiles:")
g.logger.Debugf("%s", g.printer.ListToDebugString(codeSignGroups))
}

if teamID == "" && defaultProfile != nil {
g.logger.Debugf("\ndefault profile: %v\n", defaultProfile)
filteredCodeSignGroups := export.FilterSelectableCodeSignGroups(codeSignGroups,
export.CreateExcludeProfileNameSelectableCodeSignGroupFilter(defaultProfile.Name))
filteredCodeSignGroups := codesigngroup.Filter(codeSignGroups,
codesigngroup.CreateExcludeProfileNameSelectableCodeSignGroupFilter(defaultProfile.Name))
if len(filteredCodeSignGroups) > 0 {
codeSignGroups = filteredCodeSignGroups

g.logger.Printf("Removed default profile '%s' from code signing groups", defaultProfile.Name)
g.logger.Debugf("\nGroups after removing default profile:")
for _, group := range codeSignGroups {
g.logger.Debugf(group.String())
}
g.logger.Debugf("%s", g.printer.ListToDebugString(codeSignGroups))
}
}

var iosCodeSignGroups []export.IosCodeSignGroup
var iosCodeSignGroups []codesigngroup.Ios

for _, selectable := range codeSignGroups {
bundleIDProfileMap := map[string]profileutil.ProvisioningProfileInfoModel{}
Expand All @@ -132,7 +122,7 @@ func (g codeSignGroupProvider) DetermineCodesignGroup(certificates []certificate
}
}

iosCodeSignGroups = append(iosCodeSignGroups, *export.NewIOSGroup(selectable.Certificate, bundleIDProfileMap))
iosCodeSignGroups = append(iosCodeSignGroups, *codesigngroup.NewIOSGroup(selectable.Certificate, bundleIDProfileMap))
}

g.logger.Debugf("\nFiltered groups:")
Expand All @@ -144,12 +134,12 @@ func (g codeSignGroupProvider) DetermineCodesignGroup(certificates []certificate
}

if len(iosCodeSignGroups) < 1 {
g.logger.Errorf("Failed to find Codesign Groups")
g.logger.Errorf("Failed to find code signing groups")
return nil, nil
}

if len(iosCodeSignGroups) > 1 {
g.logger.Warnf("Multiple code signing groups found! Using the first code signing group")
g.logger.Warnf("Multiple code signing groups found! Using the first code signing group.")
}

return &iosCodeSignGroups[0], nil
Expand Down
6 changes: 3 additions & 3 deletions exportoptionsgenerator/exportoptionsgenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"slices"

"github.com/bitrise-io/go-utils/v2/log"
"github.com/bitrise-io/go-xcode/export"
"github.com/bitrise-io/go-xcode/exportoptions"
"github.com/bitrise-io/go-xcode/profileutil"
"github.com/bitrise-io/go-xcode/v2/exportoptionsgenerator/internal/codesigngroup"
"github.com/bitrise-io/go-xcode/v2/plistutil"
"github.com/bitrise-io/go-xcode/v2/xcodeversion"
)
Expand Down Expand Up @@ -128,7 +128,7 @@ func (g ExportOptionsGenerator) GenerateApplicationExportOptions(

// determineCodesignGroup finds the best codesign group (certificate + profiles)
// based on the installed Provisioning Profiles and Codesign Certificates.
func (g ExportOptionsGenerator) determineCodesignGroup(bundleIDEntitlementsMap map[string]plistutil.PlistData, exportMethod exportoptions.Method, teamID string, xcodeManaged bool) (*export.IosCodeSignGroup, error) {
func (g ExportOptionsGenerator) determineCodesignGroup(bundleIDEntitlementsMap map[string]plistutil.PlistData, exportMethod exportoptions.Method, teamID string, xcodeManaged bool) (*codesigngroup.Ios, error) {
certs, err := g.certificateProvider.ListCodesignIdentities()
if err != nil {
return nil, fmt.Errorf("failed to get installed certificates: %w", err)
Expand Down Expand Up @@ -268,7 +268,7 @@ func addTestFlightInternalTestingOnly(exportOpts exportoptions.ExportOptions, te
return exportOpts
}

func addManualSigningFields(exportOpts exportoptions.ExportOptions, codeSignGroup *export.IosCodeSignGroup, archivedWithXcodeManagedProfiles bool, logger log.Logger) exportoptions.ExportOptions {
func addManualSigningFields(exportOpts exportoptions.ExportOptions, codeSignGroup *codesigngroup.Ios, archivedWithXcodeManagedProfiles bool, logger log.Logger) exportoptions.ExportOptions {
exportCodeSignStyle := ""
exportProfileMapping := map[string]string{}

Expand Down
104 changes: 104 additions & 0 deletions exportoptionsgenerator/internal/codesigngroup/codesigngroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package codesigngroup

import (
"sort"

"github.com/bitrise-io/go-xcode/certificateutil"
"github.com/bitrise-io/go-xcode/profileutil"
"github.com/ryanuber/go-glob"
)

// CodeSignGroup ...
type CodeSignGroup interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this interface used?

Certificate() certificateutil.CertificateInfoModel
InstallerCertificate() *certificateutil.CertificateInfoModel
BundleIDProfileMap() map[string]profileutil.ProvisioningProfileInfoModel
}

// SelectableCodeSignGroup ...
type SelectableCodeSignGroup struct {
Certificate certificateutil.CertificateInfoModel
BundleIDProfilesMap map[string][]profileutil.ProvisioningProfileInfoModel
}

func containsCertificate(installedCertificates []certificateutil.CertificateInfoModel, certificate certificateutil.CertificateInfoModel) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I suggest using a bit more generic param names, maybe utilising slices.ContainsFunc and moving the function to the end og the file, as this is a private utility func (less important):

func containsCertificate(list []certificateutil.CertificateInfoModel, item certificateutil.CertificateInfoModel) bool {
	return slices.ContainsFunc(list, func(cert certificateutil.CertificateInfoModel) bool {
		return cert.Serial == item.Serial
	})
}

for _, cert := range installedCertificates {
if cert.Serial == certificate.Serial {
return true
}
}
return false
}

// BuildFilterableList ...
func BuildFilterableList(installedCertificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel, bundleIDs []string) []SelectableCodeSignGroup {
groups := []SelectableCodeSignGroup{}

serialToProfiles := map[string][]profileutil.ProvisioningProfileInfoModel{}
serialToCertificate := map[string]certificateutil.CertificateInfoModel{}
for _, profile := range profiles {
for _, certificate := range profile.DeveloperCertificates {
if !containsCertificate(installedCertificates, certificate) {
continue
}

certificateProfiles, ok := serialToProfiles[certificate.Serial]
if !ok {
certificateProfiles = []profileutil.ProvisioningProfileInfoModel{}
}
certificateProfiles = append(certificateProfiles, profile)
serialToProfiles[certificate.Serial] = certificateProfiles
serialToCertificate[certificate.Serial] = certificate
}
}

for serial, profiles := range serialToProfiles {
certificate := serialToCertificate[serial]

bundleIDToProfiles := map[string][]profileutil.ProvisioningProfileInfoModel{}
for _, bundleID := range bundleIDs {

matchingProfiles := []profileutil.ProvisioningProfileInfoModel{}
for _, profile := range profiles {
if !glob.Glob(profile.BundleID, bundleID) {
continue
}

matchingProfiles = append(matchingProfiles, profile)
}

if len(matchingProfiles) > 0 {
sort.Sort(ByBundleIDLength(matchingProfiles))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: this slice sort also could be simpler (and without the additional sort helper structure ByBundleIDLength):

slices.SortFunc(matchingProfiles, func(a, b profileutil.ProvisioningProfileInfoModel) int {
	return len(b.BundleID) - len(a.BundleID)
})

The slices.SortFunc comparator returns:

  • Negative if a should come before b
  • Positive if a should come after b
  • Zero if they're equal

Since we want to sort by descending length (longest first), we return len(b.BundleID) - len(a.BundleID).

bundleIDToProfiles[bundleID] = matchingProfiles
}
}

if len(bundleIDToProfiles) == len(bundleIDs) {
group := SelectableCodeSignGroup{
Certificate: certificate,
BundleIDProfilesMap: bundleIDToProfiles,
}
groups = append(groups, group)
}
}

return groups
}

// ByBundleIDLength ...
type ByBundleIDLength []profileutil.ProvisioningProfileInfoModel

// Len ..
func (s ByBundleIDLength) Len() int {
return len(s)
}

// Swap ...
func (s ByBundleIDLength) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

// Less ...
func (s ByBundleIDLength) Less(i, j int) bool {
return len(s[i].BundleID) > len(s[j].BundleID)
}
Loading