Skip to content

Commit aa97b88

Browse files
committed
Merge branch 'master' of github.com:bitrise-tools/codesigndoc
2 parents 2c752c1 + 55ae197 commit aa97b88

File tree

3 files changed

+196
-111
lines changed

3 files changed

+196
-111
lines changed

cmd/xcode.go

+67-49
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ var xcodeCmd = &cobra.Command{
2929
}
3030

3131
var (
32-
paramXcodeProjectFilePath = ""
33-
paramXcodeScheme = ""
32+
paramXcodeProjectFilePath = ""
33+
paramXcodeScheme = ""
34+
paramXcodebuildOutputLogFilePath = ""
3435
)
3536

3637
func init() {
@@ -42,6 +43,9 @@ func init() {
4243
xcodeCmd.Flags().StringVar(&paramXcodeScheme,
4344
"scheme", "",
4445
"Xcode Scheme")
46+
xcodeCmd.Flags().StringVar(&paramXcodebuildOutputLogFilePath,
47+
"xcodebuild-log", "",
48+
"xcodebuild output log (file path), instead of running xcodebuild")
4549
}
4650

4751
func printXcodeScanFinishedWithError(format string, args ...interface{}) error {
@@ -54,65 +58,79 @@ func scanXcodeProject(cmd *cobra.Command, args []string) error {
5458
return printXcodeScanFinishedWithError("Failed to prepare Export directory: %s", err)
5559
}
5660

57-
projectPath := paramXcodeProjectFilePath
58-
if projectPath == "" {
59-
askText := `Please drag-and-drop your Xcode Project (` + colorstring.Green(".xcodeproj") + `)
61+
xcodebuildOutput := ""
62+
xcodeCmd := xcode.CommandModel{}
63+
if paramXcodebuildOutputLogFilePath != "" {
64+
xcLog, err := fileutil.ReadStringFromFile(paramXcodebuildOutputLogFilePath)
65+
if err != nil {
66+
return printXcodeScanFinishedWithError("Failed to read log from the specified log file, error: %s", err)
67+
}
68+
xcodebuildOutput = xcLog
69+
} else {
70+
projectPath := paramXcodeProjectFilePath
71+
if projectPath == "" {
72+
askText := `Please drag-and-drop your Xcode Project (` + colorstring.Green(".xcodeproj") + `)
6073
or Workspace (` + colorstring.Green(".xcworkspace") + `) file, the one you usually open in Xcode,
6174
then hit Enter.
6275
6376
(Note: if you have a Workspace file you should most likely use that)`
64-
fmt.Println()
65-
projpth, err := goinp.AskForPath(askText)
66-
if err != nil {
67-
return printXcodeScanFinishedWithError("Failed to read input: %s", err)
77+
fmt.Println()
78+
projpth, err := goinp.AskForPath(askText)
79+
if err != nil {
80+
return printXcodeScanFinishedWithError("Failed to read input: %s", err)
81+
}
82+
projectPath = projpth
6883
}
69-
projectPath = projpth
70-
}
71-
log.Debugf("projectPath: %s", projectPath)
72-
xcodeCmd := xcode.CommandModel{
73-
ProjectFilePath: projectPath,
74-
}
84+
log.Debugf("projectPath: %s", projectPath)
85+
xcodeCmd.ProjectFilePath = projectPath
7586

76-
schemeToUse := paramXcodeScheme
77-
if schemeToUse == "" {
78-
fmt.Println()
79-
fmt.Println()
80-
log.Println("🔦 Scanning Schemes ...")
81-
schemes, err := xcodeCmd.ScanSchemes()
82-
if err != nil {
83-
return printXcodeScanFinishedWithError("Failed to scan Schemes: %s", err)
87+
schemeToUse := paramXcodeScheme
88+
if schemeToUse == "" {
89+
fmt.Println()
90+
fmt.Println()
91+
log.Println("🔦 Scanning Schemes ...")
92+
schemes, err := xcodeCmd.ScanSchemes()
93+
if err != nil {
94+
return printXcodeScanFinishedWithError("Failed to scan Schemes: %s", err)
95+
}
96+
log.Debugf("schemes: %v", schemes)
97+
98+
fmt.Println()
99+
selectedScheme, err := goinp.SelectFromStrings("Select the Scheme you usually use in Xcode", schemes)
100+
if err != nil {
101+
return printXcodeScanFinishedWithError("Failed to select Scheme: %s", err)
102+
}
103+
log.Debugf("selected scheme: %v", selectedScheme)
104+
schemeToUse = selectedScheme
84105
}
85-
log.Debugf("schemes: %v", schemes)
106+
xcodeCmd.Scheme = schemeToUse
86107

87108
fmt.Println()
88-
selectedScheme, err := goinp.SelectFromStrings("Select the Scheme you usually use in Xcode", schemes)
89-
if err != nil {
90-
return printXcodeScanFinishedWithError("Failed to select Scheme: %s", err)
109+
fmt.Println()
110+
log.Println("🔦 Running an Xcode Archive, to get all the required code signing settings...")
111+
xcLog, err := xcodeCmd.GenerateLog()
112+
xcodebuildOutput = xcLog
113+
// save the xcodebuild output into a debug log file
114+
xcodebuildOutputFilePath := filepath.Join(absExportOutputDirPath, "xcodebuild-output.log")
115+
{
116+
log.Infof(" 💡 "+colorstring.Yellow("Saving xcodebuild output into file")+": %s", xcodebuildOutputFilePath)
117+
if logWriteErr := fileutil.WriteStringToFile(xcodebuildOutputFilePath, xcodebuildOutput); logWriteErr != nil {
118+
log.Errorf("Failed to save xcodebuild output into file (%s), error: %s", xcodebuildOutputFilePath, logWriteErr)
119+
} else if err != nil {
120+
log.Infoln(colorstring.Yellow("Please check the logfile (" + xcodebuildOutputFilePath + ") to see what caused the error"))
121+
log.Infoln(colorstring.Red("and make sure that you can Archive this project from Xcode!"))
122+
fmt.Println()
123+
log.Infoln("Open the project:", xcodeCmd.ProjectFilePath)
124+
log.Infoln("and Archive, using the Scheme:", xcodeCmd.Scheme)
125+
fmt.Println()
126+
}
91127
}
92-
log.Debugf("selected scheme: %v", selectedScheme)
93-
schemeToUse = selectedScheme
94-
}
95-
xcodeCmd.Scheme = schemeToUse
96-
97-
fmt.Println()
98-
fmt.Println()
99-
log.Println("🔦 Running an Xcode Archive, to get all the required code signing settings...")
100-
codeSigningSettings, xcodebuildOutput, err := xcodeCmd.ScanCodeSigningSettings()
101-
// save the xcodebuild output into a debug log file
102-
xcodebuildOutputFilePath := filepath.Join(absExportOutputDirPath, "xcodebuild-output.log")
103-
{
104-
log.Infof(" 💡 "+colorstring.Yellow("Saving xcodebuild output into file")+": %s", xcodebuildOutputFilePath)
105-
if logWriteErr := fileutil.WriteStringToFile(xcodebuildOutputFilePath, xcodebuildOutput); logWriteErr != nil {
106-
log.Errorf("Failed to save xcodebuild output into file (%s), error: %s", xcodebuildOutputFilePath, logWriteErr)
107-
} else if err != nil {
108-
log.Infoln(colorstring.Yellow("Please check the logfile (" + xcodebuildOutputFilePath + ") to see what caused the error"))
109-
log.Infoln(colorstring.Red("and make sure that you can Archive this project from Xcode!"))
110-
fmt.Println()
111-
log.Infoln("Open the project:", xcodeCmd.ProjectFilePath)
112-
log.Infoln("and Archive, using the Scheme:", xcodeCmd.Scheme)
113-
fmt.Println()
128+
if err != nil {
129+
return printXcodeScanFinishedWithError("Failed to run Xcode Archive: %s", err)
114130
}
115131
}
132+
133+
codeSigningSettings, err := xcodeCmd.ScanCodeSigningSettings(xcodebuildOutput)
116134
if err != nil {
117135
return printXcodeScanFinishedWithError("Failed to detect code signing settings: %s", err)
118136
}

xcode/xcodecmd.go

+86-56
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package xcode
33
import (
44
"bufio"
55
"fmt"
6+
"io"
67
"regexp"
78
"strings"
89
"time"
@@ -41,68 +42,94 @@ func parseSchemesFromXcodeOutput(xcodeOutput string) []string {
4142
return foundSchemes
4243
}
4344

44-
func parseCodeSigningSettingsFromXcodeOutput(xcodeOutput string) common.CodeSigningSettings {
45-
scanner := bufio.NewScanner(strings.NewReader(xcodeOutput))
45+
// ReadLongLine ...
46+
func ReadLongLine(r *bufio.Reader) (string, error) {
47+
isPrefix := true
48+
var err error
49+
var line, ln []byte
50+
51+
for isPrefix && err == nil {
52+
line, isPrefix, err = r.ReadLine()
53+
ln = append(ln, line...)
54+
}
55+
56+
return string(ln), err
57+
}
58+
59+
func parseCodeSigningSettingsFromXcodeOutput(xcodeOutput string) (common.CodeSigningSettings, error) {
60+
logReader := bufio.NewReader(strings.NewReader(xcodeOutput))
4661

4762
identitiesMap := map[string]common.CodeSigningIdentityInfo{}
4863
provProfilesMap := map[string]provprofile.ProvisioningProfileInfo{}
4964
teamIDsMap := map[string]interface{}{}
5065
appBundleIDsMap := map[string]interface{}{}
51-
for scanner.Scan() {
52-
line := scanner.Text()
5366

54-
// Team ID
55-
if rexp := regexp.MustCompile(`^[[:space:]]*"com.apple.developer.team-identifier" = (?P<teamid>[a-zA-Z0-9]+);$`); rexp.MatchString(line) {
56-
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
57-
if err != nil {
58-
log.Errorf("Failed to scan TeamID: %s", err)
59-
continue
67+
// scan log line by line
68+
{
69+
line, readErr := ReadLongLine(logReader)
70+
for ; readErr == nil; line, readErr = ReadLongLine(logReader) {
71+
// Team ID
72+
if rexp := regexp.MustCompile(`^[[:space:]]*"com.apple.developer.team-identifier" = (?P<teamid>[a-zA-Z0-9]+);$`); rexp.MatchString(line) {
73+
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
74+
if err != nil {
75+
log.Errorf("Failed to scan TeamID: %s", err)
76+
continue
77+
}
78+
teamIDsMap[results["teamid"]] = 1
6079
}
61-
teamIDsMap[results["teamid"]] = 1
62-
}
6380

64-
// App Bundle ID
65-
if rexp := regexp.MustCompile(`^[[:space:]]*"application-identifier" = "(?P<appbundleid>.+)";$`); rexp.MatchString(line) {
66-
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
67-
if err != nil {
68-
log.Errorf("Failed to scan App Bundle ID: %s", err)
69-
continue
81+
// App Bundle ID
82+
if rexp := regexp.MustCompile(`^[[:space:]]*"application-identifier" = "(?P<appbundleid>.+)";$`); rexp.MatchString(line) {
83+
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
84+
if err != nil {
85+
log.Errorf("Failed to scan App Bundle ID: %s", err)
86+
continue
87+
}
88+
appBundleIDsMap[results["appbundleid"]] = 1
7089
}
71-
appBundleIDsMap[results["appbundleid"]] = 1
72-
}
7390

74-
// Signing Identity
75-
if rexp := regexp.MustCompile(`^[[:space:]]*Signing Identity:[[:space:]]*"(?P<title>.+)"$`); rexp.MatchString(line) {
76-
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
77-
if err != nil {
78-
log.Errorf("Failed to scan Signing Identity title: %s", err)
79-
continue
91+
// Signing Identity
92+
if rexp := regexp.MustCompile(`^[[:space:]]*Signing Identity:[[:space:]]*"(?P<title>.+)"$`); rexp.MatchString(line) {
93+
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
94+
if err != nil {
95+
log.Errorf("Failed to scan Signing Identity title: %s", err)
96+
continue
97+
}
98+
codeSigningID := common.CodeSigningIdentityInfo{Title: results["title"]}
99+
identitiesMap[codeSigningID.Title] = codeSigningID
80100
}
81-
codeSigningID := common.CodeSigningIdentityInfo{Title: results["title"]}
82-
identitiesMap[codeSigningID.Title] = codeSigningID
83-
}
84-
// Prov. Profile - title line
85-
if rexp := regexp.MustCompile(`^[[:space:]]*Provisioning Profile:[[:space:]]*"(?P<title>.+)"$`); rexp.MatchString(line) {
86-
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
87-
if err != nil {
88-
log.Errorf("Failed to scan Provisioning Profile title: %s", err)
89-
continue
90-
}
91-
tmpProvProfile := provprofile.ProvisioningProfileInfo{Title: results["title"]}
92-
if !scanner.Scan() {
93-
log.Error("Failed to scan Provisioning Profile UUID: no more lines to scan")
94-
continue
101+
// Prov. Profile - title line
102+
if rexp := regexp.MustCompile(`^[[:space:]]*Provisioning Profile:[[:space:]]*"(?P<title>.+)"$`); rexp.MatchString(line) {
103+
results, err := regexputil.NamedFindStringSubmatch(rexp, line)
104+
if err != nil {
105+
log.Errorf("Failed to scan Provisioning Profile title: %s", err)
106+
continue
107+
}
108+
tmpProvProfile := provprofile.ProvisioningProfileInfo{Title: results["title"]}
109+
110+
// read next line
111+
line, readErr = ReadLongLine(logReader)
112+
if readErr != nil {
113+
continue
114+
}
115+
if line == "" {
116+
log.Error("Failed to scan Provisioning Profile UUID: no more lines to scan")
117+
continue
118+
}
119+
provProfileUUIDLine := line
120+
121+
rexp = regexp.MustCompile(`^[[:space:]]*\((?P<uuid>[a-zA-Z0-9-]{36})\)`)
122+
results, err = regexputil.NamedFindStringSubmatch(rexp, provProfileUUIDLine)
123+
if err != nil {
124+
log.Errorf("Failed to scan Provisioning Profile UUID: %s | line was: %s", err, provProfileUUIDLine)
125+
continue
126+
}
127+
tmpProvProfile.UUID = results["uuid"]
128+
provProfilesMap[tmpProvProfile.Title] = tmpProvProfile
95129
}
96-
provProfileUUIDLine := scanner.Text()
97-
98-
rexp = regexp.MustCompile(`^[[:space:]]*\((?P<uuid>[a-zA-Z0-9-]{36})\)`)
99-
results, err = regexputil.NamedFindStringSubmatch(rexp, provProfileUUIDLine)
100-
if err != nil {
101-
log.Errorf("Failed to scan Provisioning Profile UUID: %s | line was: %s", err, provProfileUUIDLine)
102-
continue
103-
}
104-
tmpProvProfile.UUID = results["uuid"]
105-
provProfilesMap[tmpProvProfile.Title] = tmpProvProfile
130+
}
131+
if readErr != nil && readErr != io.EOF {
132+
return common.CodeSigningSettings{}, fmt.Errorf("Failed to scan log output, error: %s", readErr)
106133
}
107134
}
108135

@@ -122,11 +149,11 @@ func parseCodeSigningSettingsFromXcodeOutput(xcodeOutput string) common.CodeSign
122149
ProvProfiles: provProfiles,
123150
TeamIDs: teamIDs,
124151
AppBundleIDs: appBundleIDs,
125-
}
152+
}, nil
126153
}
127154

128-
// ScanCodeSigningSettings ...
129-
func (xccmd CommandModel) ScanCodeSigningSettings() (common.CodeSigningSettings, string, error) {
155+
// GenerateLog : generates the log for subsequent "Scan" call
156+
func (xccmd CommandModel) GenerateLog() (string, error) {
130157
xcoutput := ""
131158
var err error
132159

@@ -136,11 +163,14 @@ func (xccmd CommandModel) ScanCodeSigningSettings() (common.CodeSigningSettings,
136163
fmt.Println()
137164

138165
if err != nil {
139-
return common.CodeSigningSettings{}, xcoutput,
140-
fmt.Errorf("Failed to Archive, error: %s", err)
166+
return xcoutput, fmt.Errorf("Failed to Archive, error: %s", err)
141167
}
168+
return xcoutput, nil
169+
}
142170

143-
return parseCodeSigningSettingsFromXcodeOutput(xcoutput), xcoutput, nil
171+
// ScanCodeSigningSettings ...
172+
func (xccmd CommandModel) ScanCodeSigningSettings(logToScan string) (common.CodeSigningSettings, error) {
173+
return parseCodeSigningSettingsFromXcodeOutput(logToScan)
144174
}
145175

146176
func (xccmd CommandModel) xcodeProjectOrWorkspaceParam() (string, error) {

0 commit comments

Comments
 (0)