@capacitor/cli 3.2.2 → 3.3.0

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,60 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [3.3.0](https://github.com/ionic-team/capacitor/compare/3.2.5...3.3.0) (2021-11-03)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **cli:** Add Batch plugin to static list ([#5138](https://github.com/ionic-team/capacitor/issues/5138)) ([9470633](https://github.com/ionic-team/capacitor/commit/94706338c096b30390fa288c9b107e253923a644))
12
+ * **cli:** Add onesignal-cordova-plugin to static pod list ([#5143](https://github.com/ionic-team/capacitor/issues/5143)) ([937e240](https://github.com/ionic-team/capacitor/commit/937e2408f9bb60691e653b70d8b7cb02f540b251))
13
+ * **cli:** detect and register multiple plugins from same package ([#5098](https://github.com/ionic-team/capacitor/issues/5098)) ([25e770c](https://github.com/ionic-team/capacitor/commit/25e770c3f598bf3a1e05e21d607ab3ad70268674))
14
+
15
+
16
+ ### Features
17
+
18
+ * **cli:** add support for 'pod install' in VM based environments ([#5144](https://github.com/ionic-team/capacitor/issues/5144)) ([32ecf22](https://github.com/ionic-team/capacitor/commit/32ecf22de0a550756dbfa68b3b17c2333c89a430))
19
+ * **cli:** Allow to configure access origin tags on cordova config.xml ([#5134](https://github.com/ionic-team/capacitor/issues/5134)) ([0841a09](https://github.com/ionic-team/capacitor/commit/0841a093bf73ed4acac9a90be44a8e8a3aedbcdb))
20
+ * **cli:** Allow users to include Cordova plugins to the static list ([#5175](https://github.com/ionic-team/capacitor/issues/5175)) ([664149a](https://github.com/ionic-team/capacitor/commit/664149aadbe80e66dd757315a826ec1ab305edb9))
21
+
22
+
23
+
24
+
25
+
26
+ ## [3.2.5](https://github.com/ionic-team/capacitor/compare/3.2.4...3.2.5) (2021-10-13)
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * **cli:** create only static pod if needed ([#5099](https://github.com/ionic-team/capacitor/issues/5099)) ([8304744](https://github.com/ionic-team/capacitor/commit/83047445562a52cc927c7c77d55b48288cfc1fcc))
32
+
33
+
34
+
35
+
36
+
37
+ ## [3.2.4](https://github.com/ionic-team/capacitor/compare/3.2.3...3.2.4) (2021-09-27)
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * **cli:** await sync on add to avoid telemetry hang ([833bc20](https://github.com/ionic-team/capacitor/commit/833bc20525a2558e03cd0e56c6765ce6828cdfac))
43
+
44
+
45
+
46
+
47
+
48
+ ## [3.2.3](https://github.com/ionic-team/capacitor/compare/3.2.2...3.2.3) (2021-09-15)
49
+
50
+
51
+ ### Bug Fixes
52
+
53
+ * Add SalesforceMobileSDK-CordovaPlugin to iOS incompatible list ([#5031](https://github.com/ionic-team/capacitor/issues/5031)) ([6f3f79f](https://github.com/ionic-team/capacitor/commit/6f3f79f412b77b0c90988226ec5ade5d0198c706))
54
+ * Define cordovaConfig gradle variable ([#5024](https://github.com/ionic-team/capacitor/issues/5024)) ([55c217e](https://github.com/ionic-team/capacitor/commit/55c217e6898d0270c23c3a7158a5102e9b84ff40))
55
+
56
+
57
+
58
+
59
+
6
60
  ## [3.2.2](https://github.com/ionic-team/capacitor/compare/3.2.1...3.2.2) (2021-09-02)
7
61
 
8
62
  **Note:** Version bump only for package @capacitor/cli
Binary file
Binary file
@@ -69,10 +69,12 @@ async function findAndroidPluginClassesInPlugin(plugin) {
69
69
  debug('Searching %O source files in %O by %O regex', srcFiles.length, srcPath, classRegex);
70
70
  const entries = await Promise.all(srcFiles.map(async (srcFile) => {
71
71
  const srcFileContents = await utils_fs_1.readFile(srcFile, { encoding: 'utf-8' });
72
+ classRegex.lastIndex = 0;
72
73
  const classMatch = classRegex.exec(srcFileContents);
73
74
  if (classMatch) {
74
75
  const className = classMatch[1];
75
76
  debug('Searching %O for package by %O regex', srcFile, packageRegex);
77
+ packageRegex.lastIndex = 0;
76
78
  const packageMatch = packageRegex.exec(srcFileContents.substring(0, classMatch.index));
77
79
  if (!packageMatch) {
78
80
  errors_1.fatal(`Package could not be parsed from Android plugin.\n` +
@@ -201,6 +203,7 @@ ext {
201
203
  cdvMinSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : ${config.android.minVersion}
202
204
  // Plugin gradle extensions can append to this to have code run at the end.
203
205
  cdvPluginPostBuildExtras = []
206
+ cordovaConfig = [:]
204
207
  }`;
205
208
  await utils_fs_1.writeFile(path_1.join(config.android.cordovaPluginsDirAbs, 'cordova.variables.gradle'), cordovaVariables);
206
209
  }
package/dist/cordova.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeCordovaAndroidManifest = exports.getCordovaPreferences = exports.getIncompatibleCordovaPlugins = exports.checkPluginDependencies = exports.logCordovaManualSteps = exports.getCordovaPlugins = exports.handleCordovaPluginsJS = exports.autoGenerateConfig = exports.removePluginFiles = exports.createEmptyCordovaJS = exports.copyCordovaJS = exports.copyPluginsJS = exports.generateCordovaPluginsJSFile = void 0;
3
+ exports.writeCordovaAndroidManifest = exports.getCordovaPreferences = exports.needsStaticPod = exports.getIncompatibleCordovaPlugins = exports.checkPluginDependencies = exports.logCordovaManualSteps = exports.getCordovaPlugins = exports.handleCordovaPluginsJS = exports.autoGenerateConfig = exports.removePluginFiles = exports.createEmptyCordovaJS = exports.copyCordovaJS = exports.copyPluginsJS = exports.generateCordovaPluginsJSFile = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const utils_fs_1 = require("@ionic/utils-fs");
6
6
  const path_1 = require("path");
@@ -152,7 +152,7 @@ async function removePluginFiles(config, platform) {
152
152
  }
153
153
  exports.removePluginFiles = removePluginFiles;
154
154
  async function autoGenerateConfig(config, cordovaPlugins, platform) {
155
- var _a, _b;
155
+ var _a, _b, _c, _d;
156
156
  let xmlDir = path_1.join(config.android.resDirAbs, 'xml');
157
157
  const fileName = 'config.xml';
158
158
  if (platform === 'ios') {
@@ -180,12 +180,22 @@ async function autoGenerateConfig(config, cordovaPlugins, platform) {
180
180
  }
181
181
  }
182
182
  });
183
+ let accessOriginString = [];
184
+ if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.accessOrigins) {
185
+ accessOriginString = await Promise.all(config.app.extConfig.cordova.accessOrigins.map(async (host) => {
186
+ return `
187
+ <access origin="${host}" />`;
188
+ }));
189
+ }
190
+ else {
191
+ accessOriginString.push(`<access origin="*" />`);
192
+ }
183
193
  const pluginEntriesString = await Promise.all(pluginEntries.map(async (item) => {
184
194
  const xmlString = await xml_1.writeXML(item);
185
195
  return xmlString;
186
196
  }));
187
197
  let pluginPreferencesString = [];
188
- if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.preferences) {
198
+ if ((_d = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova) === null || _d === void 0 ? void 0 : _d.preferences) {
189
199
  pluginPreferencesString = await Promise.all(Object.entries(config.app.extConfig.cordova.preferences).map(async ([key, value]) => {
190
200
  return `
191
201
  <preference name="${key}" value="${value}" />`;
@@ -193,7 +203,7 @@ async function autoGenerateConfig(config, cordovaPlugins, platform) {
193
203
  }
194
204
  const content = `<?xml version='1.0' encoding='utf-8'?>
195
205
  <widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
196
- <access origin="*" />
206
+ ${accessOriginString.join('')}
197
207
  ${pluginEntriesString.join('')}
198
208
  ${pluginPreferencesString.join('')}
199
209
  </widget>`;
@@ -368,7 +378,7 @@ function getIncompatibleCordovaPlugins(platform) {
368
378
  'cordova-support-google-services',
369
379
  ];
370
380
  if (platform === 'ios') {
371
- pluginList.push('cordova-plugin-statusbar', '@ionic-enterprise/statusbar');
381
+ pluginList.push('cordova-plugin-statusbar', '@ionic-enterprise/statusbar', 'SalesforceMobileSDK-CordovaPlugin');
372
382
  }
373
383
  if (platform === 'android') {
374
384
  pluginList.push('cordova-plugin-compat');
@@ -376,6 +386,21 @@ function getIncompatibleCordovaPlugins(platform) {
376
386
  return pluginList;
377
387
  }
378
388
  exports.getIncompatibleCordovaPlugins = getIncompatibleCordovaPlugins;
389
+ function needsStaticPod(plugin, config) {
390
+ var _a, _b, _c, _d;
391
+ let pluginList = [
392
+ 'phonegap-plugin-push',
393
+ '@havesource/cordova-plugin-push',
394
+ 'cordova-plugin-firebasex',
395
+ '@batch.com/cordova-plugin',
396
+ 'onesignal-cordova-plugin',
397
+ ];
398
+ if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.staticPlugins) {
399
+ pluginList = pluginList.concat((_d = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova) === null || _d === void 0 ? void 0 : _d.staticPlugins);
400
+ }
401
+ return pluginList.includes(plugin.id);
402
+ }
403
+ exports.needsStaticPod = needsStaticPod;
379
404
  async function getCordovaPreferences(config) {
380
405
  var _a, _b, _c, _d, _e;
381
406
  const configXml = path_1.join(config.app.rootDir, 'config.xml');
@@ -387,6 +387,15 @@ export interface CapacitorConfig {
387
387
  allowNavigation?: string[];
388
388
  };
389
389
  cordova?: {
390
+ /**
391
+ * Populates <access> tags in the config.xml with the origin set to
392
+ * the values entered here.
393
+ * If not provided, a single <access origin="*" /> tag gets included.
394
+ * It only has effect on a few Cordova plugins that respect the whitelist.
395
+ *
396
+ * @since 3.3.0
397
+ */
398
+ accessOrigins?: string[];
390
399
  /**
391
400
  * Configure Cordova preferences.
392
401
  *
@@ -395,6 +404,13 @@ export interface CapacitorConfig {
395
404
  preferences?: {
396
405
  [key: string]: string | undefined;
397
406
  };
407
+ /**
408
+ * List of Cordova plugins that need to be static but are not
409
+ * already in the static plugin list.
410
+ *
411
+ * @since 3.3.0
412
+ */
413
+ staticPlugins?: string[];
398
414
  };
399
415
  /**
400
416
  * Configure plugins.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shouldPodInstall = exports.editProjectSettingsIOS = exports.resolvePlugin = exports.getIOSPlugins = exports.checkCocoaPods = exports.checkIOSPackage = void 0;
3
+ exports.editProjectSettingsIOS = exports.resolvePlugin = exports.getIOSPlugins = exports.checkCocoaPods = exports.checkIOSPackage = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const utils_fs_1 = require("@ionic/utils-fs");
6
6
  const path_1 = require("path");
@@ -69,11 +69,3 @@ async function editProjectSettingsIOS(config) {
69
69
  await utils_fs_1.writeFile(pbxPath, pbxContent, { encoding: 'utf-8' });
70
70
  }
71
71
  exports.editProjectSettingsIOS = editProjectSettingsIOS;
72
- function shouldPodInstall(config, platformName) {
73
- // Don't run pod install or xcodebuild if not on macOS
74
- if (config.cli.os !== "mac" /* Mac */ && platformName === 'ios') {
75
- return false;
76
- }
77
- return true;
78
- }
79
- exports.shouldPodInstall = shouldPodInstall;
@@ -40,14 +40,9 @@ async function updateIOS(config, deployment) {
40
40
  }
41
41
  exports.updateIOS = updateIOS;
42
42
  async function installCocoaPodsPlugins(config, plugins, deployment) {
43
- if (common_2.shouldPodInstall(config, platform)) {
44
- await common_1.runTask(`Updating iOS native dependencies with ${colors_1.default.input(`${config.ios.podPath} install`)}`, () => {
45
- return updatePodfile(config, plugins, deployment);
46
- });
47
- }
48
- else {
49
- log_1.logger.warn('Skipping pod install on unsupported OS');
50
- }
43
+ await common_1.runTask(`Updating iOS native dependencies with ${colors_1.default.input(`${config.ios.podPath} install`)}`, () => {
44
+ return updatePodfile(config, plugins, deployment);
45
+ });
51
46
  }
52
47
  exports.installCocoaPodsPlugins = installCocoaPodsPlugins;
53
48
  async function updatePodfile(config, plugins, deployment) {
@@ -57,13 +52,25 @@ async function updatePodfile(config, plugins, deployment) {
57
52
  let podfileContent = await utils_fs_1.readFile(podfilePath, { encoding: 'utf-8' });
58
53
  podfileContent = podfileContent.replace(/(def capacitor_pods)[\s\S]+?(\nend)/, `$1${dependenciesContent}$2`);
59
54
  await utils_fs_1.writeFile(podfilePath, podfileContent, { encoding: 'utf-8' });
60
- if (!deployment) {
61
- await utils_fs_1.remove(podfileLockPath);
55
+ const podCommandExists = await subprocess_1.isInstalled('pod');
56
+ if (podCommandExists) {
57
+ if (!deployment) {
58
+ await utils_fs_1.remove(podfileLockPath);
59
+ }
60
+ await subprocess_1.runCommand(config.ios.podPath, ['install', ...(deployment ? ['--deployment'] : [])], { cwd: config.ios.nativeProjectDirAbs });
61
+ }
62
+ else {
63
+ log_1.logger.warn('Skipping pod install because CocoaPods is not installed');
64
+ }
65
+ const isXcodebuildAvailable = await subprocess_1.isInstalled('xcodebuild');
66
+ if (isXcodebuildAvailable) {
67
+ await subprocess_1.runCommand('xcodebuild', ['-project', path_1.basename(`${config.ios.nativeXcodeProjDirAbs}`), 'clean'], {
68
+ cwd: config.ios.nativeProjectDirAbs,
69
+ });
70
+ }
71
+ else {
72
+ log_1.logger.warn('Unable to find "xcodebuild". Skipping xcodebuild clean step...');
62
73
  }
63
- await subprocess_1.runCommand(config.ios.podPath, ['install', ...(deployment ? ['--deployment'] : [])], { cwd: config.ios.nativeProjectDirAbs });
64
- await subprocess_1.runCommand('xcodebuild', ['-project', path_1.basename(`${config.ios.nativeXcodeProjDirAbs}`), 'clean'], {
65
- cwd: config.ios.nativeProjectDirAbs,
66
- });
67
74
  }
68
75
  async function generatePodFile(config, plugins) {
69
76
  const capacitoriOSPath = node_1.resolveNode(config.app.rootDir, '@capacitor/ios', 'package.json');
@@ -103,19 +110,14 @@ async function generatePodFile(config, plugins) {
103
110
  });
104
111
  });
105
112
  });
106
- const noPodPlugins = cordovaPlugins.filter(filterNoPods);
107
- if (noPodPlugins.length > 0) {
113
+ const staticPlugins = cordovaPlugins.filter(p => cordova_1.needsStaticPod(p, config));
114
+ const noStaticPlugins = cordovaPlugins.filter(el => !staticPlugins.includes(el));
115
+ if (noStaticPlugins.length > 0) {
108
116
  pods.push(` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`);
109
117
  }
110
- const podPlugins = cordovaPlugins.filter(el => !noPodPlugins.includes(el));
111
- const podSwiftPlugins = podPlugins.filter(filterSwift);
112
- const podObjCPlugins = podPlugins.filter(el => !podSwiftPlugins.includes(el));
113
- if (podObjCPlugins.length > 0) {
118
+ if (staticPlugins.length > 0) {
114
119
  pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`);
115
120
  }
116
- if (podSwiftPlugins.length > 0) {
117
- pods.push(` pod 'CordovaPluginsStaticSwift', :path => '../capacitor-cordova-ios-plugins'\n`);
118
- }
119
121
  const resourcesPlugins = cordovaPlugins.filter(filterResources);
120
122
  if (resourcesPlugins.length > 0) {
121
123
  pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`);
@@ -140,15 +142,12 @@ function isFramework(framework) {
140
142
  return framework.$.src.split('.').pop().includes('framework');
141
143
  }
142
144
  async function generateCordovaPodspecs(cordovaPlugins, config) {
143
- const noPodPlugins = cordovaPlugins.filter(filterNoPods);
144
- const podPlugins = cordovaPlugins.filter(el => !noPodPlugins.includes(el));
145
- const podSwiftPlugins = podPlugins.filter(filterSwift);
146
- const podObjCPlugins = podPlugins.filter(el => !podSwiftPlugins.includes(el));
147
- generateCordovaPodspec(noPodPlugins, config, false, false);
148
- generateCordovaPodspec(podObjCPlugins, config, true, false);
149
- generateCordovaPodspec(podSwiftPlugins, config, true, true);
145
+ const staticPlugins = cordovaPlugins.filter(p => cordova_1.needsStaticPod(p, config));
146
+ const noStaticPlugins = cordovaPlugins.filter(el => !staticPlugins.includes(el));
147
+ generateCordovaPodspec(noStaticPlugins, config, false);
148
+ generateCordovaPodspec(staticPlugins, config, true);
150
149
  }
151
- async function generateCordovaPodspec(cordovaPlugins, config, isStatic, isSwift) {
150
+ async function generateCordovaPodspec(cordovaPlugins, config, isStatic) {
152
151
  const weakFrameworks = [];
153
152
  const linkedFrameworks = [];
154
153
  const customFrameworks = [];
@@ -164,10 +163,6 @@ async function generateCordovaPodspec(cordovaPlugins, config, isStatic, isSwift)
164
163
  frameworkDeps.push('s.static_framework = true');
165
164
  sourcesFolderName += 'static';
166
165
  }
167
- if (isSwift) {
168
- name += 'Swift';
169
- sourcesFolderName += 'swift';
170
- }
171
166
  cordovaPlugins.map((plugin) => {
172
167
  const frameworks = plugin_1.getPlatformElement(plugin, platform, 'framework');
173
168
  frameworks.map((framework) => {
@@ -302,14 +297,9 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
302
297
  const headerFiles = plugin_1.getPlatformElement(p, platform, 'header-file');
303
298
  const codeFiles = sourceFiles.concat(headerFiles);
304
299
  const frameworks = plugin_1.getPlatformElement(p, platform, 'framework');
305
- const podFrameworks = frameworks.filter((framework) => framework.$.type && framework.$.type === 'podspec');
306
- const podspecs = plugin_1.getPlatformElement(p, platform, 'podspec');
307
300
  let sourcesFolderName = 'sources';
308
- if (podFrameworks.length > 0 || podspecs.length > 0) {
301
+ if (cordova_1.needsStaticPod(p, config)) {
309
302
  sourcesFolderName += 'static';
310
- if (filterSwift(p)) {
311
- sourcesFolderName += 'swift';
312
- }
313
303
  }
314
304
  const sourcesFolder = path_1.join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name);
315
305
  for (const codeFile of codeFiles) {
@@ -368,17 +358,6 @@ async function removePluginsNativeFiles(config) {
368
358
  await utils_fs_1.remove(config.ios.cordovaPluginsDirAbs);
369
359
  await template_1.extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs);
370
360
  }
371
- function filterNoPods(plugin) {
372
- const frameworks = plugin_1.getPlatformElement(plugin, platform, 'framework');
373
- const podFrameworks = frameworks.filter((framework) => framework.$.type && framework.$.type === 'podspec');
374
- const podspecs = plugin_1.getPlatformElement(plugin, platform, 'podspec');
375
- return podFrameworks.length === 0 && podspecs.length === 0;
376
- }
377
- function filterSwift(plugin) {
378
- const sourceFiles = plugin_1.getPlatformElement(plugin, platform, 'source-file');
379
- const swiftFiles = sourceFiles.filter((sf) => sf.$.src && sf.$.src.split('.').pop() === 'swift');
380
- return swiftFiles.length > 0;
381
- }
382
361
  function filterResources(plugin) {
383
362
  const resources = plugin_1.getPlatformElement(plugin, platform, 'resource-file');
384
363
  return resources.length > 0;
package/dist/tasks/add.js CHANGED
@@ -55,7 +55,7 @@ async function addCommand(config, selectedPlatformName) {
55
55
  await doAdd(config, platformName);
56
56
  await editPlatforms(config, platformName);
57
57
  if (await utils_fs_1.pathExists(config.app.webDirAbs)) {
58
- sync_1.sync(config, platformName, false);
58
+ await sync_1.sync(config, platformName, false);
59
59
  }
60
60
  else {
61
61
  log_1.logger.warn(`${colors_1.default.success(colors_1.default.strong('sync'))} could not run--missing ${colors_1.default.strong(config.app.webDir)} directory.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/cli",
3
- "version": "3.2.2",
3
+ "version": "3.3.0",
4
4
  "description": "Capacitor: Cross-platform apps with JavaScript and the web",
5
5
  "homepage": "https://capacitorjs.com",
6
6
  "author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
@@ -52,12 +52,12 @@
52
52
  "debug": "^4.2.0",
53
53
  "env-paths": "^2.2.0",
54
54
  "kleur": "^4.1.1",
55
- "native-run": "^1.4.0",
56
- "open": "^7.1.0",
55
+ "native-run": "^1.5.0",
56
+ "open": "^7.4.2",
57
57
  "plist": "^3.0.2",
58
58
  "prompts": "^2.3.2",
59
59
  "semver": "^7.3.2",
60
- "tar": "^6.0.5",
60
+ "tar": "^6.1.11",
61
61
  "tslib": "^2.1.0",
62
62
  "xml2js": "^0.4.23"
63
63
  },
@@ -85,5 +85,5 @@
85
85
  "publishConfig": {
86
86
  "access": "public"
87
87
  },
88
- "gitHead": "a02716096b1492b4337f41369ebe951cae331694"
88
+ "gitHead": "cb7eacd3c69512cf85c03cb06ef9ebf44d32e425"
89
89
  }