Skip to content

profileutil v2 migration#304

Open
godrei wants to merge 38 commits intomasterfrom
profileutil-v2
Open

profileutil v2 migration#304
godrei wants to merge 38 commits intomasterfrom
profileutil-v2

Conversation

@godrei
Copy link
Copy Markdown
Contributor

@godrei godrei commented Jan 29, 2026

profileutil V1→V2 Migration Summary

V1→V2 Structural Transformation

Core Types: No Changes

The primary data structures remain identical between v1 and v2:

ProvisioningProfileInfoModel

// V1 and V2 - Structure unchanged
type ProvisioningProfileInfoModel struct {
    UUID                  string
    Name                  string
    TeamName              string
    TeamID                string
    BundleID              string
    ExportType            exportoptions.Method
    ProvisionedDevices    []string
    DeveloperCertificates []certificateutil.CertificateInfoModel
    CreationDate          time.Time
    ExpirationDate        time.Time
    Entitlements          plistutil.PlistData
    ProvisionsAllDevices  bool
    Type                  ProfileType
}

PlistData

// V1 and V2 - Type alias unchanged
type PlistData plistutil.PlistData

ProfileType Constants

// V1 and V2 - Constants unchanged
const (
    ProfileTypeIos   ProfileType = "ios"
    ProfileTypeMacOs ProfileType = "osx"
    ProfileTypeTvOs  ProfileType = "tvos"
)

Key Architectural Changes

The migration introduces struct-based APIs replacing v1's function-based approach, following go-utils v2 patterns for better testability and dependency injection.

V1→V2 Function Migration

1. Profile Reading Functions → ProfileReader Struct

V1: Package-level functions

// v1/profileutil/util.go
func ProvisioningProfileFromFile(pth string) (*pkcs7.PKCS7, error)
func InstalledProvisioningProfiles(profileType ProfileType) ([]*pkcs7.PKCS7, error)
func FindProvisioningProfile(uuid string) (*pkcs7.PKCS7, string, error)
func ProvisioningProfilesDirPath(xcodeMajorVersion int64) (string, error)

// v1/profileutil/info_model.go
func NewProvisioningProfileInfoFromFile(pth string) (ProvisioningProfileInfoModel, error)
func InstalledProvisioningProfileInfos(profileType ProfileType) ([]ProvisioningProfileInfoModel, error)
func FindProvisioningProfileInfo(uuid string) (ProvisioningProfileInfoModel, string, error)

V2: ProfileReader struct with injected dependencies

// v2/profileutil/profile_reader.go
type ProfileReader struct {
    logger       log.Logger            // v2 logger
    fileManager  fileutil.FileManager  // v2 file operations
    pathModifier pathutil.PathModifier // v2 path utilities
    pathProvider pathutil.PathProvider // v2 glob + temp dir operations
}

func NewProfileReader(logger log.Logger, fileManager fileutil.FileManager, pathModifier pathutil.PathModifier, pathProvider pathutil.PathProvider) ProfileReader

// Public methods
func (r ProfileReader) ProvisioningProfileInfoFromFile(pth string) (ProvisioningProfileInfoModel, error)
func (r ProfileReader) InstalledProvisioningProfileInfos(profileType ProfileType) ([]ProvisioningProfileInfoModel, error)
func (r ProfileReader) ListProfiles(profileType ProfileType, uuid string) ([]string, error)
func (r ProfileReader) ProvisioningProfilesDirPath(xcodeMajorVersion int64) (string, error)

// Private helpers (no longer exposed)
func (r ProfileReader) provisioningProfileFromFile(pth string) (*pkcs7.PKCS7, error)
func (r ProfileReader) installedProvisioningProfiles(profileType ProfileType) ([]*pkcs7.PKCS7, error)

Changes:

  • NewProvisioningProfileInfoFromFile()ProfileReader.ProvisioningProfileInfoFromFile()
  • InstalledProvisioningProfileInfos()ProfileReader.InstalledProvisioningProfileInfos()
  • ProvisioningProfilesDirPath()ProfileReader.ProvisioningProfilesDirPath()
  • ✅ New: ProfileReader.ListProfiles() — searches both modern (Xcode 16+) and legacy profile directories by type and UUID glob
  • FindProvisioningProfileInfo()Removed (unused by consumers)
  • 🔒 ProvisioningProfileFromFile() → private provisioningProfileFromFile()
  • 🔒 InstalledProvisioningProfiles() → private installedProvisioningProfiles()
  • 🔒 FindProvisioningProfile()Removed (unused)

