You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by GitBox <gi...@apache.org> on 2018/08/26 02:24:14 UTC

[GitHub] dpogue closed pull request #395: Implement swift support and testing

dpogue closed pull request #395: Implement swift support and testing
URL: https://github.com/apache/cordova-ios/pull/395
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/bin/templates/scripts/cordova/Api.js b/bin/templates/scripts/cordova/Api.js
index bc5f10a3e..9a1da6aff 100644
--- a/bin/templates/scripts/cordova/Api.js
+++ b/bin/templates/scripts/cordova/Api.js
@@ -233,6 +233,26 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
 
     return PluginManager.get(self.platform, self.locations, xcodeproj)
         .addPlugin(plugin, installOptions)
+        .then(function () {
+            if (plugin != null) {
+                var headerTags = plugin.getHeaderFiles(self.platform);
+                var bridgingHeaders = headerTags.filter(function (obj) {
+                    return (obj.type === 'BridgingHeader');
+                });
+                if (bridgingHeaders.length > 0) {
+                    var project_dir = self.locations.root;
+                    var project_name = self.locations.xcodeCordovaProj.split('/').pop();
+                    var BridgingHeader = require('./lib/BridgingHeader').BridgingHeader;
+                    var bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h'));
+                    events.emit('verbose', 'Adding Bridging-Headers since the plugin contained <header-file> with type="BridgingHeader"');
+                    bridgingHeaders.forEach(function (obj) {
+                        var bridgingHeaderPath = path.basename(obj.src);
+                        bridgingHeaderFile.addHeader(plugin.id, bridgingHeaderPath);
+                    });
+                    bridgingHeaderFile.write();
+                }
+            }
+        })
         .then(function () {
             var frameworkTags = plugin.getFrameworks(self.platform);
             var frameworkPods = frameworkTags.filter(function (obj) {
@@ -318,6 +338,26 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
 
     return PluginManager.get(self.platform, self.locations, xcodeproj)
         .removePlugin(plugin, uninstallOptions)
+        .then(function () {
+            if (plugin != null) {
+                var headerTags = plugin.getHeaderFiles(self.platform);
+                var bridgingHeaders = headerTags.filter(function (obj) {
+                    return (obj.type === 'BridgingHeader');
+                });
+                if (bridgingHeaders.length > 0) {
+                    var project_dir = self.locations.root;
+                    var project_name = self.locations.xcodeCordovaProj.split('/').pop();
+                    var BridgingHeader = require('./lib/BridgingHeader').BridgingHeader;
+                    var bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h'));
+                    events.emit('verbose', 'Removing Bridging-Headers since the plugin contained <header-file> with type="BridgingHeader"');
+                    bridgingHeaders.forEach(function (obj) {
+                        var bridgingHeaderPath = path.basename(obj.src);
+                        bridgingHeaderFile.removeHeader(plugin.id, bridgingHeaderPath);
+                    });
+                    bridgingHeaderFile.write();
+                }
+            }
+        })
         .then(function () {
             var frameworkTags = plugin.getFrameworks(self.platform);
             var frameworkPods = frameworkTags.filter(function (obj) {
diff --git a/bin/templates/scripts/cordova/lib/BridgingHeader.js b/bin/templates/scripts/cordova/lib/BridgingHeader.js
new file mode 100644
index 000000000..e20383515
--- /dev/null
+++ b/bin/templates/scripts/cordova/lib/BridgingHeader.js
@@ -0,0 +1,125 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+'use strict';
+
+var fs = require('fs');
+var CordovaError = require('cordova-common').CordovaError;
+
+function BridgingHeader (bridgingHeaderPath) {
+    this.path = bridgingHeaderPath;
+    this.bridgingHeaders = null;
+    if (!fs.existsSync(this.path)) {
+        throw new CordovaError('BridgingHeader.h is not found.');
+    }
+    this.bridgingHeaders = this.__parseForBridgingHeader(fs.readFileSync(this.path, 'utf8'));
+}
+
+BridgingHeader.prototype.addHeader = function (plugin_id, header_path) {
+    this.bridgingHeaders.push({type: 'code', code: '#import "' + header_path + '"\n'});
+};
+
+BridgingHeader.prototype.removeHeader = function (plugin_id, header_path) {
+    this.bridgingHeaders = this.bridgingHeaders.filter(function (line) {
+        if (this.found) {
+            return true;
+        }
+        if (line.type === 'code') {
+            var re = new RegExp('#import\\s+"' + preg_quote(header_path) + '"(\\s*|\\s.+)(\\n|$)');
+            if (re.test(line.code)) {
+                this.found = true;
+                return false;
+            }
+        }
+        return true;
+    }, {found: false});
+};
+
+BridgingHeader.prototype.write = function () {
+    var text = this.__stringifyForBridgingHeader(this.bridgingHeaders);
+    fs.writeFileSync(this.path, text, 'utf8');
+};
+
+BridgingHeader.prototype.__stringifyForBridgingHeader = function (bridgingHeaders) {
+    return bridgingHeaders.map(function (obj) {
+        return obj.code;
+    }).join('');
+};
+
+BridgingHeader.prototype.__parseForBridgingHeader = function (text) {
+    var i = 0;
+    var list = [];
+    var type = 'code';
+    var start = 0;
+    while (i < text.length) {
+        switch (type) {
+        case 'comment':
+            if (i + 1 < text.length && text[i] === '*' && text[i + 1] === '/') {
+                i += 2;
+                list.push({type: type, code: text.slice(start, i)});
+                type = 'code';
+                start = i;
+            } else {
+                i += 1;
+            }
+            break;
+        case 'line-comment':
+            if (i < text.length && text[i] === '\n') {
+                i += 1;
+                list.push({type: type, code: text.slice(start, i)});
+                type = 'code';
+                start = i;
+            } else {
+                i += 1;
+            }
+            break;
+        case 'code':
+        default:
+            if (i + 1 < text.length && text[i] === '/' && text[i + 1] === '*') { // comment
+                if (start < i) {
+                    list.push({type: type, code: text.slice(start, i)});
+                }
+                type = 'comment';
+                start = i;
+            } else if (i + 1 < text.length && text[i] === '/' && text[i + 1] === '/') { // line comment
+                if (start < i) {
+                    list.push({type: type, code: text.slice(start, i)});
+                }
+                type = 'line-comment';
+                start = i;
+            } else if (i < text.length && text[i] === '\n') {
+                i += 1;
+                list.push({type: type, code: text.slice(start, i)});
+                start = i;
+            } else {
+                i += 1;
+            }
+            break;
+        }
+    }
+    if (start < i) {
+        list.push({type: type, code: text.slice(start, i)});
+    }
+    return list;
+};
+
+function preg_quote (str, delimiter) {
+    return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
+}
+
+module.exports.BridgingHeader = BridgingHeader;
diff --git a/bin/templates/scripts/cordova/lib/prepare.js b/bin/templates/scripts/cordova/lib/prepare.js
index de345dd55..10f14cc45 100644
--- a/bin/templates/scripts/cordova/lib/prepare.js
+++ b/bin/templates/scripts/cordova/lib/prepare.js
@@ -277,10 +277,11 @@ function handleBuildSettings (platformConfig, locations, infoPlist) {
     var targetDevice = parseTargetDevicePreference(platformConfig.getPreference('target-device', 'ios'));
     var deploymentTarget = platformConfig.getPreference('deployment-target', 'ios');
     var needUpdatedBuildSettingsForLaunchStoryboard = checkIfBuildSettingsNeedUpdatedForLaunchStoryboard(platformConfig, infoPlist);
+    var swiftVersion = platformConfig.getPreference('SwiftVersion', 'ios');
 
     // no build settings provided and we don't need to update build settings for launch storyboards,
     // then we don't need to parse and update .pbxproj file
-    if (!targetDevice && !deploymentTarget && !needUpdatedBuildSettingsForLaunchStoryboard) {
+    if (!targetDevice && !deploymentTarget && !needUpdatedBuildSettingsForLaunchStoryboard && !swiftVersion) {
         return Q();
     }
 
@@ -302,6 +303,11 @@ function handleBuildSettings (platformConfig, locations, infoPlist) {
         proj.updateBuildProperty('IPHONEOS_DEPLOYMENT_TARGET', deploymentTarget);
     }
 
+    if (swiftVersion) {
+        events.emit('verbose', 'Set SwiftVersion to "' + swiftVersion + '".');
+        proj.updateBuildProperty('SWIFT_VERSION', swiftVersion);
+    }
+
     updateBuildSettingsForLaunchStoryboard(proj, platformConfig, infoPlist);
 
     fs.writeFileSync(locations.pbxproj, proj.writeSync(), 'utf-8');
diff --git a/tests/spec/unit/Api.spec.js b/tests/spec/unit/Api.spec.js
index eef7f7275..2e8db2da5 100644
--- a/tests/spec/unit/Api.spec.js
+++ b/tests/spec/unit/Api.spec.js
@@ -35,6 +35,7 @@ if (process.platform === 'darwin') {
 }
 
 var projectFile = require('../../../bin/templates/scripts/cordova/lib/projectFile');
+var BridgingHeader_mod = require('../../../bin/templates/scripts/cordova/lib/BridgingHeader.js');
 var Podfile_mod = require('../../../bin/templates/scripts/cordova/lib/Podfile');
 var PodsJson_mod = require('../../../bin/templates/scripts/cordova/lib/PodsJson');
 var Q = require('q');
@@ -158,12 +159,14 @@ describe('Platform Api', function () {
 
         describe('addPlugin', function () {
             var my_plugin = {
+                getHeaderFiles: function () { return []; },
                 getFrameworks: function () {}
             };
             beforeEach(function () {
                 spyOn(PluginManager, 'get').and.returnValue({
                     addPlugin: function () { return Q(); }
                 });
+                spyOn(BridgingHeader_mod, 'BridgingHeader');
                 spyOn(Podfile_mod, 'Podfile');
                 spyOn(PodsJson_mod, 'PodsJson');
             });
@@ -172,6 +175,32 @@ describe('Platform Api', function () {
                 api.addPlugin('my cool plugin', opts);
                 expect(opts.variables.PACKAGE_NAME).toEqual('ios.cordova.io');
             });
+            describe('with header-file of `BridgingHeader` type', function () {
+                var bridgingHeader_mock;
+                var my_bridgingHeader_json = {
+                    type: 'BridgingHeader',
+                    src: 'bridgingHeaderSource!'
+                };
+                beforeEach(function () {
+                    bridgingHeader_mock = jasmine.createSpyObj('bridgingHeader mock', ['addHeader', 'write']);
+                    spyOn(my_plugin, 'getFrameworks').and.returnValue([]);
+                    spyOn(my_plugin, 'getHeaderFiles').and.returnValue([my_bridgingHeader_json]);
+                    BridgingHeader_mod.BridgingHeader.and.callFake(function () {
+                        return bridgingHeader_mock;
+                    });
+                });
+                it('should add BridgingHeader', function (done) {
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(bridgingHeader_mock.addHeader).toHaveBeenCalledWith(my_plugin.id, 'bridgingHeaderSource!');
+                            expect(bridgingHeader_mock.write).toHaveBeenCalled();
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked');
+                            console.error(err);
+                        }).done(done);
+                });
+
+            });
             describe('with frameworks of `podspec` type', function () {
                 var podsjson_mock;
                 var podfile_mock;
diff --git a/tests/spec/unit/BridgingHeader.spec.js b/tests/spec/unit/BridgingHeader.spec.js
new file mode 100644
index 000000000..99760ac72
--- /dev/null
+++ b/tests/spec/unit/BridgingHeader.spec.js
@@ -0,0 +1,111 @@
+var fs = require('fs');
+var path = require('path');
+
+var BridgingHeader = require(path.resolve(path.join(__dirname, '..', '..', '..', 'bin', 'templates', 'scripts', 'cordova', 'lib', 'BridgingHeader.js'))).BridgingHeader;
+var fixtureBridgingHeader = fs.readFileSync(path.resolve(__dirname, 'fixtures', 'test-Bridging-Header.h'), 'utf-8');
+
+describe('unit tests for BridgingHeader module', function () {
+    var existsSyncSpy;
+    var readFileSyncSpy;
+    var writeFileSyncSpy;
+    var dummy_path = 'dummy_path';
+    var dummy_plugin = { id: 'dummy_plugin', header_path: 'dummy_header_path' };
+    var dummy_plugin2 = { id: 'dummy_plugin2', header_path: 'dummy_header_path2' };
+    var headerImportText = function (header_path) { return '#import "' + header_path + '"'; };
+
+    beforeEach(function () {
+        existsSyncSpy = spyOn(fs, 'existsSync');
+        readFileSyncSpy = spyOn(fs, 'readFileSync');
+        writeFileSyncSpy = spyOn(fs, 'writeFileSync');
+    });
+    it('Test#001 : should error if BridgingHeader file does not exist', function () {
+        existsSyncSpy.and.returnValue(false);
+        expect(function () {
+            var _ = new BridgingHeader(fixtureBridgingHeader);
+            expect(_).not.toEqual(null); // To avoid ESLINT error "Do not use 'new' for side effects"
+        }).toThrow();
+    });
+    it('Test#002 : load BridgingHeader file', function () {
+        existsSyncSpy.and.returnValue(true);
+        readFileSyncSpy.and.returnValue(fixtureBridgingHeader);
+
+        var bridgingHeader = new BridgingHeader(dummy_path);
+        expect(bridgingHeader.path).toEqual(dummy_path);
+        expect(bridgingHeader).not.toEqual(null);
+    });
+    it('Test#003 : add and remove a BridgingHeader', function () {
+        var result_json = null;
+        var text_list = null;
+        var bridgingHeaderFileContent = fixtureBridgingHeader;
+        existsSyncSpy.and.returnValue(true);
+        readFileSyncSpy.and.callFake(function (read_path, charset) {
+            return bridgingHeaderFileContent;
+        });
+        writeFileSyncSpy.and.callFake(function (write_path, text, charset) {
+            result_json = {write_path: write_path, text: text, charset: charset};
+        });
+
+        var bridgingHeader = new BridgingHeader(dummy_path);
+        bridgingHeader.addHeader(dummy_plugin.id, dummy_plugin.header_path);
+        bridgingHeader.write();
+        expect(result_json).not.toEqual(null);
+        expect(result_json.write_path).toEqual(dummy_path);
+        expect(result_json.text).not.toEqual(null);
+        expect(result_json.charset).toEqual('utf8');
+        text_list = result_json.text.split('\n');
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin.header_path); }).length).toEqual(1);
+
+        bridgingHeader = new BridgingHeader(dummy_path);
+        bridgingHeader.removeHeader(dummy_plugin.id, dummy_plugin.header_path);
+        bridgingHeader.write();
+        expect(result_json).not.toEqual(null);
+        expect(result_json.write_path).toEqual(dummy_path);
+        expect(result_json.text).not.toEqual(null);
+        expect(result_json.charset).toEqual('utf8');
+        text_list = result_json.text.split('\n');
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin.header_path); }).length).toEqual(0);
+    });
+    it('Test#004 : add and remove two BridgingHeaders', function () {
+        var result_json = null;
+        var text_list = null;
+        var bridgingHeaderFileContent = fixtureBridgingHeader;
+        existsSyncSpy.and.returnValue(true);
+        readFileSyncSpy.and.callFake(function (read_path, charset) {
+            return bridgingHeaderFileContent;
+        });
+        writeFileSyncSpy.and.callFake(function (write_path, text, charset) {
+            bridgingHeaderFileContent = text;
+            result_json = {write_path: write_path, text: text, charset: charset};
+        });
+
+        var bridgingHeader = new BridgingHeader(dummy_path);
+        bridgingHeader.addHeader(dummy_plugin.id, dummy_plugin.header_path);
+        bridgingHeader.write();
+
+        bridgingHeader = new BridgingHeader(dummy_path);
+        bridgingHeader.addHeader(dummy_plugin2.id, dummy_plugin2.header_path);
+        bridgingHeader.write();
+
+        text_list = result_json.text.split('\n');
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin.header_path); }).length).toEqual(1);
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin2.header_path); }).length).toEqual(1);
+
+        bridgingHeader = new BridgingHeader(dummy_path);
+        bridgingHeader.removeHeader(dummy_plugin.id, dummy_plugin.header_path);
+        bridgingHeader.write();
+
+        text_list = result_json.text.split('\n');
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin.header_path); }).length).toEqual(0);
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin2.header_path); }).length).toEqual(1);
+
+        bridgingHeader = new BridgingHeader(dummy_path);
+        bridgingHeader.removeHeader(dummy_plugin2.id, dummy_plugin2.header_path);
+        bridgingHeader.write();
+
+        text_list = result_json.text.split('\n');
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin.header_path); }).length).toEqual(0);
+        expect(text_list.filter(function (line) { return line === headerImportText(dummy_plugin2.header_path); }).length).toEqual(0);
+
+    });
+
+});
diff --git a/tests/spec/unit/fixtures/test-Bridging-Header.h b/tests/spec/unit/fixtures/test-Bridging-Header.h
new file mode 100644
index 000000000..9c9c87cd5
--- /dev/null
+++ b/tests/spec/unit/fixtures/test-Bridging-Header.h
@@ -0,0 +1,30 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+//
+//  Bridging-Header.h
+//  __PROJECT_NAME__
+//
+//  Created by ___FULLUSERNAME___ on ___DATE___.
+//  Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved.
+//
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import <Cordova/CDV.h>
+#import "CDVSwift22Object.h"
+#import "CDVSwift2Object.h"
diff --git a/tests/spec/unit/fixtures/test-config-3.xml b/tests/spec/unit/fixtures/test-config-3.xml
new file mode 100644
index 000000000..9bea9d6f3
--- /dev/null
+++ b/tests/spec/unit/fixtures/test-config-3.xml
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget android-packageName="io.cordova.hellocordova.android" id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>Hello Cordova</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+
+    <platform name="ios">
+        <preference name="orientation" value="all" />
+        <preference name="target-device" value="handset" />
+        <preference name="deployment-target" value="8.0" />
+        <preference name="SwiftVersion" value="4.1" />
+    </platform>
+
+    <access origin="http://*.apache.org" />
+    <access origin="https://*.apache.org" />
+
+    <allow-navigation href="http://*.apache.org" />
+    <allow-navigation href="https://*.apache.org" />
+     
+</widget>
diff --git a/tests/spec/unit/prepare.spec.js b/tests/spec/unit/prepare.spec.js
index 8c12305f1..4705d715f 100644
--- a/tests/spec/unit/prepare.spec.js
+++ b/tests/spec/unit/prepare.spec.js
@@ -44,6 +44,7 @@ var ConfigParser = require('cordova-common').ConfigParser;
 // Create a real config object before mocking out everything.
 var cfg = new ConfigParser(path.join(FIXTURES, 'test-config.xml'));
 var cfg2 = new ConfigParser(path.join(FIXTURES, 'test-config-2.xml'));
