setup-php 2.36.0 → 2.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +15 -12
  2. package/lib/config.js +5 -2
  3. package/lib/config.js.map +1 -1
  4. package/lib/core.d.ts +8 -0
  5. package/lib/core.js +55 -0
  6. package/lib/core.js.map +1 -0
  7. package/lib/extensions.js +9 -8
  8. package/lib/extensions.js.map +1 -1
  9. package/lib/fetch.js +25 -70
  10. package/lib/fetch.js.map +1 -1
  11. package/lib/install.js +3 -3
  12. package/lib/install.js.map +1 -1
  13. package/lib/tools.d.ts +50 -21
  14. package/lib/tools.js +211 -150
  15. package/lib/tools.js.map +1 -1
  16. package/lib/utils.d.ts +3 -0
  17. package/lib/utils.js +62 -26
  18. package/lib/utils.js.map +1 -1
  19. package/package.json +20 -20
  20. package/src/config.ts +5 -2
  21. package/src/configs/brew_extensions +19 -0
  22. package/src/configs/composer-gh-auth-no-op +3 -0
  23. package/src/configs/composer-gh-auth-warn +1 -0
  24. package/src/configs/os_releases.csv +4 -1
  25. package/src/core.ts +112 -0
  26. package/src/extensions.ts +16 -15
  27. package/src/fetch.ts +28 -42
  28. package/src/install.ts +6 -3
  29. package/src/scripts/darwin.sh +12 -6
  30. package/src/scripts/extensions/couchbase.sh +13 -2
  31. package/src/scripts/extensions/firebird.sh +6 -23
  32. package/src/scripts/extensions/gearman.sh +1 -1
  33. package/src/scripts/extensions/http.ps1 +7 -5
  34. package/src/scripts/extensions/phalcon.ps1 +3 -21
  35. package/src/scripts/extensions/phalcon.sh +2 -0
  36. package/src/scripts/extensions/relay.sh +5 -2
  37. package/src/scripts/extensions/source.sh +3 -1
  38. package/src/scripts/extensions/sqlsrv.ps1 +2 -0
  39. package/src/scripts/extensions/sqlsrv.sh +2 -0
  40. package/src/scripts/linux.sh +49 -9
  41. package/src/scripts/tools/add_tools.ps1 +75 -27
  42. package/src/scripts/tools/add_tools.sh +67 -23
  43. package/src/scripts/tools/blackfire.sh +1 -1
  44. package/src/scripts/tools/brew.sh +130 -0
  45. package/src/scripts/tools/grpc_php_plugin.sh +2 -2
  46. package/src/scripts/tools/ppa.sh +5 -1
  47. package/src/scripts/unix.sh +7 -3
  48. package/src/scripts/win32.ps1 +17 -11
  49. package/src/tools.ts +349 -203
  50. package/src/utils.ts +81 -34
package/src/tools.ts CHANGED
@@ -5,14 +5,130 @@ import * as fetch from './fetch';
5
5
  import * as packagist from './packagist';
6
6
  import * as utils from './utils';
7
7
 
8
- type RS = Record<string, string>;
9
- type RSRS = Record<string, RS>;
8
+ /**
9
+ * Valid function names for custom tool handlers
10
+ */
11
+ type ToolFunction =
12
+ | 'castor'
13
+ | 'composer'
14
+ | 'deployer'
15
+ | 'dev_tools'
16
+ | 'phive'
17
+ | 'blackfire_player'
18
+ | 'pecl'
19
+ | 'phing'
20
+ | 'phpunit'
21
+ | 'phpcpd'
22
+ | 'wp_cli';
23
+
24
+ /**
25
+ * Tool data interface containing all properties for tool installation
26
+ */
27
+ export interface ToolData {
28
+ tool: string;
29
+ version: string;
30
+ os: string;
31
+ php_version: string;
32
+ github: string;
33
+ domain: string;
34
+ extension: string;
35
+ repository: string;
36
+ prefix: string;
37
+ verb: string;
38
+ fetch_latest: 'true' | 'false';
39
+ scope: string;
40
+ version_parameter: string;
41
+ version_prefix: string;
42
+ release: string;
43
+ packagist: string;
44
+ type?: string;
45
+ function?: ToolFunction;
46
+ alias?: string;
47
+ url: string;
48
+ uri?: string;
49
+ error?: string;
50
+ }
51
+
52
+ /**
53
+ * Input type for functions that may receive partial/unresolved tool data
54
+ * Used by getUrl, getLatestVersion etc. before version is fully resolved
55
+ */
56
+ export type ToolInput = Omit<ToolData, 'version' | 'url'> & {version?: string};
57
+
58
+ /**
59
+ * Partial tool data from tools.json configuration
60
+ */
61
+ interface ToolConfig {
62
+ tool?: string;
63
+ repository?: string;
64
+ type?: string;
65
+ function?: ToolFunction;
66
+ alias?: string;
67
+ domain?: string;
68
+ extension?: string;
69
+ fetch_latest?: 'true' | 'false';
70
+ scope?: string;
71
+ version_parameter?: string;
72
+ version_prefix?: string;
73
+ packagist?: string;
74
+ }
10
75
 
