Skip to content

Commit 68c9816

Browse files
authored
fix(ios): Automate team id selection (#39)
* [ios] Automate team id selection, prompt if more user has more than one team * [ios] Save dev account password into CircleCI env variable (FASTLANE_PASSWORD) for CI access to dev account for deployments * [circle] Create circle extension and move circle specific flows there
1 parent b3a3962 commit 68c9816

7 files changed

Lines changed: 300 additions & 76 deletions

File tree

src/extensions/circle-extension.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module.exports = toolbox => {
2+
toolbox.circle = {
3+
postEnvVariable: async ({
4+
org,
5+
project,
6+
apiToken,
7+
key,
8+
value
9+
}) => {
10+
const { http } = toolbox
11+
const api = http.create({
12+
baseURL: 'https://circleci.com/api/v1.1/'
13+
})
14+
15+
await api.post(
16+
`project/github/${org}/${project}/envvar?circle-token=${apiToken}`,
17+
{name: key, value: value },
18+
{headers: {'Content-Type': 'application/json'}}
19+
)
20+
},
21+
followProject: async ({ org, project, apiToken}) => {
22+
const { http } = toolbox
23+
const api = http.create({
24+
baseURL: 'https://circleci.com/api/v1.1/'
25+
})
26+
27+
const { status } = await api.post(
28+
`project/github/${org}/${project}/follow?circle-token=${apiToken}`
29+
)
30+
return status
31+
}
32+
}
33+
}

src/extensions/ios-extension.js

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ module.exports = toolbox => {
99
resolve()
1010
})
1111
},
12-
addBuildConfigurations: async (teamId) => {
12+
addBuildConfigurations: async teamId => {
1313
const { print, system, meta } = toolbox
1414
return new Promise((resolve, reject) => {
15-
system.run(`ruby ${meta.src}/ios.rb make_new_build_configurations ${teamId}`)
15+
system.run(
16+
`ruby ${meta.src}/ios.rb make_new_build_configurations ${teamId}`
17+
)
1618
resolve()
1719
})
1820
},
@@ -34,19 +36,93 @@ module.exports = toolbox => {
3436
resolve()
3537
})
3638
},
37-
produceApp: async ({ appId, devId, appName, developerTeamId, iTunesTeamId}) => {
39+
produceApp: async ({
40+
appId,
41+
devId,
42+
developerPassword,
43+
appName,
44+
developerTeamId,
45+
iTunesTeamId
46+
}) => {
3847
const { print, system } = toolbox
3948
return new Promise((resolve, reject) => {
40-
const output = system.run(`fastlane produce -u ${devId} -a ${appId} --app_name "${appName}" --team_id "${developerTeamId}" --itc_team_id "${iTunesTeamId}"`)
49+
const output = system.run(
50+
`fastlane produce -u ${devId} -a ${appId} --app_name "${appName}" --team_id "${developerTeamId}" --itc_team_id "${iTunesTeamId}"`
51+
)
4152
resolve(output)
4253
})
4354
},
4455
matchSync: async ({ certType, password }) => {
4556
const { print, system } = toolbox
4657
return new Promise((resolve, reject) => {
47-
const output = system.run(`cd ios && (export MATCH_PASSWORD=${password}; bundle exec fastlane match ${certType})`)
58+
const output = system.run(
59+
`cd ios && (export MATCH_PASSWORD=${password}; bundle exec fastlane match ${certType})`
60+
)
4861
resolve(output)
4962
})
63+
},
64+
getTeamIds: async accountInfo => {
65+
const { print, system, meta } = toolbox
66+
return new Promise(async (resolve, reject) => {
67+
try {
68+
const devTeams = await parseTeam('dev', accountInfo, meta.src)
69+
const itcTeams = await parseTeam('itc', accountInfo, meta.src)
70+
const teams = {
71+
itcTeams: itcTeams,
72+
devTeams: devTeams
73+
}
74+
resolve(teams)
75+
} catch (e) {
76+
reject(e)
77+
}
78+
})
5079
}
5180
}
5281
}
82+
83+
const parseTeam = async (
84+
teamType,
85+
{ developerAccount, developerPassword },
86+
path
87+
) => {
88+
return new Promise((resolve, reject) => {
89+
const spawn = require('child_process').spawn
90+
const child = spawn('ruby', [
91+
`${path}/ios.rb`,
92+
'get_team_id',
93+
teamType,
94+
developerAccount,
95+
developerPassword
96+
])
97+
child.stdout.on('data', data => {
98+
const dataStr = data.toString()
99+
const regexpTeamLine = new RegExp(/^\d+[)]/)
100+
const regexpTeamName = new RegExp(/"(.*?)"/)
101+
const lines = dataStr.split('\n')
102+
const teams = []
103+
//if only one team found
104+
if (lines.length === 2 && lines[1].length === 0) {
105+
teams.push(lines[0])
106+
resolve(teams)
107+
}
108+
//parse multiple teams
109+
const regexpTeamId =
110+
teamType === 'dev' ? new RegExp(/\)(.*?)"/) : new RegExp(/\((.*?)\)/)
111+
lines.forEach(line => {
112+
if (regexpTeamLine.test(line)) {
113+
const teamName = regexpTeamName.exec(line)[1]
114+
const teamId = regexpTeamId.exec(line)[1].trim()
115+
teams.push({ name: teamName, id: teamId })
116+
}
117+
})
118+
if (teams.length === 0) {
119+
reject('Not able to retrieve teams, check account credentials')
120+
}
121+
resolve(teams)
122+
})
123+
child.stderr.on('data', data => {
124+
reject('Not able to retrieve teams, check account credentials')
125+
})
126+
child.stdin.write('2\n')
127+
})
128+
}

