From 55553b08827faf2cadfeb3fb91584cac7fedcd4a Mon Sep 17 00:00:00 2001 From: lpusok <7979773+lpusok@users.noreply.github.com> Date: Tue, 11 Jun 2019 15:59:23 +0200 Subject: [PATCH] Exported functions (#105) * Create exported functions to use codesigndoc from package * Move to function ExportCodesigningFiles export logic * Improve logging * Move log file writing to a closure * Rename GenerateXcodeArchive to BuildXcodeArchive --- cmd/xamarin.go | 38 ++++++++++++------------ cmd/xcode.go | 64 ++++++++++++---------------------------- cmd/xcodeUITests.go | 36 +++++++++++++---------- codesign/export.go | 43 +++++++++++++++------------ codesigndoc/xcode.go | 69 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+), 97 deletions(-) create mode 100644 codesigndoc/xcode.go diff --git a/cmd/xamarin.go b/cmd/xamarin.go index 60d22cf0..667c215c 100644 --- a/cmd/xamarin.go +++ b/cmd/xamarin.go @@ -161,34 +161,31 @@ func scanXamarinProject(cmd *cobra.Command, args []string) error { fmt.Println() fmt.Println() log.Printf(`🔦 Running a Build, to get all the required code signing settings...`) - var isLogFileWritten bool logOutputFilePath := filepath.Join(absExportOutputDirPath, "xamarin-build-output.log") archivePath, logOutput, err := xamarinCmd.GenerateArchive() - if writeFiles == codesign.WriteFilesAlways || - writeFiles == codesign.WriteFilesFallback && err != nil { // save the xamarin output into a debug log file + if writeFiles == codesign.WriteFilesAlways || writeFiles == codesign.WriteFilesFallback && err != nil { // save the xamarin output into a debug log file if err := os.MkdirAll(absExportOutputDirPath, 0700); err != nil { return fmt.Errorf("failed to create output directory, error: %s", err) } + log.Infof("💡 "+colorstring.Yellow("Saving xamarin output into file")+": %s", logOutputFilePath) if err := fileutil.WriteStringToFile(logOutputFilePath, logOutput); err != nil { log.Errorf("Failed to save xamarin build output into file (%s), error: %s", logOutputFilePath, err) - } else { - isLogFileWritten = true } } if err != nil { - log.Warnf("Last lines of build log:") + log.Warnf("Last lines of the build log:") fmt.Println(stringutil.LastNLines(logOutput, 15)) + + log.Infof(colorstring.Yellow("Please check the build log to see what caused the error.")) fmt.Println() - if isLogFileWritten { - log.Warnf("Please check the logfile (%s) to see what caused the error", logOutputFilePath) - log.Warnf(`and make sure that you can "Archive for Publishing" this project from Xamarin!`) - fmt.Println() - log.Infof("Open the project: %s", xamarinCmd.SolutionFilePath) - log.Infof(`And do "Archive for Publishing", after selecting the Configuration+Platform: %s|%s`, xamarinCmd.Configuration, xamarinCmd.Platform) - fmt.Println() - } + + log.Errorf("Build failed.") + log.Infof(colorstring.Yellow("Open the project: ")+"%s", xamarinCmd.SolutionFilePath) + log.Infof(colorstring.Yellow(`And do "Archive for Publishing", after selecting the Configuration+Platform: `)+"%s|%s", xamarinCmd.Configuration, xamarinCmd.Platform) + fmt.Println() + return ArchiveError{toolXamarin, "failed to run xamarin build command: " + err.Error()} } @@ -197,9 +194,14 @@ func scanXamarinProject(cmd *cobra.Command, args []string) error { if err != nil { return err } - exoprtResult, err := codesign.UploadAndWriteCodesignFiles(certificatesToExport, - profilesToExport, - isAskForPassword, + + certificates, profiles, err := codesign.ExportCodesigningFiles(certificatesToExport, profilesToExport, isAskForPassword) + if err != nil { + return err + } + + exportResult, err := codesign.UploadAndWriteCodesignFiles(certificates, + profiles, codesign.WriteFilesConfig{ WriteFiles: writeFiles, AbsOutputDirPath: absExportOutputDirPath, @@ -212,6 +214,6 @@ func scanXamarinProject(cmd *cobra.Command, args []string) error { return err } - printFinished(exoprtResult, absExportOutputDirPath) + printFinished(exportResult, absExportOutputDirPath) return nil } diff --git a/cmd/xcode.go b/cmd/xcode.go index 060a0a87..ee7ed686 100644 --- a/cmd/xcode.go +++ b/cmd/xcode.go @@ -13,8 +13,6 @@ import ( "github.com/bitrise-io/go-utils/fileutil" "github.com/bitrise-io/go-utils/log" "github.com/bitrise-io/go-utils/pathutil" - "github.com/bitrise-io/go-utils/stringutil" - "github.com/bitrise-io/go-xcode/utility" "github.com/bitrise-io/goinp/goinp" "github.com/spf13/cobra" ) @@ -54,21 +52,12 @@ func absOutputDir() (string, error) { return absExportOutputDirPath, nil } -func scanXcodeProject(cmd *cobra.Command, args []string) error { +func scanXcodeProject(_ *cobra.Command, _ []string) error { absExportOutputDirPath, err := absOutputDir() if err != nil { return err } - // Output tools versions - xcodebuildVersion, err := utility.GetXcodeVersion() - if err != nil { - return fmt.Errorf("failed to get Xcode (xcodebuild) version, error: %s", err) - } - fmt.Println() - log.Infof("%s: %s (%s)", colorstring.Green("Xcode (xcodebuild) version"), xcodebuildVersion.Version, xcodebuildVersion.BuildVersion) - fmt.Println() - xcodeCmd := xcode.CommandModel{} projectPath := paramXcodeProjectFilePath @@ -76,7 +65,6 @@ func scanXcodeProject(cmd *cobra.Command, args []string) error { log.Infof("Scan the directory for project files") log.Warnf("You can specify the Xcode project/workscape file to scan with the --file flag.") - // // Scan the directory for Xcode Project (.xcworkspace / .xcodeproject) file first // If can't find any, ask the user to drag-and-drop the file projpth, err := findXcodeProject() @@ -120,47 +108,33 @@ func scanXcodeProject(cmd *cobra.Command, args []string) error { xcodeCmd.SDK = paramXcodebuildSDK } - fmt.Println() - log.Printf("🔦 Running an Xcode Archive, to get all the required code signing settings...") - var isLogFileWritten bool - xcodebuildOutputFilePath := filepath.Join(absExportOutputDirPath, "xcodebuild-output.log") - archivePath, xcodebuildOutput, err := xcodeCmd.GenerateArchive() + writeBuildLogs := func(xcodebuildOutput string) error { + if writeFiles == codesign.WriteFilesAlways || writeFiles == codesign.WriteFilesFallback && err != nil { // save the xcodebuild output into a debug log file + xcodebuildOutputFilePath := filepath.Join(absExportOutputDirPath, "xcodebuild-output.log") + if err := os.MkdirAll(absExportOutputDirPath, 0700); err != nil { + return fmt.Errorf("failed to create output directory, error: %s", err) + } - if writeFiles == codesign.WriteFilesAlways || - writeFiles == codesign.WriteFilesFallback && err != nil { // save the xcodebuild output into a debug log file - if err := os.MkdirAll(absExportOutputDirPath, 0700); err != nil { - return fmt.Errorf("failed to create output directory, error: %s", err) - } - log.Infof("💡 "+colorstring.Yellow("Saving xcodebuild output into file")+": %s", xcodebuildOutputFilePath) - if err := fileutil.WriteStringToFile(xcodebuildOutputFilePath, xcodebuildOutput); err != nil { - log.Errorf("Failed to save xcodebuild output into file (%s), error: %s", xcodebuildOutputFilePath, err) - } else { - isLogFileWritten = true + log.Infof("💡 "+colorstring.Yellow("Saving xcodebuild output into file")+": %s", xcodebuildOutputFilePath) + if err := fileutil.WriteStringToFile(xcodebuildOutputFilePath, xcodebuildOutput); err != nil { + return fmt.Errorf("Failed to save xcodebuild output into file (%s), error: %s", xcodebuildOutputFilePath, err) + } } + return nil } + + archivePath, err := codesigndoc.BuildXcodeArchive(xcodeCmd, writeBuildLogs) if err != nil { - log.Warnf("Last lines of build log:") - fmt.Println(stringutil.LastNLines(xcodebuildOutput, 15)) - fmt.Println() - if isLogFileWritten { - log.Warnf("Please check the logfile (%s) to see what caused the error", xcodebuildOutputFilePath) - log.Warnf("and make sure that you can Archive this project from Xcode!") - fmt.Println() - log.Printf("Open the project: %s", xcodeCmd.ProjectFilePath) - log.Printf("and Archive, using the Scheme: %s", xcodeCmd.Scheme) - fmt.Println() - } return ArchiveError{toolXcode, err.Error()} } - // If certificatesOnly is set, CollectCodesignFiles returns an empty slice for profiles - certificatesToExport, profilesToExport, err := codesigndoc.CollectCodesignFiles(archivePath, certificatesOnly) + certificates, profiles, err := codesigndoc.CodesigningFilesForXCodeProject(archivePath, certificatesOnly, isAskForPassword) if err != nil { return err } - exoprtResult, err := codesign.UploadAndWriteCodesignFiles(certificatesToExport, - profilesToExport, - isAskForPassword, + + exportResult, err := codesign.UploadAndWriteCodesignFiles(certificates, + profiles, codesign.WriteFilesConfig{ WriteFiles: writeFiles, AbsOutputDirPath: absExportOutputDirPath, @@ -173,6 +147,6 @@ func scanXcodeProject(cmd *cobra.Command, args []string) error { return err } - printFinished(exoprtResult, absExportOutputDirPath) + printFinished(exportResult, absExportOutputDirPath) return nil } diff --git a/cmd/xcodeUITests.go b/cmd/xcodeUITests.go index 94150282..5576b28b 100644 --- a/cmd/xcodeUITests.go +++ b/cmd/xcodeUITests.go @@ -114,35 +114,34 @@ func scanXcodeUITestsProject(cmd *cobra.Command, args []string) error { xcodeUITestsCmd.SDK = paramXcodebuildSDK } + fmt.Println() fmt.Println() log.Printf("🔦 Running an Xcode build-for-testing, to get all the required code signing settings...") - var isLogFileWritten bool xcodebuildOutputFilePath := filepath.Join(absExportOutputDirPath, "xcodebuild-output.log") buildForTestingPath, xcodebuildOutput, err := xcodeUITestsCmd.RunBuildForTesting() - if writeFiles == codesign.WriteFilesAlways || - writeFiles == codesign.WriteFilesFallback && err != nil { // save the xcodebuild output into a debug log file + if writeFiles == codesign.WriteFilesAlways || writeFiles == codesign.WriteFilesFallback && err != nil { // save the xcodebuild output into a debug log file if err := os.MkdirAll(absExportOutputDirPath, 0700); err != nil { return fmt.Errorf("failed to create output directory, error: %s", err) } + log.Infof("💡 "+colorstring.Yellow("Saving xcodebuild output into file")+": %s", xcodebuildOutputFilePath) if err := fileutil.WriteStringToFile(xcodebuildOutputFilePath, xcodebuildOutput); err != nil { log.Errorf("Failed to save xcodebuild output into file (%s), error: %s", xcodebuildOutputFilePath, err) - } else { - isLogFileWritten = true } } if err != nil { - log.Warnf("Last lines of build log:") + log.Warnf("Last lines of the build log:") fmt.Println(stringutil.LastNLines(xcodebuildOutput, 15)) + + log.Infof(colorstring.Yellow("Please check the build log to see what caused the error.")) fmt.Println() - if isLogFileWritten { - log.Warnf("Please check the logfile (%s) to see what caused the error", xcodebuildOutputFilePath) - log.Warnf("and make sure that you can run Build for testing against the project from Xcode!") - fmt.Println() - log.Printf("Open the project: %s", xcodeUITestsCmd.ProjectFilePath) - fmt.Println() - } + + log.Errorf("Xcode Build For Testing failed.") + log.Infof(colorstring.Yellow("Open the project: ")+"%s", xcodeUITestsCmd.ProjectFilePath) + log.Infof(colorstring.Yellow("and make sure that you can run Build For Testing, with the scheme: ")+"%s", xcodeUITestsCmd.Scheme) + fmt.Println() + return BuildForTestingError{toolXcode, err.Error()} } @@ -151,9 +150,14 @@ func scanXcodeUITestsProject(cmd *cobra.Command, args []string) error { if err != nil { return err } - exportResult, err := codesign.UploadAndWriteCodesignFiles(certificatesToExport, - profilesToExport, - isAskForPassword, + + certificates, profiles, err := codesign.ExportCodesigningFiles(certificatesToExport, profilesToExport, isAskForPassword) + if err != nil { + return err + } + + exportResult, err := codesign.UploadAndWriteCodesignFiles(certificates, + profiles, codesign.WriteFilesConfig{ WriteFiles: writeFiles, AbsOutputDirPath: absExportOutputDirPath, diff --git a/codesign/export.go b/codesign/export.go index cf1b2f20..6efd1835 100644 --- a/codesign/export.go +++ b/codesign/export.go @@ -1,7 +1,6 @@ package codesign import ( - "bytes" "errors" "fmt" "io/ioutil" @@ -53,38 +52,49 @@ type ExportReport struct { CodesignFilesWritten bool } -// UploadAndWriteCodesignFiles exports then uploads codesign files to bitrise.io and saves them to output folder -func UploadAndWriteCodesignFiles(certificates []certificateutil.CertificateInfoModel, profiles []profileutil.ProvisioningProfileInfoModel, askForPassword bool, writeFilesConfig WriteFilesConfig, uploadConfig UploadConfig) (ExportReport, error) { - identities, err := collectAndExportIdentities(certificates, askForPassword) +// ExportCodesigningFiles exports certificates from the Keychain and provisoining profiles from their directory +func ExportCodesigningFiles(certificatesRequired []certificateutil.CertificateInfoModel, profilesRequired []profileutil.ProvisioningProfileInfoModel, askForPassword bool) (models.Certificates, []models.ProvisioningProfile, error) { + certificates, err := exportIdentities(certificatesRequired, askForPassword) if err != nil { - return ExportReport{}, err + return models.Certificates{}, nil, err } - provisioningProfiles, err := collectAndExportProvisioningProfiles(profiles) + + profiles, err := exportProvisioningProfiles(profilesRequired) if err != nil { - return ExportReport{}, err + return models.Certificates{}, nil, err } + return certificates, profiles, nil +} + +// UploadAndWriteCodesignFiles exports then uploads codesign files to bitrise.io and saves them to output folder +func UploadAndWriteCodesignFiles(certificates models.Certificates, provisioningProfiles []models.ProvisioningProfile, writeFilesConfig WriteFilesConfig, uploadConfig UploadConfig) (ExportReport, error) { var client *bitrise.Client // both or none CLI flags are required if uploadConfig.PersonalAccessToken != "" && uploadConfig.AppSlug != "" { // Upload automatically if token is provided as CLI paramter, do not export to filesystem // Used to upload artifacts as part of an other CLI tool + var err error client, err = bitrise.NewClient(uploadConfig.PersonalAccessToken) if err != nil { return ExportReport{}, err } + client.SetSelectedAppSlug(uploadConfig.AppSlug) } + if client == nil { uploadConfirmMsg := "Do you want to upload the provisioning profiles and certificates to Bitrise?" if len(provisioningProfiles) == 0 { uploadConfirmMsg = "Do you want to upload the certificates to Bitrise?" } fmt.Println() + shouldUpload, err := goinp.AskForBoolFromReader(uploadConfirmMsg, os.Stdin) if err != nil { return ExportReport{}, err } + if shouldUpload { if client, err = bitriseio.GetInteractiveConfigClient(); err != nil { return ExportReport{}, err @@ -95,7 +105,7 @@ func UploadAndWriteCodesignFiles(certificates []certificateutil.CertificateInfoM var filesWritten bool if writeFilesConfig.WriteFiles == WriteFilesAlways || writeFilesConfig.WriteFiles == WriteFilesFallback && client == nil { - if err := writeFiles(identities, provisioningProfiles, writeFilesConfig); err != nil { + if err := writeFiles(certificates, provisioningProfiles, writeFilesConfig); err != nil { return ExportReport{}, err } filesWritten = true @@ -103,13 +113,13 @@ func UploadAndWriteCodesignFiles(certificates []certificateutil.CertificateInfoM if client == nil { return ExportReport{ - CertificatesUploaded: len(certificates) == 0, - ProvisioningProfilesUploaded: len(profiles) == 0, + CertificatesUploaded: len(certificates.Info) == 0, + ProvisioningProfilesUploaded: len(provisioningProfiles) == 0, CodesignFilesWritten: filesWritten, }, nil } - certificatesUploaded, profilesUploaded, err := bitriseio.UploadCodesigningFiles(client, identities, provisioningProfiles) + certificatesUploaded, profilesUploaded, err := bitriseio.UploadCodesigningFiles(client, certificates, provisioningProfiles) return ExportReport{ CertificatesUploaded: certificatesUploaded, ProvisioningProfilesUploaded: profilesUploaded, @@ -147,8 +157,8 @@ func writeFiles(identities models.Certificates, provisioningProfiles []models.Pr return nil } -// collectAndExportIdentities exports the given certificates merged in a single .p12 file -func collectAndExportIdentities(certificates []certificateutil.CertificateInfoModel, isAskForPassword bool) (models.Certificates, error) { +// exportIdentities exports the given certificates merged in a single .p12 file +func exportIdentities(certificates []certificateutil.CertificateInfoModel, isAskForPassword bool) (models.Certificates, error) { if len(certificates) == 0 { return models.Certificates{}, nil } @@ -215,8 +225,8 @@ func writeIdentities(identites []byte, absExportOutputDirPath string) error { return ioutil.WriteFile(filepath.Join(absExportOutputDirPath, "Identities.p12"), identites, 0600) } -// collectAndExportProvisioningProfiles returns provisioning profies -func collectAndExportProvisioningProfiles(profiles []profileutil.ProvisioningProfileInfoModel) ([]models.ProvisioningProfile, error) { +// exportProvisioningProfiles returns provisioning profies +func exportProvisioningProfiles(profiles []profileutil.ProvisioningProfileInfoModel) ([]models.ProvisioningProfile, error) { if len(profiles) == 0 { return nil, nil } @@ -242,9 +252,6 @@ func collectAndExportProvisioningProfiles(profiles []profileutil.ProvisioningPro if err != nil { return nil, fmt.Errorf("failed to parse exported profile, error: %s", err) } - if bytes.Compare(profile.Content(), exportedProfile.Content()) != 0 { - return nil, fmt.Errorf("Profile found in the archive does not match found profile") - } contents, err := ioutil.ReadFile(pth) if err != nil { diff --git a/codesigndoc/xcode.go b/codesigndoc/xcode.go new file mode 100644 index 00000000..70710f33 --- /dev/null +++ b/codesigndoc/xcode.go @@ -0,0 +1,69 @@ +package codesigndoc + +import ( + "fmt" + + "github.com/bitrise-io/codesigndoc/xcode" + "github.com/bitrise-io/go-xcode/utility" + + "github.com/bitrise-io/codesigndoc/codesign" + "github.com/bitrise-io/codesigndoc/models" + "github.com/bitrise-io/go-utils/colorstring" + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/stringutil" +) + +// BuildXcodeArchive builds an Xcode archive +func BuildXcodeArchive(xcodeCmd xcode.CommandModel, handleBuildLog func(string) error) (archivePath string, err error) { + // Output tools versions + xcodebuildVersion, err := utility.GetXcodeVersion() + if err != nil { + return "", fmt.Errorf("failed to get Xcode (xcodebuild) version, error: %s", err) + } + fmt.Println() + log.Infof("%s: %s (%s)", colorstring.Green("Xcode (xcodebuild) version"), xcodebuildVersion.Version, xcodebuildVersion.BuildVersion) + + fmt.Println() + fmt.Println() + log.Printf("🔦 Running an Xcode Archive, to get all the required code signing settings...") + + archivePath, xcodebuildOutput, err := xcodeCmd.GenerateArchive() + + defer func() { + if handleBuildLog != nil { + if derr := handleBuildLog(xcodebuildOutput); derr != nil { + if err != nil { + err = derr + } + } + } + }() + + if err != nil { + log.Warnf("Last lines of the build log:") + fmt.Println(stringutil.LastNLines(xcodebuildOutput, 15)) + + log.Infof(colorstring.Yellow("Please check the build log to see what caused the error.")) + fmt.Println() + + log.Errorf("Xcode Archive failed.") + log.Infof(colorstring.Yellow("Open the project: ")+"%s", xcodeCmd.ProjectFilePath) + log.Infof(colorstring.Yellow("and make sure that you can build an Archive, with the scheme: ")+"%s", xcodeCmd.Scheme) + fmt.Println() + + return "", err + } + + return archivePath, nil +} + +// CodesigningFilesForXCodeProject ... +func CodesigningFilesForXCodeProject(archivePath string, certificatesOnly bool, isAskForPassword bool) (models.Certificates, []models.ProvisioningProfile, error) { + // If certificatesOnly is set, CollectCodesignFiles returns an empty slice for profiles + certificatesToExport, profilesToExport, err := CollectCodesignFiles(archivePath, certificatesOnly) + if err != nil { + return models.Certificates{}, nil, err + } + + return codesign.ExportCodesigningFiles(certificatesToExport, profilesToExport, isAskForPassword) +}