/
androidsigning.ts
208 lines (157 loc) · 5.74 KB
/
androidsigning.ts
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
/*
Copyright (c) Microsoft. All rights reserved.
Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
/// <reference path="../../definitions/node.d.ts"/>
/// <reference path="../../definitions/Q.d.ts" />
/// <reference path="../../definitions/vsts-task-lib.d.ts" />
import path = require('path');
import Q = require('q');
import tl = require('vsts-task-lib/task');
// Define error handler
var onError = function (errorMsg) {
tl.error(errorMsg);
tl.exit(1);
}
/*
Signing the specified file. Move the current file to fn.unsigned, and
place the signed file at the same location fn
*/
var jarsigning = (fn: string) => {
// file must exist
tl.checkPath(fn, 'file to sign');
var java_home = tl.getVariable('JAVA_HOME');
var jarsigner = path.join(java_home, 'bin', 'jarsigner');
var jarsignerRunner = tl.createToolRunner(jarsigner);
// Get keystore file for signing
var keystoreFile = tl.getInput('keystoreFile', true);
tl.debug('keystoreFile: ' + keystoreFile);
// Get keystore alias
var keystoreAlias = tl.getInput('keystoreAlias', true);
tl.debug('keystoreAlias: ' + keystoreAlias);
var keystorePass = tl.getInput('keystorePass', false);
tl.debug('keystorePass nil?: ' + !keystorePass)
var keyPass = tl.getInput('keyPass', false);
tl.debug('keyPass nil?: ' + !keyPass)
var jarsignerArguments = tl.getInput('jarsignerArguments', false);
tl.debug("jarsignerArguments: " + jarsignerArguments);
jarsignerRunner.arg(['-keystore', keystoreFile]);
if (keystorePass) {
jarsignerRunner.arg(['-storepass', keystorePass]);
}
if (keyPass) {
jarsignerRunner.arg(['-keypass', keyPass]);
}
if (jarsignerArguments) {
jarsignerRunner.arg(jarsignerArguments);
}
var unsignedFn = fn + ".unsigned";
var success = tl.mv(fn, unsignedFn, true, false);
jarsignerRunner.arg(['-signedjar', fn, unsignedFn, keystoreAlias]);
return jarsignerRunner.exec(null);
}
/*
Zipaligning apk
*/
var zipaligning = (fn: string) => {
// file must exist
tl.checkPath(fn, 'file to zipalign');
var zipaligner = tl.getInput('zipalignLocation', false);
tl.debug("zipalign tool: " + zipaligner);
// if the tool path is not set, let's find one (anyone) from the SDK folder
if (!zipaligner) {
var android_home = tl.getVariable('ANDROID_HOME');
if (!android_home) {
onError("ANDROID_HOME is not set");
}
var allFiles = tl.find(path.join(android_home, 'build-tools'));
// Now matching the pattern against all files
var zipalignToolsList = tl.match(allFiles, "zipalign*", {matchBase: true});
if (!zipalignToolsList || zipalignToolsList.length === 0) {
onError("Could not find zipalign tool inside ANDROID_HOME: " + android_home);
}
zipaligner = zipalignToolsList[0];
}
if (!zipaligner) {
onError("Could not find zipalign tool.");
}
var zipalignRunner = tl.createToolRunner(zipaligner);
// alignment must be 4 or play store will reject, hard code this to avoid user errors
zipalignRunner.arg(["-v", "4"]);
var unalignedFn = fn + ".unaligned";
var success = tl.mv(fn, unalignedFn, true, false);
zipalignRunner.arg([unalignedFn, fn]);
return zipalignRunner.exec(null);
}
var process = (fn: string) => {
tl.debug('process '+fn);
return Q.fcall(() => {
if (jarsign) {
return jarsigning(fn);
}
return Q(0);
})
.then(() => {
if (zipalign) {
return zipaligning(fn);
}
return Q(0);
})
}
//-----------------------------------------------------------------------------
// Program
//-----------------------------------------------------------------------------
// Get files to be signed
var filesPattern = tl.getInput('files', true);
tl.debug('filesPattern: ' + filesPattern);
// Signing the APK?
var jarsign: boolean = tl.getBoolInput('jarsign');
tl.debug('jarsign: ' + jarsign);
// Zipaligning the APK?
var zipalign: boolean = tl.getBoolInput('zipalign');
tl.debug('zipalign: ' + zipalign);
// Resolve files for the specified value or pattern
if (filesPattern.indexOf('*') == -1 && filesPattern.indexOf('?') == -1) {
// No pattern found, check literal path to a single file
tl.checkPath(filesPattern, 'files');
// Use the specified single file
var filesList = [filesPattern];
} else {
var firstWildcardIndex = function(str) {
var idx = str.indexOf('*');
var idxOfWildcard = str.indexOf('?');
if (idxOfWildcard > -1) {
return (idx > -1) ?
Math.min(idx, idxOfWildcard) : idxOfWildcard;
}
return idx;
}
// Find app files matching the specified pattern
tl.debug('Matching glob pattern: ' + filesPattern);
// First find the most complete path without any matching patterns
var idx = firstWildcardIndex(filesPattern);
tl.debug('Index of first wildcard: ' + idx);
var findPathRoot = path.dirname(filesPattern.slice(0, idx));
tl.debug('find root dir: ' + findPathRoot);
// Now we get a list of all files under this root
var allFiles = tl.find(findPathRoot);
// Now matching the pattern against all files
var filesList: string[] = tl.match(allFiles, filesPattern, {matchBase: true});
// Fail if no matching app files were found
if (!filesList || filesList.length == 0) {
onError('No matching files were found with search pattern: ' + filesPattern);
}
}
var result = Q({});
filesList.forEach((fn) => {
result = result.then(() => {
return process(fn);
})
})
result.then(() => {
tl.exit(0);
})
.fail((err) => {
tl.error(err);
tl.exit(1);
});