-
Notifications
You must be signed in to change notification settings - Fork 19
/
config.go
227 lines (194 loc) · 7.34 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package main
import (
"fmt"
"net/http"
"strings"
"github.com/bitrise-io/go-steputils/v2/stepconf"
"github.com/bitrise-io/go-utils/v2/retryhttp"
"github.com/bitrise-io/go-xcode/appleauth"
"github.com/bitrise-io/go-xcode/devportalservice"
"github.com/kballard/go-shellquote"
)
// Inputs ...
type Inputs struct {
InputWorkDir string `env:"work_dir,dir"`
Lane string `env:"lane,required"`
BitriseConnection bitriseConnection `env:"connection,opt[automatic,api_key,apple_id,off]"`
AppleID string `env:"apple_id"`
Password stepconf.Secret `env:"password"`
AppSpecificPassword stepconf.Secret `env:"app_password"`
APIKeyPath stepconf.Secret `env:"api_key_path"`
APIIssuer string `env:"api_issuer"`
UpdateFastlane bool `env:"update_fastlane,opt[true,false]"`
VerboseLog bool `env:"verbose_log,opt[yes,no]"`
EnableCache bool `env:"enable_cache,opt[yes,no]"`
GemHome string `env:"GEM_HOME"`
// Used to get Bitrise Apple Developer Portal Connection
BuildURL string `env:"BITRISE_BUILD_URL"`
BuildAPIToken stepconf.Secret `env:"BITRISE_BUILD_API_TOKEN"`
}
// Config contains inputs parsed from environment variables
type Config struct {
Inputs
WorkDir string
AuthCredentials appleauth.Credentials
LaneOptions []string
GemVersions gemVersions
}
// ProcessConfig ...
func (f FastlaneRunner) ProcessConfig() (Config, error) {
var inputs Inputs
if err := f.inputParser.Parse(&inputs); err != nil {
return Config{}, err
}
stepconf.Print(inputs)
f.logger.Println()
config := Config{Inputs: inputs}
f.logger.EnableDebugLog(config.VerboseLog)
authInputs, err := f.validateAuthInputs(config)
if err != nil {
return Config{}, fmt.Errorf("Issue with authentication related inputs: %v", err)
}
authSources, err := f.parseAuthSources(config.BitriseConnection)
if err != nil {
return Config{}, fmt.Errorf("Invalid Input: %v", err)
}
f.validateGemHome(config)
workDir, err := f.getWorkDir(config)
if err != nil {
return Config{}, err
}
config.WorkDir = workDir
// Select and fetch Apple authenication source
authConfig, err := f.selectAppleAuthSource(config, authSources, authInputs)
if err != nil {
return Config{}, err
}
config.AuthCredentials = authConfig
// Split lane option
laneOptions, err := shellquote.Split(config.Lane)
if err != nil {
return Config{}, fmt.Errorf("Failed to parse lane (%s), error: %s", config.Lane, err)
}
config.LaneOptions = laneOptions
// Determine desired Fastlane version
f.logger.Println()
f.logger.Infof("Determine desired Fastlane version")
gemVersions, err := f.parseGemfileLock(config.WorkDir)
if err != nil {
return Config{}, err
}
config.GemVersions = gemVersions
return config, nil
}
func (f FastlaneRunner) validateAuthInputs(config Config) (appleauth.Inputs, error) {
authInputs := appleauth.Inputs{
Username: config.AppleID,
Password: string(config.Password),
AppSpecificPassword: string(config.AppSpecificPassword),
APIIssuer: config.APIIssuer,
APIKeyPath: string(config.APIKeyPath),
}
if err := authInputs.Validate(); err != nil {
return appleauth.Inputs{}, err
}
return authInputs, nil
}
type bitriseConnection string
const (
automatic = "automatic"
apiKey = "api_key"
appleID = "apple_id"
off = "off"
)
func (f FastlaneRunner) parseAuthSources(connection bitriseConnection) ([]appleauth.Source, error) {
switch connection {
case automatic:
return []appleauth.Source{
&appleauth.ConnectionAPIKeySource{},
&appleauth.ConnectionAppleIDFastlaneSource{},
&appleauth.InputAPIKeySource{},
&appleauth.InputAppleIDFastlaneSource{},
}, nil
case apiKey:
return []appleauth.Source{&appleauth.ConnectionAPIKeySource{}}, nil
case appleID:
return []appleauth.Source{&appleauth.ConnectionAppleIDFastlaneSource{}}, nil
case off:
return []appleauth.Source{
&appleauth.InputAPIKeySource{},
&appleauth.InputAppleIDFastlaneSource{},
}, nil
default:
return nil, fmt.Errorf("invalid connection input: %s", connection)
}
}
func (f FastlaneRunner) validateGemHome(config Config) {
if strings.TrimSpace(config.GemHome) == "" {
return
}
f.logger.Warnf("GEM_HOME environment variable is set to:\n%s\nThis can lead to errors as gem lookup path may not contain GEM_HOME.", config.GemHome)
}
func (f FastlaneRunner) getWorkDir(config Config) (string, error) {
f.logger.Infof("Expand WorkDir")
workDir := config.InputWorkDir
if workDir == "" {
f.logger.Printf("WorkDir not set, using CurrentWorkingDirectory...")
currentDir, err := f.pathModifier.AbsPath(".")
if err != nil {
return "", fmt.Errorf("Failed to get current dir, error: %s", err)
}
workDir = currentDir
} else {
absWorkDir, err := f.pathModifier.AbsPath(workDir)
if err != nil {
return "", fmt.Errorf("Failed to expand path (%s), error: %s", workDir, err)
}
workDir = absWorkDir
}
f.logger.Donef("Expanded WorkDir: %s", workDir)
return workDir, nil
}
func (f FastlaneRunner) selectAppleAuthSource(config Config, authSources []appleauth.Source, authInputs appleauth.Inputs) (appleauth.Credentials, error) {
f.logger.Println()
f.logger.Infof("Reading Apple Developer Portal authentication data")
var devportalConnectionProvider *devportalservice.BitriseClient
if config.BuildURL != "" && config.BuildAPIToken != "" {
devportalConnectionProvider = devportalservice.NewBitriseClient(retryhttp.NewClient(f.logger).StandardClient(), config.BuildURL, string(config.BuildAPIToken))
} else {
f.logger.Warnf("Connected Apple Developer Portal Account not found. Step is not running on bitrise.io: BITRISE_BUILD_URL and BITRISE_BUILD_API_TOKEN envs are not set")
}
var conn *devportalservice.AppleDeveloperConnection
if config.BitriseConnection != "off" && devportalConnectionProvider != nil {
var err error
conn, err = devportalConnectionProvider.GetAppleDeveloperConnection()
if err != nil {
f.handleSessionDataError(err)
}
}
authConfig, err := appleauth.Select(conn, authSources, authInputs)
if err != nil {
if _, ok := err.(*appleauth.MissingAuthConfigError); !ok {
return appleauth.Credentials{}, fmt.Errorf("Could not configure Apple Service authentication: %v", err)
}
f.logger.Warnf("No authentication data found matching the selected Apple Service authentication method (%s).", config.BitriseConnection)
if conn != nil && (conn.APIKeyConnection == nil && conn.AppleIDConnection == nil) {
f.logger.Warnf("%s", notConnected)
}
}
return authConfig, nil
}
const notConnected = `Connected Apple Developer Portal Account not found.
Most likely because there is no Apple Developer Portal Account connected to the build.
Read more: https://devcenter.bitrise.io/getting-started/configuring-bitrise-steps-that-require-apple-developer-account-data/`
func (f FastlaneRunner) handleSessionDataError(err error) {
if err == nil {
return
}
if networkErr, ok := err.(devportalservice.NetworkError); ok && networkErr.Status == http.StatusUnauthorized {
f.logger.Warnf("%s", "Unauthorized to query Connected Apple Developer Portal Account. This happens by design, with a public app's PR build, to protect secrets.")
return
}
f.logger.Warnf("Failed to activate Bitrise Apple Developer Portal connection: %s", err)
f.logger.Warnf("Read more: https://devcenter.bitrise.io/getting-started/configuring-bitrise-steps-that-require-apple-developer-account-data/")
}