Skip to content

Commit bd31090

Browse files
authored
Fallback profiles fix (#279)
* Move export package to go-xcode@v2 * prepareManualAssets retruns signing assets * Revert "Move export package to go-xcode@v2" This reverts commit 5ef3991. * Share codesign group generator logic * Fix lint issues * Code cleanup and tests for createCodeSignAssetMap * Code cleanup * Update manual asset installation logs * Fix force code signing related logs * Remove duplicated certificate installation * Update TestManager_createCodeSignAssetMap test
1 parent ee433ea commit bd31090

File tree

5 files changed

+411
-153
lines changed

5 files changed

+411
-153
lines changed

autocodesign/projectmanager/projectmanager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func (p Project) ForceCodesignAssets(distribution autocodesign.DistributionType,
168168
archivableTargetsCounter++
169169
}
170170

171-
log.TInfof("Applied Bitrise managed codesigning on up to %s targets", archivableTargetsCounter)
171+
log.TInfof("Applied Bitrise managed codesigning on up to %d targets", archivableTargetsCounter)
172172

173173
devCodesignAssets, isDevelopmentAvailable := codesignAssetsByDistributionType[autocodesign.Development]
174174
if isDevelopmentAvailable && len(devCodesignAssets.UITestTargetProfilesByBundleID) != 0 {

codesign/codesign.go

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package codesign
22

33
import (
4+
"errors"
45
"fmt"
56
"time"
67

78
"github.com/bitrise-io/go-utils/v2/log"
89
"github.com/bitrise-io/go-xcode/certificateutil"
10+
"github.com/bitrise-io/go-xcode/exportoptions"
11+
"github.com/bitrise-io/go-xcode/plistutil"
12+
"github.com/bitrise-io/go-xcode/profileutil"
913
"github.com/bitrise-io/go-xcode/v2/autocodesign"
1014
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient"
1115
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
16+
"github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset"
1217
"github.com/bitrise-io/go-xcode/v2/autocodesign/projectmanager"
1318
"github.com/bitrise-io/go-xcode/v2/devportalservice"
19+
"github.com/bitrise-io/go-xcode/v2/exportoptionsgenerator"
1420
"github.com/bitrise-io/go-xcode/v2/xcarchive"
1521
)
1622

@@ -58,6 +64,7 @@ type Manager struct {
5864
fallbackProfileDownloader autocodesign.ProfileProvider
5965
assetInstaller autocodesign.AssetWriter
6066
localCodeSignAssetManager autocodesign.LocalCodeSignAssetManager
67+
profileConverter localcodesignasset.ProvisioningProfileConverter
6168

6269
detailsProvider DetailsProvider
6370
assetWriter AssetWriter
@@ -75,6 +82,7 @@ func NewManagerWithArchive(
7582
fallbackProfileDownloader autocodesign.ProfileProvider,
7683
assetInstaller autocodesign.AssetWriter,
7784
localCodeSignAssetManager autocodesign.LocalCodeSignAssetManager,
85+
profileConverter localcodesignasset.ProvisioningProfileConverter,
7886
archive xcarchive.IosArchive,
7987
logger log.Logger,
8088
) Manager {
@@ -87,6 +95,7 @@ func NewManagerWithArchive(
8795
fallbackProfileDownloader: fallbackProfileDownloader,
8896
assetInstaller: assetInstaller,
8997
localCodeSignAssetManager: localCodeSignAssetManager,
98+
profileConverter: profileConverter,
9099
detailsProvider: archive,
91100
logger: logger,
92101
}
@@ -102,6 +111,7 @@ func NewManagerWithProject(
102111
fallbackProfileDownloader autocodesign.ProfileProvider,
103112
assetInstaller autocodesign.AssetWriter,
104113
localCodeSignAssetManager autocodesign.LocalCodeSignAssetManager,
114+
profileConverter localcodesignasset.ProvisioningProfileConverter,
105115
project projectmanager.Project,
106116
logger log.Logger,
107117
) Manager {
@@ -114,6 +124,7 @@ func NewManagerWithProject(
114124
fallbackProfileDownloader: fallbackProfileDownloader,
115125
assetInstaller: assetInstaller,
116126
localCodeSignAssetManager: localCodeSignAssetManager,
127+
profileConverter: profileConverter,
117128
detailsProvider: project,
118129
assetWriter: project,
119130
logger: logger,
@@ -389,18 +400,23 @@ func (m *Manager) prepareCodeSigningWithBitrise(credentials devportalservice.Cre
389400
testDevicesToRegister = testDevices
390401
}
391402

392-
codesignAssetsByDistributionType, err := m.prepareAutomaticAssets(credentials, appLayout, typeToLocalCerts, testDevicesToRegister)
393-
if err != nil {
403+
codesignAssetsByDistributionType, autoCodesignErr := m.prepareAutomaticAssets(credentials, appLayout, typeToLocalCerts, testDevicesToRegister)
404+
if autoCodesignErr != nil {
394405
if !m.fallbackProfileDownloader.IsAvailable() {
395-
return err
406+
return autoCodesignErr
396407
}
397408

398409
m.logger.Println()
399-
m.logger.Warnf("Automatic code signing failed: %s", err)
410+
m.logger.Warnf("Automatic code signing failed: %s", autoCodesignErr)
400411
m.logger.Println()
401412
m.logger.Infof("Falling back to manually managed codesigning assets.")
402413

403-
return m.prepareManualAssets(certs)
414+
codesignAssetsByDistributionType, err = m.prepareManualAssets(appLayout, typeToLocalCerts)
415+
if err != nil {
416+
m.logger.Println()
417+
m.logger.Warnf("Manual code signing failed: %s", err)
418+
return autoCodesignErr
419+
}
404420
}
405421

406422
if m.assetWriter != nil {
@@ -438,24 +454,100 @@ func (m *Manager) prepareAutomaticAssets(credentials devportalservice.Credential
438454
return codesignAssets, nil
439455
}
440456

441-
func (m *Manager) prepareManualAssets(certificates []certificateutil.CertificateInfoModel) error {
442-
if err := m.installCertificates(certificates); err != nil {
443-
return err
457+
func (m *Manager) prepareManualAssets(appLayout autocodesign.AppLayout, typeToLocalCerts autocodesign.LocalCertificates) (map[autocodesign.DistributionType]autocodesign.AppCodesignAssets, error) {
458+
var certificates []certificateutil.CertificateInfoModel
459+
for _, certs := range typeToLocalCerts {
460+
certificates = append(certificates, certs...)
461+
}
462+
463+
installedCerts, installedProfiles, err := m.installManualCodeSigningAssets(certificates)
464+
if err != nil {
465+
return nil, fmt.Errorf("failed to install manual code signing assets: %w", err)
466+
}
467+
468+
assets, err := m.createCodeSignAssetMap(appLayout, installedCerts, installedProfiles)
469+
if err != nil {
470+
return nil, fmt.Errorf("failed to create code signing asset map: %w", err)
471+
}
472+
473+
return assets, nil
474+
}
475+
476+
func (m *Manager) createCodeSignAssetMap(appLayout autocodesign.AppLayout, certificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel) (map[autocodesign.DistributionType]autocodesign.AppCodesignAssets, error) {
477+
provider := exportoptionsgenerator.NewCodeSignGroupProvider(m.logger)
478+
479+
bundleIDEntitlementsMap := map[string]plistutil.PlistData{}
480+
for bundleID, entitlements := range appLayout.EntitlementsByArchivableTargetBundleID {
481+
bundleIDEntitlementsMap[bundleID] = plistutil.PlistData(entitlements)
482+
}
483+
484+
distributionTypes := []autocodesign.DistributionType{m.opts.ExportMethod}
485+
if m.opts.ExportMethod != autocodesign.Development {
486+
// Add development distribution type if the selected export method is not development
487+
distributionTypes = append(distributionTypes, autocodesign.Development)
488+
}
489+
490+
assetsByDistributionType := map[autocodesign.DistributionType]autocodesign.AppCodesignAssets{}
491+
for _, distributionType := range distributionTypes {
492+
signingAssets, err := provider.DetermineCodesignGroup(certificates, profiles, nil, bundleIDEntitlementsMap, exportoptions.Method(distributionType), m.opts.TeamID, true)
493+
if err != nil || signingAssets == nil {
494+
if err == nil {
495+
err = errors.New("no signing assets found")
496+
}
497+
if distributionType == m.opts.ExportMethod {
498+
return nil, fmt.Errorf("failed to determine codesign group for %s distribution: %w", distributionType, err)
499+
}
500+
m.logger.Warnf("Failed to determine codesign group for %s distribution: %s, skipping", distributionType, err)
501+
continue
502+
}
503+
504+
bundleIDProfileInfoMap := signingAssets.BundleIDProfileMap()
505+
bundleIDProfileMap := map[string]autocodesign.Profile{}
506+
for bundleID, profileInfo := range bundleIDProfileInfoMap {
507+
signingProfile, err := m.profileConverter.ProfileInfoToProfile(profileInfo)
508+
if err != nil {
509+
return nil, fmt.Errorf("failed to convert profile info: %w", err)
510+
}
511+
bundleIDProfileMap[bundleID] = signingProfile
512+
}
513+
514+
assetsByDistributionType[distributionType] = autocodesign.AppCodesignAssets{
515+
Certificate: signingAssets.Certificate(),
516+
ArchivableTargetProfilesByBundleID: bundleIDProfileMap,
517+
}
518+
}
519+
520+
// TODO: UI test targets are not supported yet
521+
522+
return assetsByDistributionType, nil
523+
524+
}
525+
526+
func (m *Manager) installManualCodeSigningAssets(certificates []certificateutil.CertificateInfoModel) ([]certificateutil.CertificateInfoModel, []profileutil.ProvisioningProfileInfoModel, error) {
527+
m.logger.Printf("Installing %d certificate(s):", len(certificates))
528+
for _, cert := range certificates {
529+
m.logger.Printf("- %s", cert.String())
530+
// Empty passphrase provided, as already parsed certificate + private key
531+
if err := m.assetInstaller.InstallCertificate(cert); err != nil {
532+
return nil, nil, err
533+
}
444534
}
445535

446536
profiles, err := m.fallbackProfileDownloader.GetProfiles()
447537
if err != nil {
448-
return fmt.Errorf("failed to fetch profiles: %w", err)
538+
return certificates, nil, fmt.Errorf("failed to fetch profiles: %w", err)
449539
}
450540

451-
m.logger.Printf("Installing manual profiles:")
541+
m.logger.Printf("Installing %d profile(s)...", len(profiles))
542+
var installedProfiles []profileutil.ProvisioningProfileInfoModel
452543
for _, profile := range profiles {
453-
m.logger.Printf("%s", profile.Info.String(certificates...))
454-
544+
m.logger.Printf("- UUID: %s, Name: %s, Export type: %s, Team: %s, Expiry: %s", profile.Info.UUID, profile.Info.Name, profile.Info.ExportType, profile.Info.TeamID, profile.Info.ExpirationDate)
455545
if err := m.assetInstaller.InstallProfile(profile.Profile); err != nil {
456-
return fmt.Errorf("failed to install profile: %w", err)
546+
return certificates, installedProfiles, fmt.Errorf("failed to install profile: %w", err)
457547
}
548+
549+
installedProfiles = append(installedProfiles, profile.Info)
458550
}
459551

460-
return nil
552+
return certificates, installedProfiles, nil
461553
}

codesign/codesign_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import (
1111
"github.com/bitrise-io/go-steputils/v2/stepconf"
1212
"github.com/bitrise-io/go-utils/v2/log"
1313
"github.com/bitrise-io/go-xcode/certificateutil"
14+
"github.com/bitrise-io/go-xcode/exportoptions"
15+
"github.com/bitrise-io/go-xcode/profileutil"
1416
"github.com/bitrise-io/go-xcode/v2/autocodesign"
17+
"github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset"
18+
localcodesignassetMocks "github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/mocks"
1519
"github.com/bitrise-io/go-xcode/v2/codesign/mocks"
1620
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1721
"github.com/stretchr/testify/mock"
@@ -391,3 +395,132 @@ func TestSelectConnectionCredentials(t *testing.T) {
391395
})
392396
}
393397
}
398+
399+
func TestManager_createCodeSignAssetMap(t *testing.T) {
400+
bundleID := "com.example.app"
401+
teamID := "team_1"
402+
certificate := certificateutil.CertificateInfoModel{
403+
Serial: "serial_1",
404+
TeamID: teamID,
405+
}
406+
profile := profileutil.ProvisioningProfileInfoModel{
407+
DeveloperCertificates: []certificateutil.CertificateInfoModel{certificate},
408+
BundleID: bundleID,
409+
TeamID: teamID,
410+
ExportType: exportoptions.MethodDevelopment,
411+
Type: profileutil.ProfileTypeIos,
412+
}
413+
localProfile := localcodesignasset.NewProfile(profile, nil)
414+
415+
tests := []struct {
416+
name string
417+
appLayout autocodesign.AppLayout
418+
certificates []certificateutil.CertificateInfoModel
419+
profiles []profileutil.ProvisioningProfileInfoModel
420+
421+
opts Opts
422+
423+
want map[autocodesign.DistributionType]autocodesign.AppCodesignAssets
424+
wantErr string
425+
}{
426+
{
427+
name: "Creates codesign assets map with single development profile",
428+
appLayout: autocodesign.AppLayout{
429+
EntitlementsByArchivableTargetBundleID: map[string]autocodesign.Entitlements{
430+
bundleID: nil,
431+
},
432+
},
433+
certificates: []certificateutil.CertificateInfoModel{certificate},
434+
profiles: []profileutil.ProvisioningProfileInfoModel{profile},
435+
opts: Opts{
436+
ExportMethod: autocodesign.Development,
437+
TeamID: teamID,
438+
},
439+
want: map[autocodesign.DistributionType]autocodesign.AppCodesignAssets{
440+
autocodesign.Development: {
441+
ArchivableTargetProfilesByBundleID: map[string]autocodesign.Profile{
442+
bundleID: localProfile,
443+
},
444+
UITestTargetProfilesByBundleID: map[string]autocodesign.Profile(nil),
445+
Certificate: certificate,
446+
},
447+
},
448+
},
449+
{
450+
name: "Throws an error when no code signing assets are found for the provided Developer Team",
451+
appLayout: autocodesign.AppLayout{
452+
EntitlementsByArchivableTargetBundleID: map[string]autocodesign.Entitlements{
453+
bundleID: {},
454+
},
455+
},
456+
certificates: []certificateutil.CertificateInfoModel{certificate},
457+
profiles: []profileutil.ProvisioningProfileInfoModel{profile},
458+
opts: Opts{
459+
ExportMethod: autocodesign.Development,
460+
TeamID: "team_2",
461+
},
462+
wantErr: "failed to determine codesign group for development distribution: no signing assets found",
463+
},
464+
{
465+
name: "Known project entitlements are filtering the profiles",
466+
appLayout: autocodesign.AppLayout{
467+
EntitlementsByArchivableTargetBundleID: map[string]autocodesign.Entitlements{
468+
bundleID: {"com.apple.developer.in-app-payments": "true"},
469+
},
470+
},
471+
certificates: []certificateutil.CertificateInfoModel{certificate},
472+
profiles: []profileutil.ProvisioningProfileInfoModel{profile},
473+
opts: Opts{
474+
ExportMethod: autocodesign.Development,
475+
TeamID: teamID,
476+
},
477+
wantErr: "failed to determine codesign group for development distribution: no signing assets found",
478+
},
479+
{
480+
name: "Unknown project entitlements are not filtering the profiles",
481+
appLayout: autocodesign.AppLayout{
482+
EntitlementsByArchivableTargetBundleID: map[string]autocodesign.Entitlements{
483+
bundleID: {"key1": "value1"},
484+
},
485+
},
486+
certificates: []certificateutil.CertificateInfoModel{certificate},
487+
profiles: []profileutil.ProvisioningProfileInfoModel{profile},
488+
opts: Opts{
489+
ExportMethod: autocodesign.Development,
490+
TeamID: teamID,
491+
},
492+
want: map[autocodesign.DistributionType]autocodesign.AppCodesignAssets{
493+
autocodesign.Development: {
494+
ArchivableTargetProfilesByBundleID: map[string]autocodesign.Profile{
495+
bundleID: localProfile,
496+
},
497+
UITestTargetProfilesByBundleID: map[string]autocodesign.Profile(nil),
498+
Certificate: certificate,
499+
},
500+
},
501+
},
502+
}
503+
504+
for _, tt := range tests {
505+
t.Run(tt.name, func(t *testing.T) {
506+
logger := log.NewLogger()
507+
profileConverter := new(localcodesignassetMocks.ProvisioningProfileConverter)
508+
profileConverter.On("ProfileInfoToProfile", profile).Return(localProfile, nil)
509+
510+
m := &Manager{
511+
opts: tt.opts,
512+
profileConverter: profileConverter,
513+
logger: logger,
514+
}
515+
516+
got, err := m.createCodeSignAssetMap(tt.appLayout, tt.certificates, tt.profiles)
517+
if tt.wantErr != "" {
518+
require.EqualError(t, err, tt.wantErr)
519+
require.Nil(t, got)
520+
} else {
521+
require.NoError(t, err)
522+
require.Equal(t, tt.want, got)
523+
}
524+
})
525+
}
526+
}

0 commit comments

Comments
 (0)