2. Profile Printing Functions → ProfilePrinter Struct

V1: Method on ProvisioningProfileInfoModel

// v1/profileutil/info_model.go
func (info ProvisioningProfileInfoModel) String(installedCertificates ...certificateutil.CertificateInfoModel) string

V2: ProfilePrinter struct

// v2/profileutil/profile_printer.go
type ProfilePrinter struct {
    logger       log.Logger
    timeProvider TimeProvider
}

func NewProfilePrinter(logger log.Logger, timeProvider TimeProvider) *ProfilePrinter
func (p ProfilePrinter) PrintableProfile(profile ProvisioningProfileInfoModel, installedCertificates ...certificateutil.CertificateInfoModel) string

Changes:

  • info.String(certs...)printer.PrintableProfile(info, certs...)
  • Extracted from model method to separate printer struct
  • Added TimeProvider dependency for testable time-based validation
  • Uses v2 logger instead of v1's global log package

3. Model Constructor Functions

Changes:

  • NewProvisioningProfileInfo(pkcs7.PKCS7) — signature unchanged
  • Platform detection logic moved from constructor to PlistData.GetProfileType()
  • Better separation of concerns

V2 Addition:

// New helper — parses PKCS7 bytes and constructs the info model in one step
func NewProvisioningProfileInfoFromPKCS7Content(content []byte) (ProvisioningProfileInfoModel, error)

4. Model Validation Methods

V1: Uses global time.Now()

// v1/profileutil/info_model.go
func (info ProvisioningProfileInfoModel) CheckValidity() error {
    timeNow := time.Now()  // Global time - hard to test
    if !timeNow.Before(info.ExpirationDate) {
        return fmt.Errorf("Provisioning Profile is not valid anymore - validity ended at: %s", info.ExpirationDate)
    }
    return nil
}

V2: Injected time function for testability

// v2/profileutil/info_model.go
func (info ProvisioningProfileInfoModel) CheckValidity(currentTime func() time.Time) error {
    timeNow := currentTime()  // Injected — easy to control in tests
    if !timeNow.Before(info.ExpirationDate) {
        return fmt.Errorf("provisioning profile is not valid anymore, validity ended at: %s", info.ExpirationDate)
    }
    return nil
}

Changes:

  • Method now accepts currentTime func() time.Time parameter
  • Enables time-based testing without mocking global state
  • Use profileutil.DefaultTimeProvider{}.Now in production code

Unchanged Model Methods:

// Both V1 and V2 - No changes
func (info ProvisioningProfileInfoModel) IsXcodeManaged() bool
func (info ProvisioningProfileInfoModel) HasInstalledCertificate(installedCertificates []certificateutil.CertificateInfoModel) bool

5. PlistData Methods

V1 and V2: Core methods unchanged

func (profile PlistData) GetUUID() string
func (profile PlistData) GetName() string
func (profile PlistData) GetApplicationIdentifier() string
func (profile PlistData) GetBundleIdentifier() string
func (profile PlistData) GetExportMethod() exportoptions.Method
func (profile PlistData) GetProvisionedDevices() []string
func (profile PlistData) GetTeamName() string
func (profile PlistData) GetTeamID() string
func (profile PlistData) GetCreationDate() time.Time
func (profile PlistData) GetExpirationDate() time.Time
func (profile PlistData) GetProvisionsAllDevices() bool
func (profile PlistData) GetEntitlements() plistutil.PlistData
func (profile PlistData) GetDeveloperCertificates() [][]byte

V2 Additions: Extracted from NewProvisioningProfileInfo constructor

// V2 only - platform detection extracted from v1's NewProvisioningProfileInfo
func (profile PlistData) GetProfileType() (ProfileType, error)

// V2 only - certificate parsing extracted from v1's NewProvisioningProfileInfo
func (profile PlistData) GetDeveloperCertificateInfo() []certificateutil.CertificateInfoModel

V1 Function Removed:

// V1 only - removed in V2
func NewPlistDataFromFile(provisioningProfilePth string) (PlistData, error)

Functionality replaced by ProfileReader.ProvisioningProfileInfoFromFile()