+var cfg3 = new ConfigParser(path.join(FIXTURES, 'test-config-3.xml'));
 
 function wrapper (p, done, post) {
     p.then(post, function (err) {
@@ -626,6 +627,41 @@ describe('prepare', function () {
                 cfg2.name = cfg2OriginalName;
             });
         });
+        it('should write SwiftVersion preference (4.1)', function (done) {
+            var cfg3OriginalName = cfg3.name;
+            cfg3.name = function () { return 'SampleApp'; }; // new config does *not* have a name change
+            writeFileSyncSpy.and.callThrough();
+            wrapper(updateProject(cfg3, p.locations), done, function () {
+                var xcode = require('xcode');
+                var proj = new xcode.project(p.locations.pbxproj); /* eslint new-cap : 0 */
+                proj.parseSync();
+                var prop = proj.getBuildProperty('SWIFT_VERSION');
+                expect(prop).toEqual('4.1');
+
+                // restore cfg2 original name
+                cfg3.name = cfg3OriginalName;
+            });
+        });
+        it('should write SwiftVersion preference (3.3)', function (done) {
+            var cfg3OriginalName = cfg3.name;
+            cfg3.name = function () { return 'SampleApp'; }; // new config does *not* have a name change
+            var pref = cfg3.doc.findall('platform[@name=\'ios\']/preference').filter(function (elem) {
+                return elem.attrib.name.toLowerCase() === 'swiftversion';
+            })[0];
+            var prefOriginalSwiftVersion = pref.attrib.value;
+            pref.attrib.value = '3.3';
+            writeFileSyncSpy.and.callThrough();
+            wrapper(updateProject(cfg3, p.locations), done, function () {
+                var xcode = require('xcode');
+                var proj = new xcode.project(p.locations.pbxproj); /* eslint new-cap : 0 */
+                proj.parseSync();
+                var prop = proj.getBuildProperty('SWIFT_VERSION');
+                expect(prop).toEqual('3.3');
+                // restore cfg2 original name
+                cfg3.name = cfg3OriginalName;
+                pref.attrib.value = prefOriginalSwiftVersion;
+            });
+        });
 
         it('Test#002 : should write out the app id to info plist as CFBundleIdentifier', function (done) {
             var orig = cfg.getAttribute;


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org