src/extensions/npm-extension.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// add your CLI-specific functionality here, which will then be accessible
2-
// to your commands
31
const install = require('install-packages')
42

53
module.exports = toolbox => {

src/flows/android.js

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const askQuestion = async (prompt, defaults = {}) => {
1414
return prompt.ask(askGooglePlayJSONPath)
1515
}
1616

17-
const initAndroid = async ({ android, http, prompt, print }, { project, org, apiToken, defaults }) => {
17+
const initAndroid = async ({ android, http, prompt, print, circle }, { project, org, apiToken, defaults }) => {
1818

1919
const { jsonPath } = await askQuestion(prompt, defaults)
2020

@@ -26,31 +26,33 @@ const initAndroid = async ({ android, http, prompt, print }, { project, org, ap
2626
})
2727

2828
print.info('Store keystore to secret variables')
29-
const api = http.create({
30-
baseURL: 'https://circleci.com/api/v1.1/'
29+
circle.postEnvVariable({
30+
org,
31+
project,
32+
apiToken,
33+
key: 'KEYSTORE',
34+
value: keystoreFiles.encodedKeystore
3135
})
3236

33-
await api.post(
34-
`project/github/${org}/${project}/envvar?circle-token=${apiToken}`,
35-
{name: 'KEYSTORE', value: keystoreFiles.encodedKeystore},
36-
{headers: {'Content-Type': 'application/json'}}
37-
)
38-
3937
print.info('Store keystore properties to secret variables')
40-
await api.post(
41-
`project/github/${org}/${project}/envvar?circle-token=${apiToken}`,
42-
{name: 'KEYSTORE_PROPERTIES', value: keystoreFiles.keystoreProperties},
43-
{headers: {'Content-Type': 'application/json'}}
44-
)
38+
circle.postEnvVariable({
39+
org,
40+
project,
41+
apiToken,
42+
key: 'KEYSTORE_PROPERTIES',
43+
value: keystoreFiles.keystoreProperties
44+
})
4545

4646
if (jsonPath !== '' && jsonPath !== undefined) {
4747
print.info('Store Google Play JSON to secret variables')
4848
const encodedPlayStoreJSON = android.base64EncodeJson(jsonPath)
49-
await api.post(
50-
`project/github/${org}/${project}/envvar?circle-token=${apiToken}`,
51-
{name: 'GOOGLE_PLAY_JSON', value: encodedPlayStoreJSON},
52-
{headers: {'Content-Type': 'application/json'}}
53-
)
49+
circle.postEnvVariable({
50+
org,
51+
project,
52+
apiToken,
53+
key: 'GOOGLE_PLAY_JSON',
54+
value: encodedPlayStoreJSON
55+
})
5456
}
5557
}
5658

0 commit comments

Comments
 (0)