6. Utility Functions

V1 and V2: Unchanged

func IsXcodeManaged(profileName string) bool
func MatchTargetAndProfileEntitlements(targetEntitlements, profileEntitlements plistutil.PlistData, profileType ProfileType) []string
var KnownProfileCapabilitiesMap = map[ProfileType]map[string]bool{...}

7. Time Provider (V2 Addition)

New in V2:

// v2/profileutil/time_provider.go

// TimeProvider is an interface for injectable time access.
type TimeProvider interface {
    Now() time.Time
}

// DefaultTimeProvider is the production implementation.
type DefaultTimeProvider struct{}

func (DefaultTimeProvider) Now() time.Time { return time.Now() }

Used for testable time-dependent operations (e.g., CheckValidity, ProfilePrinter).

Known Issues / TODOs

  • NewProvisioningProfileConverter() and NewProvisioningProfileProvider() in localcodesignasset still wire their own dependencies internally instead of accepting them as constructor parameters. This is a partial migration — the structs hold the deps as fields, but injection is not yet complete. Tracked via TODO comments in those files.
  • xcarchive.NewIosBaseApplication and xcarchive.newMacosBaseApplication construct ProfileReader inline rather than receiving it as a struct dependency. Tracked via // TODO: wire in as a dep on the struct comments.

godrei added 11 commits January 29, 2026 11:04
# Conflicts:
#	autocodesign/localcodesignasset/profilelookup.go
#	autocodesign/profiles.go
# Conflicts:
#	autocodesign/profiledownloader/profiledownloader.go
#	autocodesign/profiles.go
#	go.mod
#	go.sum
#	mocks/PathModifier.go
#	mocks/PathProvider.go
@godrei godrei changed the title profileutil v2 init profileutil v2 migration Jan 30, 2026
@godrei godrei marked this pull request as ready for review January 30, 2026 16:10
Comment thread profileutil/profile_printer.go Outdated
Comment thread profileutil/profile_reader.go Outdated
Comment thread profileutil/profile_reader.go Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Migrates provisioning profile utilities and their consumers from profileutil v1’s package-level functions to the v2 struct-based, dependency-injected API (aligned with go-utils/v2), adding new helpers and tests to support the new architecture.

Changes:

  • Introduces v2 profileutil components (ProfileReader, ProfilePrinter, TimeProvider, expanded PlistData helpers) and comprehensive unit tests.
  • Updates multiple consumers (xcarchive, autocodesign, exportoptionsgenerator, codesign, artifacts, integration tests) to use go-xcode/v2/profileutil.
  • Refactors export options generator and codesign asset writer wiring to use injected v2 dependencies; removes the legacy plist conversion helper.

Reviewed changes

