@@ -3,39 +3,54 @@ const AbortController = require('abort-controller')
3
3
const moment = require ( 'moment' )
4
4
const fs = require ( 'fs' )
5
5
const XlsxTemplate = require ( 'xlsx-template' )
6
- const spsave = require ( 'spsave' ) . spsave // eslint-disable-line
6
+ const spsave = require ( 'spsave' ) . spsave
7
7
const ENV_VARS = require ( './env-vars.json' )
8
8
const SP_CONFIG = require ( './sp-config.json' )
9
9
10
+ const INTERNAL_ERROR = { 'error' : 'An error occurred.' }
10
11
const INTERNAL_TIMEOUT = 20000
11
12
const finalBuildNumber = parseInt ( ENV_VARS . buildNumber ) + 1000
13
+ const buildArtifacts = { }
12
14
13
15
ENV_VARS . commitHash = ENV_VARS . commitHash . substring ( 0 , 7 )
14
16
15
17
const buildNotify = async ( ) => {
16
18
try {
17
19
const buildTimestamp = moment ( ) . format ( 'YYYY-MM-DD h:mm A' )
18
20
const fciProjectLink = 'https://codemagic.io/app/' + ENV_VARS . fciProjectId + '/build/' + ENV_VARS . fciBuildId
19
- const buildSuccess = ( ENV_VARS . fciBuildStepStatus === 'success' )
20
- let artifactUrl = ''
21
+ let buildSuccess = ( ENV_VARS . fciBuildStepStatus === 'success' ) ? true : false
22
+ let buildApkFile = 'app-release.apk'
23
+ let buildIpaFile = 'UC_San_Diego.ipa'
21
24
let prAuthor = ''
25
+ let saveArtifactApkSuccess = false
26
+ let saveArtifactIpaSuccess = false
22
27
let testPlanFilename = ''
23
28
let testPlanUrl = ''
24
29
30
+ console . log ( 'ENV_VARS.fciBuildStepStatus: ' + ENV_VARS . fciBuildStepStatus )
31
+ console . log ( 'buildSuccess: ' + buildSuccess )
32
+ console . log ( 'buildPlatform: ' + ENV_VARS . buildPlatform )
25
33
// Check build success
26
34
if ( buildSuccess ) {
27
35
// Supplemental GitHub metadata for PRs
28
36
if ( ENV_VARS . prNumber ) {
29
37
prAuthor = await githubMeta ( )
30
38
}
31
39
32
- // Get Artifact URL
33
- artifactUrl = getArtifactUrl ( )
40
+ // Save build artifacts
41
+ if ( ENV_VARS . buildPlatform === 'IOS' ) {
42
+ saveArtifactIpaSuccess = await saveArtifact ( buildIpaFile )
43
+ } else if ( ENV_VARS . buildPlatform === 'ANDROID' ) {
44
+ saveArtifactApkSuccess = await saveArtifact ( buildApkFile )
45
+ }
34
46
35
47
// Generate test plan
36
- ; ( { testPlanFilename, testPlanUrl } = await generateTestPlan ( prAuthor ) ) // eslint-disable-line
48
+ ; ( { testPlanFilename, testPlanUrl } = await generateTestPlan ( prAuthor ) )
37
49
}
38
50
51
+ console . log ( 'saveArtifactIpaSuccess: ' + saveArtifactIpaSuccess )
52
+ console . log ( 'saveArtifactApkSuccess: ' + saveArtifactApkSuccess )
53
+
39
54
// Construct build notifier message
40
55
let teamsMessage = '#### Campus Mobile Build Notifier\n\n'
41
56
teamsMessage += '<table border="0" style="margin:16px">'
@@ -53,9 +68,17 @@ const buildNotify = async () => {
53
68
54
69
// Build Artifacts
55
70
if ( ENV_VARS . buildPlatform === 'IOS' ) {
56
- teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>iOS:</b></td><td><a href="https://mobile.ucsd.edu/testflight" style="text-decoration:underline">TestFlight ' + ENV_VARS . appVersion + ' (' + finalBuildNumber + ')</a></td></tr>'
71
+ if ( saveArtifactIpaSuccess ) {
72
+ teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>iOS:</b></td><td><a href="https://mobile.ucsd.edu/testflight" style="text-decoration:underline">TestFlight ' + ENV_VARS . appVersion + ' (' + finalBuildNumber + ')</a></td></tr>'
73
+ } else {
74
+ teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>iOS:</b></td><td><span style="color:#d60000">N/A</span></td></tr>'
75
+ }
57
76
} else if ( ENV_VARS . buildPlatform === 'ANDROID' ) {
58
- teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>Android:</b></td><td><a href="' + artifactUrl + '" download style="text-decoration:underline">APK ' + ENV_VARS . appVersion + ' (' + finalBuildNumber + ')</a></td></tr>'
77
+ if ( saveArtifactApkSuccess ) {
78
+ teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>Android:</b></td><td><a href="' + buildArtifacts . buildApkFinalUrl + '" download style="text-decoration:underline">' + buildArtifacts . buildApkFinalFilename + '</a></td></tr>'
79
+ } else {
80
+ teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>Android:</b></td><td><span style="color:#d60000">N/A</span></td></tr>'
81
+ }
59
82
}
60
83
61
84
// Test plan
@@ -67,7 +90,9 @@ const buildNotify = async () => {
67
90
const failedEmojiList = [ '🙀' , '😱' , '😵' ]
68
91
69
92
// Build success or failure
70
- if ( buildSuccess ) {
93
+ if ( buildSuccess &&
94
+ ( ( saveArtifactApkSuccess && ENV_VARS . buildPlatform === 'ANDROID' ) ||
95
+ ( saveArtifactIpaSuccess && ENV_VARS . buildPlatform === 'IOS' ) ) ) {
71
96
const successEmoji = successEmojiList [ Math . floor ( Math . random ( ) * successEmojiList . length ) ]
72
97
teamsMessage += '<tr style="border-bottom: 1px solid grey"><td align="right"><b>Status:</b></td><td><span style="color:#12a102">BUILD SUCCESS ' + successEmoji + '</span> (<a href="' + fciProjectLink + '" style="text-decoration:underline">detail</a>)</td></tr>'
73
98
} else {
@@ -94,26 +119,55 @@ const buildNotify = async () => {
94
119
} )
95
120
clearTimeout ( notifyTimeout )
96
121
97
- if ( notifyResp . statusText !== 'OK' ) {
98
- throw notifyResp
122
+ if ( notifyResp . statusText != 'OK' ) {
123
+ throw 'Error: Unable to POST to webhookUrl (status: ' + notifyResp . statusText + ')'
99
124
}
100
125
} catch ( err ) {
101
126
console . log ( err )
102
127
process . exitCode = 1
103
128
}
104
129
}
105
130
106
- const getArtifactUrl = ( ) => {
107
- let artifactUrl = ''
108
-
109
- ENV_VARS . fciArtifactLinks . forEach ( ( artifact , index ) => {
110
- if ( ( ENV_VARS . buildPlatform === 'IOS' && artifact . type === 'ipa' ) ||
111
- ( ENV_VARS . buildPlatform === 'ANDROID' && artifact . type === 'apk' ) ) {
112
- artifactUrl = artifact . url
131
+ const saveArtifact = async ( artifactFilename ) => {
132
+ try {
133
+ // Exit if artifact filename unavailable (build failed)
134
+ if ( ! artifactFilename ) {
135
+ console . log ( 'Error1: saveArtifact: artifact filename unavailable (build failed)' )
136
+ return false
113
137
}
114
- } )
115
138
116
- return artifactUrl
139
+ const buildFilenamePrEnvStr = ENV_VARS . prNumber ? '-PR-' + ENV_VARS . prNumber : '-' + ENV_VARS . buildEnv
140
+ const buildFolder = ENV_VARS . prNumber ? SP_CONFIG . spPullRequestBuildFolder : SP_CONFIG . spRegressionBuildFolder
141
+ const coreOptions = { siteUrl : SP_CONFIG . spSiteUrl }
142
+ const fileOptions = { folder : ENV_VARS . prNumber ? SP_CONFIG . spPullRequestBuildFolder : SP_CONFIG . spRegressionBuildFolder }
143
+
144
+ // Save build artifacts to SP
145
+ if ( ENV_VARS . buildPlatform === 'ANDROID' ) {
146
+ buildArtifacts . buildApkFilepath = '../../build/app/outputs/apk/release/app-release.apk'
147
+ buildArtifacts . buildApkFinalFilename = ENV_VARS . appVersion + '-' + finalBuildNumber + buildFilenamePrEnvStr + '.apk'
148
+ buildArtifacts . buildApkFinalUrl = ( SP_CONFIG . spSiteUrl + buildFolder + buildArtifacts . buildApkFinalFilename ) . replace ( / / g, '%20' )
149
+ fs . copyFileSync ( buildArtifacts . buildApkFilepath , './' + buildArtifacts . buildApkFinalFilename )
150
+ fileOptions . fileName = buildArtifacts . buildApkFinalFilename
151
+ fileOptions . fileContent = fs . readFileSync ( buildArtifacts . buildApkFinalFilename )
152
+ console . log ( 'Saving artifact `' + fileOptions . fileName + ' to SP...' )
153
+ await spsave ( coreOptions , SP_CONFIG . credentials , fileOptions )
154
+ return true
155
+ } else if ( ENV_VARS . buildPlatform === 'IOS' ) {
156
+ buildArtifacts . buildIpaFilepath = '../../build/ios/ipa/UC San Diego.ipa'
157
+ buildArtifacts . buildIpaFinalFilename = ENV_VARS . appVersion + '-' + finalBuildNumber + buildFilenamePrEnvStr + '.ipa'
158
+ buildArtifacts . buildIpaFinalUrl = ( SP_CONFIG . spSiteUrl + buildFolder + buildArtifacts . buildIpaFinalFilename ) . replace ( / / g, '%20' )
159
+ fs . copyFileSync ( buildArtifacts . buildIpaFilepath , './' + buildArtifacts . buildIpaFinalFilename )
160
+ fileOptions . fileName = buildArtifacts . buildIpaFinalFilename
161
+ fileOptions . fileContent = fs . readFileSync ( buildArtifacts . buildIpaFinalFilename )
162
+ console . log ( 'Saving artifact `' + fileOptions . fileName + ' to SP...' )
163
+ await spsave ( coreOptions , SP_CONFIG . credentials , fileOptions )
164
+ return true
165
+ }
166
+ return false
167
+ } catch ( err ) {
168
+ console . log ( err )
169
+ return false
170
+ }
117
171
}
118
172
119
173
const generateTestPlan = async ( prAuthor ) => {
@@ -126,6 +180,7 @@ const generateTestPlan = async (prAuthor) => {
126
180
testPlanUrl = ( SP_CONFIG . spSiteUrl + SP_CONFIG . spPullRequestTestFolder + testPlanFilename + '?web=1' ) . replace ( / / g, '%20' )
127
181
console . log ( ' (1/3) Downloading PR test plan template ...' )
128
182
if ( ENV_VARS . buildPlatform === 'IOS' ) {
183
+
129
184
fs . copyFileSync ( SP_CONFIG . prTestPlanTemplateUrlIos , testPlanFilename )
130
185
} else if ( ENV_VARS . buildPlatform === 'ANDROID' ) {
131
186
fs . copyFileSync ( SP_CONFIG . prTestPlanTemplateUrlAndroid , testPlanFilename )
@@ -134,7 +189,7 @@ const generateTestPlan = async (prAuthor) => {
134
189
console . log ( 'Generating regression test plan for branch ' + ENV_VARS . buildBranch )
135
190
testPlanFilename = 'Regression-Test-Plan-' + ENV_VARS . appVersion + '-' + ENV_VARS . buildEnv + '-' + finalBuildNumber + '.xlsx'
136
191
testPlanUrl = ( SP_CONFIG . spSiteUrl + SP_CONFIG . spRegressionTestFolder + testPlanFilename + '?web=1' ) . replace ( / / g, '%20' )
137
- switch ( ENV_VARS . buildEnv ) {
192
+ switch ( ENV_VARS . buildEnv ) {
138
193
case 'PROD' :
139
194
console . log ( ' (1/3) Downloading PROD regression test plan template ...' )
140
195
if ( ENV_VARS . buildPlatform === 'IOS' ) {
@@ -180,7 +235,7 @@ const generateTestPlan = async (prAuthor) => {
180
235
181
236
console . log ( ' (3/4) Writing ' + testPlanFilename )
182
237
fs . writeFileSync ( testPlanFilename , Buffer . from (
183
- template . generate ( { type : 'base64' } ) ,
238
+ template . generate ( { type : 'base64' } ) ,
184
239
'base64'
185
240
) )
186
241
@@ -193,10 +248,10 @@ const generateTestPlan = async (prAuthor) => {
193
248
}
194
249
await spsave ( coreOptions , SP_CONFIG . credentials , fileOptions )
195
250
return {
196
- testPlanFilename : testPlanFilename , // eslint-disable-line
197
- testPlanUrl : testPlanUrl , // eslint-disable-line
251
+ testPlanFilename : testPlanFilename ,
252
+ testPlanUrl : testPlanUrl ,
198
253
}
199
- } catch ( err ) {
254
+ } catch ( err ) {
200
255
console . log ( err )
201
256
return null
202
257
}
@@ -213,7 +268,7 @@ const githubMeta = async () => {
213
268
return ghRespJson . user . login
214
269
} catch ( err ) {
215
270
console . log ( err )
216
- return 'N/A '
271
+ return 'n/a '
217
272
}
218
273
}
219
274
0 commit comments