11
- interface IRef {
76
+ /**
77
+ * GitHub reference object from API response
78
+ */
79
+ interface GitHubRef {
12
80
  ref: string;
13
81
  node_id: string;
14
82
  url: string;
15
- object: RS;
83
+ object: {
84
+ sha: string;
85
+ type: string;
86
+ url: string;
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Deployer manifest entry
92
+ */
93
+ interface DeployerManifestEntry {
94
+ version: string;
95
+ url: string;
96
+ }
97
+
98
+ export function skipGitHubAuthForComposerVersion(version: string): boolean {
99
+ if (!/^\d+\.\d+\.\d+(?:-[\w-]+)?$/.test(version)) {
100
+ return false;
101
+ }
102
+ return fs
103
+ .readFileSync(
104
+ path.join(__dirname, '../src/configs/composer-gh-auth-no-op'),
105
+ 'utf8'
106
+ )
107
+ .trim()
108
+ .split(/\r?\n/)
109
+ .some(range => {
110
+ const [min, max] = range.trim().split(/\s+/);
111
+ return (
112
+ cv.compareVersions(version, min) >= 0 &&
113
+ cv.compareVersions(version, max) < 0
114
+ );
115
+ });
116
+ }
117
+
118
+ export function cleanComposerAuthJson(): void {
119
+ try {
120
+ const auth_json = process.env['COMPOSER_AUTH_JSON'] || '';
121
+ if (!auth_json.includes('github-oauth')) return;
122
+ const auth = JSON.parse(auth_json);
123
+ delete auth['github-oauth'];
124
+ if (!Object.keys(auth).length) {
125
+ delete process.env['COMPOSER_AUTH_JSON'];
126
+ } else {
127
+ process.env['COMPOSER_AUTH_JSON'] = JSON.stringify(auth);
128
+ }
129
+ } catch {
130
+ return;
131
+ }
16
132
  }
17
133
 
18
134
  /**
@@ -20,7 +136,7 @@ interface IRef {
20
136
  *
21
137
  * @param data
22
138
  */
23
- export async function getSemverVersion(data: RS): Promise<string> {
139
+ export async function getSemverVersion(data: ToolData): Promise<string> {
24
140
  const fixSemver = (t: string): string => {
25
141
  if (/^\d+\.\d+\.\d+(-|$)/.test(t)) return t;
26
142
  const m = t.match(/^(\d+\.\d+\.\d+)([A-Za-z]+[0-9A-Za-z.]+)$/);
@@ -31,14 +147,16 @@ export async function getSemverVersion(data: RS): Promise<string> {
31
147
  const github_token: string =
32
148
  (await utils.readEnv('GITHUB_TOKEN')) ||
33
149
  (await utils.readEnv('COMPOSER_TOKEN'));
34
- const response: RS = await fetch.fetch(url, github_token);
150
+ const response = await fetch.fetch(url, github_token);
35
151
  if (response.error || response.data === '[]') {
36
- data['error'] = response.error ?? `No version found with prefix ${search}.`;
37
- return data['version'];
152
+ data.error = response.error ?? `No version found with prefix ${search}.`;
153
+ return data.version;
38
154
  } else {
39
- const refs: IRef[] = JSON.parse(response['data']);
155
+ const refs: GitHubRef[] = JSON.parse(response.data);
40
156
  const tags = refs
41
- .map((i: IRef) => (i.ref?.split('/').pop() ?? '').replace(/^v(?=\d)/, ''))
157
+ .map((i: GitHubRef) =>
158
+ (i.ref?.split('/').pop() ?? '').replace(/^v(?=\d)/, '')
159
+ )
42
160
  .filter((t: string) => t.length > 0);
43
161
  const fixedToOriginal = new Map<string, string>();
44
162
  const fixed = tags.map(t => {
@@ -46,14 +164,14 @@ export async function getSemverVersion(data: RS): Promise<string> {
46
164
  fixedToOriginal.set(f, t);
47
165
  return f;
48
166
  });
49
- fixed.sort((a, b) => {
167
+ const sorted = fixed.toSorted((a, b) => {
50
168
  try {
51
169
  return cv.compareVersions(b, a);
52
170
  } catch {
53
171
  return b.localeCompare(a, 'en', {numeric: true, sensitivity: 'base'});
54
172
  }
55
173
  });
56
- return fixedToOriginal.get(fixed[0]) ?? fixed[0];
174
+ return fixedToOriginal.get(sorted[0]) ?? sorted[0];
57
175
  }
58
176
  }
59
177
 
@@ -62,25 +180,25 @@ export async function getSemverVersion(data: RS): Promise<string> {
62
180
  *
63
181
  * @param data
64
182
  */
65
- export async function getLatestVersion(data: RS): Promise<string> {
66
- if (!data['version'] && data['fetch_latest'] === 'false') {
183
+ export async function getLatestVersion(data: ToolInput): Promise<string> {
184
+ if (!data.version && data.fetch_latest === 'false') {
185
+ return 'latest';
186
+ }
187
+ if (data.fetch_latest === 'true' && !data.repository) {
67
188
  return 'latest';
68
189
  }
69
- const resp: Record<string, string> = await fetch.fetch(
70
- `${data['github']}/${data['repository']}/releases.atom`
190
+ const resp = await fetch.fetch(
191
+ `${data.github}/${data.repository}/releases.atom`
71
192
  );
72
- if (resp['data']) {
193
+ if (resp.data) {
73
194
  const releases: string[] = [
74
- ...resp['data'].matchAll(/releases\/tag\/([a-zA-Z]*)?(\d+.\d+.\d+)"/g)
195
+ ...resp.data.matchAll(/releases\/tag\/([a-zA-Z]*)?(\d+\.\d+\.\d+)"/g)
75
196
  ].map(match => match[2]);
76
197
 
77
- return (
78
- releases
79
- .sort((a: string, b: string) =>
80
- a.localeCompare(b, undefined, {numeric: true})
81
- )
82
- .pop() || 'latest'
198
+ const sorted = releases.toSorted((a: string, b: string) =>
199
+ a.localeCompare(b, undefined, {numeric: true})
83
200
  );
201
+ return sorted.at(-1) || 'latest';
84
202
  }
85
203
  return 'latest';
86
204
  }
@@ -91,26 +209,29 @@ export async function getLatestVersion(data: RS): Promise<string> {
91
209
  * @param version
92
210
  * @param data
93
211
  */
94
- export async function getVersion(version: string, data: RS): Promise<string> {
212
+ export async function getVersion(
213
+ version: string,
214
+ data: ToolData
215
+ ): Promise<string> {
95
216
  // semver_regex - https://semver.org/
96
217
  const semver_regex =
97
218
  /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
98
- const composer_regex = /^composer:(stable|preview|snapshot|[1|2])$/;
219
+ const composer_regex = /^composer:(stable|preview|snapshot|[12])$/;
99
220
  const constraint_regex = /[><=^~]+.*/;
100
221
  const major_minor_regex = /^\d+(\.\d+)?$/;
101
- data['version'] = version.replace(/v?(\d)/, '$1').replace(/\.x/, '');
222
+ data.version = version.replace(/v?(\d)/, '$1').replace(/\.x/, '');
102
223
  switch (true) {
103
- case composer_regex.test(data['release']):
104
- case semver_regex.test(data['version']):
105
- case constraint_regex.test(data['version']) && data['type'] === 'composer':
106
- return data['version'];
107
- case major_minor_regex.test(data['version']) && data['type'] === 'composer':
108
- data['release'] = `${data['tool']}:${data['version']}.*`;
109
- return `${data['version']}.*`;
110
- case data['repository'] && major_minor_regex.test(data['version']):
224
+ case composer_regex.test(data.release):
225
+ case semver_regex.test(data.version):
226
+ case constraint_regex.test(data.version) && data.type === 'composer':
227
+ return data.version;
228
+ case major_minor_regex.test(data.version) && data.type === 'composer':
229
+ data.release = `${data.tool}:${data.version}.*`;
230
+ return `${data.version}.*`;
231
+ case !!data.repository && major_minor_regex.test(data.version):
111
232
  return await getSemverVersion(data);
112
233
  default:
113
- return data['version'].replace(/[><=^~]*/, '');
234
+ return data.version.replace(/[^a-zA-Z0-9_.:@+,/-]/g, '');
114
235
  }
115
236
  }
116
237
 
@@ -120,11 +241,14 @@ export async function getVersion(version: string, data: RS): Promise<string> {
120
241
  * @param release
121
242
  * @param data
122
243
  */
123
- export async function getRelease(release: string, data: RS): Promise<string> {
244
+ export async function getRelease(
245
+ release: string,
246
+ data: ToolData
247
+ ): Promise<string> {
124
248
  release = release.includes('/') ? release.split('/')[1] : release;
125
249
  return release.includes(':')
126
- ? [data['tool'], release.split(':')[1]].join(':')
127
- : data['tool'];
250
+ ? [data.tool, release.split(':')[1]].join(':')
251
+ : data.tool;
128
252
  }
129
253
 
130
254
  /**
@@ -143,7 +267,7 @@ export async function filterList(tools_list: string[]): Promise<string[]> {
143
267
  case matches[0] == undefined:
144
268
  break;
145
269
  default:
146
- composer = matches[matches.length - 1].replace(/v(\d\S*)/, '$1');
270
+ composer = matches.at(-1)!.replace(/v(\d\S*)/, '$1');
147
271
  break;
148
272
  }
149
273
  tools_list.unshift(composer);
@@ -155,26 +279,27 @@ export async function filterList(tools_list: string[]): Promise<string[]> {
155
279
  *
156
280
  * @param data
157
281
  */
158
- export async function getUrl(data: RS): Promise<string> {
159
- if ((data['version'] ?? 'latest') === 'latest') {
282
+ export async function getUrl(data: ToolInput): Promise<string> {
283
+ const version = data.version ?? 'latest';
284
+ if (version === 'latest' || version === '') {
160
285
  return [
161
- data['domain'],
162
- data['repository'],
163
- data['prefix'],
164
- data['version'],
165
- data['verb'],
166
- data['tool'] + data['extension']
286
+ data.domain,
287
+ data.repository,
288
+ data.prefix,
289
+ 'latest',
290
+ data.verb,
291
+ data.tool + data.extension
167
292
  ]
168
293
  .filter(Boolean)
169
294
  .join('/');
170
295
  } else {
171
296
  return [
172
- data['domain'],
173
- data['repository'],
174
- data['prefix'],
175
- data['verb'],
176
- data['version_prefix'] + data['version'],
177
- data['tool'] + data['extension']
297
+ data.domain,
298
+ data.repository,
299
+ data.prefix,
300
+ data.verb,
301
+ data.version_prefix + data.version,
302
+ data.tool + data.extension
178
303
  ]
179
304
  .filter(Boolean)
180
305
  .join('/');
@@ -186,17 +311,17 @@ export async function getUrl(data: RS): Promise<string> {
186
311
  *
187
312
  * @param data
188
313
  */
189
- export async function getPharUrl(data: RS): Promise<string> {
190
- if (data['version'] === 'latest') {
191
- return data['domain'] + '/' + data['tool'] + '.phar';
314
+ export async function getPharUrl(data: ToolData): Promise<string> {
315
+ if (data.version === 'latest') {
316
+ return data.domain + '/' + data.tool + '.phar';
192
317
  } else {
193
318
  return (
194
- data['domain'] +
319
+ data.domain +
195
320
  '/' +
196
- data['tool'] +
321
+ data.tool +
197
322
  '-' +
198
- data['version_prefix'] +
199
- data['version'] +
323
+ data.version_prefix +
324
+ data.version +
200
325
  '.phar'
201
326
  );
202
327
  }
@@ -207,10 +332,10 @@ export async function getPharUrl(data: RS): Promise<string> {
207
332
  *
208
333
  * @param data
209
334
  */
210
- export async function addArchive(data: RS): Promise<string> {
335
+ export async function addArchive(data: ToolData): Promise<string> {
211
336
  return (
212
- (await utils.getCommand(data['os'], 'tool')) +
213
- (await utils.joins(data['url'], data['tool'], data['version_parameter']))
337
+ (await utils.getCommand(data.os, 'tool')) +
338
+ (await utils.joins(data.url, data.tool, data.version_parameter))
214
339
  );
215
340
  }
216
341
 
@@ -219,15 +344,12 @@ export async function addArchive(data: RS): Promise<string> {
219
344
  *
220
345
  * @param data
221
346
  */
222
- export async function addPackage(data: RS): Promise<string> {
223
- const command = await utils.getCommand(data['os'], 'composer_tool');
224
- const parts: string[] = data['repository'].split('/');
225
- const args: string = await utils.joins(
226
- parts[1],
227
- data['release'],
228
- parts[0] + '/',
229
- data['scope']
230
- );
347
+ export async function addPackage(data: ToolData): Promise<string> {
348
+ const command = await utils.getCommand(data.os, 'composer_tool');
349
+ const parts: string[] = data.repository.split('/');
350
+ const args = [parts[1], data.release, parts[0] + '/', data.scope]
351
+ .map(a => utils.safeArg(a, data.os))
352
+ .join(' ');
231
353
  return command + args;
232
354
  }
233
355
 
@@ -236,24 +358,24 @@ export async function addPackage(data: RS): Promise<string> {
236
358
  *
237
359
  * @param data
238
360
  */
239
- export async function addBlackfirePlayer(data: RS): Promise<string> {
240
- switch (data['os']) {
361
+ export async function addBlackfirePlayer(data: ToolData): Promise<string> {
362
+ switch (data.os) {
241
363
  case 'win32':
242
364
  return await utils.addLog(
243
365
  '$cross',
244
- data['tool'],
245
- data['tool'] + ' is not a windows tool',
366
+ data.tool,
367
+ data.tool + ' is not a windows tool',
246
368
  'win32'
247
369
  );
248
370
  default:
249
- if (data['version'] == 'latest') {
250
- if (/5\.[5-6]|7\.0/.test(data['php_version'])) {
251
- data['version'] = '1.9.3';
252
- } else if (/7\.[1-4]|8\.0/.test(data['php_version'])) {
253
- data['version'] = '1.22.0';
371
+ if (data.version == 'latest') {
372
+ if (/5\.[5-6]|7\.0/.test(data.php_version)) {
373
+ data.version = '1.9.3';
374
+ } else if (/7\.[1-4]|8\.0/.test(data.php_version)) {
375
+ data.version = '1.22.0';
254
376
  }
255
377
  }
256
- data['url'] = await getPharUrl(data);
378
+ data.url = await getPharUrl(data);
257
379
  return addArchive(data);
258
380
  }
259
381
  }
@@ -263,12 +385,12 @@ export async function addBlackfirePlayer(data: RS): Promise<string> {
263
385
  *
264
386
  * @param data
265
387
  */
266
- export async function addCastor(data: RS): Promise<string> {
267
- data['tool'] = 'castor.' + data['os'].replace('win32', 'windows') + '-amd64';
268
- data['url'] = await getUrl(data);
269
- data['tool'] = 'castor';
270
- data['version_parameter'] = fs.existsSync('castor.php')
271
- ? data['version_parameter']
388
+ export async function addCastor(data: ToolData): Promise<string> {
389
+ data.tool = 'castor.' + data.os.replace('win32', 'windows') + '-amd64';
390
+ data.url = await getUrl(data);
391
+ data.tool = 'castor';
392
+ data.version_parameter = fs.existsSync('castor.php')
393
+ ? data.version_parameter
272
394
  : '';
273
395
  return await addArchive(data);
274
396
  }
@@ -278,22 +400,23 @@ export async function addCastor(data: RS): Promise<string> {
278
400
  *
279
401
  * @param data
280
402
  */
281
- export async function addComposer(data: RS): Promise<string> {
282
- const channel = data['version'].replace('latest', 'stable');
283
- const github = data['github'];
284
- const getcomposer = data['domain'];
403
+ export async function addComposer(data: ToolData): Promise<string> {
404
+ const channel = data.version.replace('latest', 'stable');
405
+ const github = data.github;
406
+ const getcomposer = data.domain;
285
407
  const cds = 'https://dl.cloudsmith.io';
286
408
  const spc = 'https://artifacts.setup-php.com';
287
- const filename = `composer-${data['php_version']}-${channel}.phar`;
409
+ const filename = `composer-${data.php_version}-${channel}.phar`;
288
410
  const releases_url = `${github}/shivammathur/composer-cache/releases/latest/download/${filename}`;
289
411
  const cds_url = `${cds}/public/shivammathur/composer-cache/raw/files/${filename}`;
290
412
  const spc_url = `${spc}/composer/${filename}`;
291
413
  const lts_url = `${getcomposer}/download/latest-2.2.x/composer.phar`;
292
- const is_lts = /^5\.[3-6]$|^7\.[0-1]$/.test(data['php_version']);
414
+ const is_lts = /^5\.[3-6]$|^7\.[0-1]$/.test(data.php_version);
293
415
  const channel_source_url = `${getcomposer}/composer-${channel}.phar`;
294
416
  const version_source_url = `${getcomposer}/download/${channel}/composer.phar`;
295
417
  let cache_url = `${releases_url},${spc_url},${cds_url}`;
296
418
  let source_url = `${getcomposer}/composer.phar`;
419
+ let skip_composer_github_auth = '';
297
420
  switch (true) {
298
421
  case /^snapshot$/.test(channel):
299
422
  source_url = is_lts ? lts_url : source_url;
@@ -304,16 +427,20 @@ export async function addComposer(data: RS): Promise<string> {
304
427
  case /^1$/.test(channel):
305
428
  source_url = channel_source_url;
306
429
  break;
307
- case /^\d+\.\d+\.\d+[\w-]*$/.test(data['version']):
308
- cache_url = `${github}/${data['repository']}/releases/download/${data['version']}/composer.phar`;
430
+ case /^\d+\.\d+\.\d+(?:-[\w-]+)?$/.test(data.version):
431
+ if (skipGitHubAuthForComposerVersion(data.version)) {
432
+ cleanComposerAuthJson();
433
+ skip_composer_github_auth = ' true';
434
+ }
435
+ cache_url = `${github}/${data.repository}/releases/download/${data.version}/composer.phar`;
309
436
  source_url = version_source_url;
310
437
  break;
311
438
  default:
312
439
  source_url = is_lts ? lts_url : channel_source_url;
313
440
  }
314
441
  const use_cache: boolean = (await utils.readEnv('NO_TOOLS_CACHE')) !== 'true';
315
- data['url'] = use_cache ? `${cache_url},${source_url}` : source_url;
316
- data['version_parameter'] = data['version'];
442
+ data.url = use_cache ? `${cache_url},${source_url}` : source_url;
443
+ data.version_parameter = data.version + skip_composer_github_auth;
317
444
  return await addArchive(data);
318
445
  }
319
446
 
@@ -322,27 +449,27 @@ export async function addComposer(data: RS): Promise<string> {
322
449
  *
323
450
  * @param data
324
451
  */
325
- export async function addDeployer(data: RS): Promise<string> {
326
- if (data['version'] === 'latest') {
327
- data['url'] = data['domain'] + '/deployer.phar';
452
+ export async function addDeployer(data: ToolData): Promise<string> {
453
+ if (data.version === 'latest') {
454
+ data.url = data.domain + '/deployer.phar';
328
455
  } else {
329
- const manifest: RS = await fetch.fetch(
330
- 'https://deployer.org/manifest.json'
456
+ const manifest = await fetch.fetch('https://deployer.org/manifest.json');
457
+ const version_data: Record<string, DeployerManifestEntry> = JSON.parse(
458
+ manifest.data
331
459
  );
332
- const version_data: RSRS = JSON.parse(manifest.data);
333
460
  const version_key: string | undefined = Object.keys(version_data).find(
334
461
  (key: string) => {
335
- return version_data[key]['version'] === data['version'];
462
+ return version_data[key].version === data.version;
336
463
  }
337
464
  );
338
465
  if (version_key) {
339
- data['url'] = version_data[version_key]['url'];
466
+ data.url = version_data[version_key].url;
340
467
  } else {
341
468
  return await utils.addLog(
342
469
  '$cross',
343
470
  'deployer',
344
471
  'Version missing in deployer manifest',
345
- data['os']
472
+ data.os
346
473
  );
347
474
  }
348
475
  }
@@ -354,22 +481,22 @@ export async function addDeployer(data: RS): Promise<string> {
354
481
  *
355
482
  * @param data
356
483
  */
357
- export async function addDevTools(data: RS): Promise<string> {
358
- switch (data['os']) {
484
+ export async function addDevTools(data: ToolData): Promise<string> {
485
+ switch (data.os) {
359
486
  case 'linux':
360
487
  case 'darwin':
361
- return 'add_devtools ' + data['tool'];
488
+ return 'add_devtools ' + data.tool;
362
489
  case 'win32':
363
490
  return await utils.addLog(
364
491
  '$tick',
365
- data['tool'],
366
- data['tool'] + ' is not a windows tool',
492
+ data.tool,
493
+ data.tool + ' is not a windows tool',
367
494
  'win32'
368
495
  );
369
496
  default:
370
497
  return await utils.log(
371
- 'Platform ' + data['os'] + ' is not supported',
372
- data['os'],
498
+ 'Platform ' + data.os + ' is not supported',
499
+ data.os,
373
500
  'error'
374
501
  );
375
502
  }
@@ -380,8 +507,8 @@ export async function addDevTools(data: RS): Promise<string> {
380
507
  *
381
508
  * @param data
382
509
  */
383
- export async function addPECL(data: RS): Promise<string> {
384
- return await utils.getCommand(data['os'], 'pecl');
510
+ export async function addPECL(data: ToolData): Promise<string> {
511
+ return await utils.getCommand(data.os, 'pecl');
385
512
  }
386
513
 
387
514
  /**
@@ -389,14 +516,13 @@ export async function addPECL(data: RS): Promise<string> {
389
516
  *
390
517
  * @param data
391
518
  */
392
- export async function addPhing(data: RS): Promise<string> {
393
- data['url'] =
394
- data['domain'] + '/get/phing-' + data['version'] + data['extension'];
395
- if (data['version'] != 'latest') {
396
- [data['prefix'], data['verb']] = ['releases', 'download'];
397
- data['domain'] = data['github'];
398
- data['extension'] = '-' + data['version'] + data['extension'];
399
- data['url'] += ',' + (await getUrl(data));
519
+ export async function addPhing(data: ToolData): Promise<string> {
520
+ data.url = data.domain + '/get/phing-' + data.version + data.extension;
521
+ if (data.version != 'latest') {
522
+ [data.prefix, data.verb] = ['releases', 'download'];
523
+ data.domain = data.github;
524
+ data.extension = '-' + data.version + data.extension;
525
+ data.url += ',' + (await getUrl(data));
400
526
  }
401
527
  return await addArchive(data);
402
528
  }
@@ -406,33 +532,33 @@ export async function addPhing(data: RS): Promise<string> {
406
532
  *
407
533
  * @param data
408
534
  */
409
- export async function addPhive(data: RS): Promise<string> {
535
+ export async function addPhive(data: ToolData): Promise<string> {
410
536
  switch (true) {
411
- case /5\.[3-5]/.test(data['php_version']):
537
+ case /5\.[3-5]/.test(data.php_version):
412
538
  return await utils.addLog(
413
539
  '$cross',
414
540
  'phive',
415
- 'Phive is not supported on PHP ' + data['php_version'],
416
- data['os']
541
+ 'Phive is not supported on PHP ' + data.php_version,
542
+ data.os
417
543
  );
418
- case /5\.6|7\.0/.test(data['php_version']):
419
- data['version'] = '0.12.1';
544
+ case /5\.6|7\.0/.test(data.php_version):
545
+ data.version = '0.12.1';
420
546
  break;
421
- case /7\.1/.test(data['php_version']):
422
- data['version'] = '0.13.5';
547
+ case /7\.1/.test(data.php_version):
548
+ data.version = '0.13.5';
423
549
  break;
424
- case /7\.2/.test(data['php_version']):
425
- data['version'] = '0.14.5';
550
+ case /7\.2/.test(data.php_version):
551
+ data.version = '0.14.5';
426
552
  break;
427
- case /7\.3|7\.4/.test(data['php_version']):
428
- data['version'] = '0.15.3';
553
+ case /7\.3|7\.4/.test(data.php_version):
554
+ data.version = '0.15.3';
429
555
  break;
430
- case /^latest$/.test(data['version']):
431
- data['version'] = await getLatestVersion(data);
556
+ case /^latest$/.test(data.version):
557
+ data.version = await getLatestVersion(data);
432
558
  break;
433
559
  }
434
- data['extension'] = '-' + data['version'] + data['extension'];
435
- data['url'] = await getUrl(data);
560
+ data.extension = '-' + data.version + data.extension;
561
+ data.url = await getUrl(data);
436
562
  return await addArchive(data);
437
563
  }
438
564
 
@@ -441,16 +567,15 @@ export async function addPhive(data: RS): Promise<string> {
441
567
  *
442
568
  * @param data
443
569
  */
444
- export async function addPHPUnitTools(data: RS): Promise<string> {
570
+ export async function addPHPUnitTools(data: ToolData): Promise<string> {
445
571
  /* istanbul ignore next */
446
- if (data['version'] === 'latest') {
447
- data['version'] =
448
- (await packagist.search(data['packagist'], data['php_version'])) ??
449
- 'latest';
572
+ if (data.version === 'latest') {
573
+ data.version =
574
+ (await packagist.search(data.packagist, data.php_version)) ?? 'latest';
450
575
  }
451
- data['url'] = await getPharUrl(data);
452
- if (data['url'].match(/-\d+/)) {
453
- data['url'] += ',' + data['url'].replace(/-(\d+)\.\d+\.\d+/, '-$1');
576
+ data.url = await getPharUrl(data);
577
+ if (data.url.match(/-\d+/)) {
578
+ data.url += ',' + data.url.replace(/-(\d+)\.\d+\.\d+/, '-$1');
454
579
  }
455
580
  return await addArchive(data);
456
581
  }
@@ -460,13 +585,13 @@ export async function addPHPUnitTools(data: RS): Promise<string> {
460
585
  *
461
586
  * @param data
462
587
  */
463
- export async function addWPCLI(data: RS): Promise<string> {
464
- if (data['version'] === 'latest') {
465
- data['uri'] = 'wp-cli/builds/blob/gh-pages/phar/wp-cli.phar?raw=true';
466
- data['url'] = [data['domain'], data['uri']].join('/');
588
+ export async function addWPCLI(data: ToolData): Promise<string> {
589
+ if (data.version === 'latest') {
590
+ data.uri = 'wp-cli/builds/blob/gh-pages/phar/wp-cli.phar?raw=true';
591
+ data.url = [data.domain, data.uri].join('/');
467
592
  } else {
468
- data['extension'] = '-' + data['version'] + data['extension'];
469
- data['url'] = await getUrl(data);
593
+ data.extension = '-' + data.version + data.extension;
594
+ data.url = await getUrl(data);
470
595
  }
471
596
  return await addArchive(data);
472
597
  }
@@ -482,56 +607,74 @@ export async function getData(
482
607
  release: string,
483
608
  php_version: string,
484
609
  os: string
485
- ): Promise<RS> {
610
+ ): Promise<ToolData> {
486
611
  const json_file_path = path.join(__dirname, '../src/configs/tools.json');
487
612
  const json_file: string = fs.readFileSync(json_file_path, 'utf8');
488
- const json_objects: RSRS = JSON.parse(json_file);
613
+ const json_objects: Record<string, ToolConfig> = JSON.parse(json_file);
489
614
  release = release.replace(/\s+/g, '');
490
615
  const parts: string[] = release.split(':');
491
616
  const tool = parts[0];
492
617
  const version = parts[1];
493
- let data: RS;
494
- if (Object.keys(json_objects).includes(tool)) {
495
- data = json_objects[tool];
496
- data['tool'] = tool;
618
+ let config: ToolConfig & {tool: string};
619
+ if (Object.hasOwn(json_objects, tool)) {
620
+ config = {...json_objects[tool], tool};
497
621
  } else {
498
622
  const key: string | undefined = Object.keys(json_objects).find(
499
623
  (key: string) => {
500
- return json_objects[key]['alias'] == tool;
624
+ return json_objects[key].alias == tool;
501
625
  }
502
626
  );
503
627
  if (key) {
504
- data = json_objects[key];
505
- data['tool'] = key;
506
- } else {
507
- data = {
628
+ config = {...json_objects[key], tool: key};
629
+ } else if (tool.includes('/')) {
630
+ config = {
508
631
  tool: tool.split('/')[1],
509
632
  repository: tool,
510
633
  type: 'composer'
511
634
  };
512
- data = !tool.includes('/') ? {tool: tool} : data;
635
+ } else {
636
+ config = {tool};
513
637
  }
514
638
  }
515
- data['github'] = 'https://github.com';
516
- data['domain'] ??= data['github'];
517
- data['extension'] ??= '.phar';
518
- data['os'] = os;
519
- data['php_version'] = php_version;
520
- data['packagist'] ??= data['repository'];
521
- data['prefix'] = data['github'] === data['domain'] ? 'releases' : '';
522
- data['verb'] = data['github'] === data['domain'] ? 'download' : '';
523
- data['fetch_latest'] ??= 'false';
524
- data['scope'] ??= 'global';
525
- data['version_parameter'] = JSON.stringify(data['version_parameter']) || '';
526
- data['version_prefix'] ??= '';
527
- data['release'] = await getRelease(release, data);
528
- data['version'] = version
639
+ const github = 'https://github.com';
640
+ const domain = config.domain ?? github;
641
+ const data: ToolData = {
642
+ tool: config.tool,
643
+ version: '',
644
+ url: '',
645
+ os,
646
+ php_version,
647
+ github,
648
+ domain,
649
+ extension: config.extension ?? '.phar',
650
+ repository: config.repository ?? '',
651
+ prefix: domain === github ? 'releases' : '',
652
+ verb: domain === github ? 'download' : '',
653
+ fetch_latest: config.fetch_latest ?? 'false',
654
+ scope: config.scope ?? 'global',
655
+ version_parameter:
656
+ config.version_parameter != null
657
+ ? JSON.stringify(config.version_parameter)
658
+ : '',
659
+ version_prefix: config.version_prefix ?? '',
660
+ release: '',
661
+ packagist: config.packagist ?? config.repository ?? '',
662
+ type: config.type,
663
+ function: config.function,
664
+ alias: config.alias
665
+ };
666
+ data.release = await getRelease(release, data);
667
+ data.version = version
529
668
  ? await getVersion(version, data)
530
669
  : await getLatestVersion(data);
670
+ data.url = await getUrl(data);
531
671
  return data;
532
672
  }
533
673
 
534
- export const functionRecord: Record<string, (data: RS) => Promise<string>> = {
674
+ export const functionRecord: Record<
675
+ ToolFunction,
676
+ (data: ToolData) => Promise<string>
677
+ > = {
535
678
  castor: addCastor,
536
679
  composer: addComposer,
537
680
  deployer: addDeployer,
@@ -565,43 +708,46 @@ export async function addTools(
565
708
  }
566
709
  const tools_list = await filterList(await utils.CSVArray(tools_csv));
567
710
  await utils.asyncForEach(tools_list, async function (release: string) {
568
- const data: RS = await getData(release, php_version, os);
711
+ const data: ToolData = await getData(release, php_version, os);
569
712
  script += '\n';
570
713
  switch (true) {
571
- case data['error'] !== undefined:
572
- script += await utils.addLog(
573
- '$cross',
574
- data['tool'],
575
- data['error'],
576
- data['os']
577
- );
714
+ case data.error !== undefined:
715
+ script += await utils.addLog('$cross', data.tool, data.error, data.os);
578
716
  break;
579
- case 'phar' === data['type']:
580
- data['url'] = await getUrl(data);
717
+ case 'phar' === data.type:
581
718
  script += await addArchive(data);
582
719
  break;
583
- case 'composer' === data['type']:
720
+ case 'composer' === data.type:
584
721
  script += await addPackage(data);
585
722
  break;
586
- case 'custom-package' === data['type']:
723
+ case 'custom-package' === data.type:
587
724
  script += await utils.customPackage(
588
- data['tool'].split('-')[0],
725
+ data.tool.split('-')[0],
589
726
  'tools',
590
- data['version'],
591
- data['os']
727
+ data.version,
728
+ data.os
592
729
  );
593
730
  break;
594
- case 'custom-function' === data['type']:
595
- script += await functionRecord[data['function']](data);
731
+ case 'custom-function' === data.type:
732
+ if (!data.function) {
733
+ script += await utils.addLog(
734
+ '$cross',
735
+ data.tool,
736
+ data.tool + ' has no function defined. Please report this issue.',
737
+ data.os
738
+ );
739
+ } else {
740
+ script += await functionRecord[data.function](data);
741
+ }
596
742
  break;
597
- case /^none$/.test(data['tool']):
743
+ case /^none$/.test(data.tool):
598
744
  break;
599
745
  default:
600
746
  script += await utils.addLog(
601
747
  '$cross',
602
- data['tool'],
603
- 'Tool ' + data['tool'] + ' is not supported',
604
- data['os']
748
+ data.tool,
749
+ 'Tool ' + data.tool + ' is not supported',
750
+ data.os
605
751
  );
606
752
  break;
607
753
  }