Copilot reviewed 48 out of 48 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
xcarchive/macos.go Switches to v2 profileutil and constructs a ProfileReader for reading embedded profiles.
xcarchive/ios.go Switches to v2 profileutil and uses ProfileReader.ProvisioningProfileInfoFromFile.
xcarchive/ios_test.go Updates imports to v2 profileutil.
profileutil/util.go Adds v2 utility helpers (KnownProfileCapabilitiesMap, IsXcodeManaged, entitlement matching).
profileutil/util_test.go Adds unit tests for IsXcodeManaged and entitlement matching.
profileutil/time_provider.go Adds injectable TimeProvider + DefaultTimeProvider.
profileutil/testdata_test.go Adds provisioning profile plist fixtures for tests.
profileutil/profile_reader.go Adds ProfileReader (DI-based) to read/list installed provisioning profiles across legacy/modern dirs.
profileutil/profile_reader_test.go Adds tests for ProfileReader behaviors and error propagation.
profileutil/profile_printer.go Adds ProfilePrinter to render profile info as JSON with validity/cert diagnostics.
profileutil/profile_printer_test.go Adds tests validating printer JSON output and error scenarios.
profileutil/plist_data.go Adds PlistData helpers (platform/type detection, export method, cert parsing, etc.).
profileutil/plist_data_test.go Adds tests for PlistData parsing/type detection/export method logic.
profileutil/info_model.go Adds v2 ProvisioningProfileInfoModel + constructors, validity and certificate checks.
profileutil/info_model_test.go Adds tests for model constructors and methods, including time-injected validity.
mocks/PathProvider.go Regenerates mock (mockery version bump).
mocks/PathModifier.go Regenerates mock (mockery version bump).
go.mod Promotes github.com/fullsailor/pkcs7 to a direct dependency.
exportoptionsgenerator/profiles.go Refactors profile provider to use injected ProfileReader and v2 pathutil.PathProvider.
exportoptionsgenerator/plistconverter.go Removes v1 plist conversion helper no longer needed after v2 migration.
exportoptionsgenerator/internal/codesigngroup/printer_test.go Updates imports to v2 profileutil.
exportoptionsgenerator/internal/codesigngroup/ios.go Updates imports to v2 profileutil.
exportoptionsgenerator/internal/codesigngroup/filter.go Updates imports to v2 plistutil/profileutil and uses v2 entitlements map.
exportoptionsgenerator/internal/codesigngroup/codesigngroup_test.go Updates imports to v2 profileutil.
exportoptionsgenerator/internal/codesigngroup/codesigngroup.go Updates imports to v2 profileutil.
exportoptionsgenerator/exportoptionsgenerator_test.go Updates imports to v2 profileutil.
exportoptionsgenerator/exportoptionsgenerator.go Wires v2 ProfileReader/providers in generator constructor.
exportoptionsgenerator/codesign_group_provider.go Replaces info.String() with v2 ProfilePrinter output; updates filtering to v2 entitlements.
exportoptionsgenerator/certificates.go Adds a constructor for LocalCodesignIdentityProvider used in v2 wiring.
codesign/inputparse.go Updates imports to v2 profileutil.
codesign/codesign_test.go Updates imports to v2 profileutil.
codesign/codesign.go Updates imports to v2 profileutil.
autocodesign/profiles.go Switches raw profile parsing to NewProvisioningProfileInfoFromPKCS7Content.
autocodesign/profiledownloader/profiledownloader.go Switches downloaded profile parsing to NewProvisioningProfileInfoFromPKCS7Content.
autocodesign/localcodesignasset/profileprovider.go Refactors provider to use an internal v2 ProfileReader.
autocodesign/localcodesignasset/profilelookup.go Updates imports to v2 profileutil.
autocodesign/localcodesignasset/profilelookup_test.go Updates imports to v2 profileutil.
autocodesign/localcodesignasset/profileconverter.go Refactors profile lookup/reading to v2 ProfileReader + v2 file operations.
autocodesign/localcodesignasset/profile.go Updates imports to v2 profileutil and platform mapping logic.
autocodesign/localcodesignasset/mocks/ProvisioningProfileProvider.go Updates mock import path to v2 profileutil.
autocodesign/localcodesignasset/mocks/ProvisioningProfileConverter.go Regenerates mock with updated import path and mockery version.
autocodesign/localcodesignasset/localcodesignasset_test.go Updates imports to v2 profileutil.
autocodesign/example_test.go Updates example wiring to provide a v2 ProfileReader to the asset writer.
autocodesign/codesignasset/writer.go Updates writer to accept a v2 ProfileReader and use it for dir path selection.
autocodesign/codesignasset/writer_test.go Updates tests to provide a v2 ProfileReader to the writer.
autocodesign/autocodesign.go Updates imports to v2 profileutil.
artifacts/ipa_reader.go Switches embedded profile parsing to NewProvisioningProfileInfoFromPKCS7Content.
_integration_tests/zip/ipa_reader_test.go Updates imports to v2 profileutil.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread profileutil/util.go
Comment on lines +69 to +71
for key := range targetEntitlements {
_, known := KnownProfileCapabilitiesMap[profileType][key]
if !known {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in: 95692e0

profile, err := c.fileManager.Open(pth)
if err != nil {
return nil, err
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in: f48b3b4

Comment thread profileutil/info_model.go
Comment on lines +99 to +106
has := false
for _, certificate := range info.DeveloperCertificates {
for _, installedCertificate := range installedCertificates {
if certificate.Serial == installedCertificate.Serial {
has = true
break
}
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in: 1eb7696

}
}()

content, err := io.ReadAll(f)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need to open and read contents, we could do os.ReadFile or a mocked alternative.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately we don't have a ReadFile utility function, os.ReadFile is something we shouldn't call directly. So I went with the easiest path: FileManager.Open + io.ReadAll.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants