You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@openwhisk.apache.org by GitBox <gi...@apache.org> on 2018/12/19 03:07:01 UTC

[GitHub] jonpspri closed pull request #28: Create a multi-architecture Docker image for the runtime

jonpspri closed pull request #28: Create a multi-architecture Docker image for the runtime
URL: https://github.com/apache/incubator-openwhisk-runtime-nodejs/pull/28
 
 
   

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/.gitignore b/.gitignore
index 52f108d..0ddf2b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,11 +63,8 @@ ansible/roles/nginx/files/*cert.pem
 
 # .zip files must be explicited whitelisted
 *.zip
-!tests/dat/actions/blackbox.zip
-!tests/dat/actions/helloSwift.zip
-!tests/dat/actions/python.zip
-!tests/dat/actions/python2_virtualenv.zip
-!tests/dat/actions/python3_virtualenv.zip
-!tests/dat/actions/python_virtualenv_dir.zip
-!tests/dat/actions/python_virtualenv_name.zip
-!tests/dat/actions/zippedaction.zip
+
+# Some places where secrets are kept...
+docker-local.json
+*.pem
+/tls
diff --git a/.travis.yml b/.travis.yml
index 67dbb53..430bc94 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,10 +14,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+---
+os:
+  - linux
+  - linux-ppc64le
 
-sudo: required
-group: deprecated-2017Q3
-language: scala
+#sudo: required
+language: java
 services:
   - docker
 
@@ -25,26 +28,80 @@ notifications:
   email: false
   webhooks:
     urls:
-      # travis2slack webhook to enable DMs on openwhisk-team.slack.com to PR authors with TravisCI results
+      # travis2slack webhook to enable DMs on openwhisk-team.slack.com to PR
+      # authors with TravisCI results
       secure: "uCb2cZQRl1aJzdzQvI5acERfxqnQMQHwQn1eq/Vm6aD9yHLZBdYJ+g6Uj6a6QtVo/AodNsyQvj3MtcOEIim5ZyC8UTVISEAo7QmtwPdjLuH7mC57jFY48RPTJ7GUdpxUkwF2hVK4VtM3mF1nWeKUaLOdgu80EV4yLqLfTsLPsH45GLxnZ4+lF95ofYRTUDg3gZ34PoHxzMZkOStRHRmN3ceRmlUkfWanWqBx2MsU80D7hf7JIRjOdXRw3hyrjyRiF5c0mAprh9R7J4i9BdM5dyI0UfDXZ13afS9w9wRpvO4LQZ2F3NyhbK+Dn0V9kXEuY+gQJnDJOrz+pyKjQxpIgQ/I9jP6OizUt+4lWJ9c9IxPVSAoMllQuww5LFHcL6Ch10aVn1iqo/VeLyvZUZoqrvNRNwXR4RyAaQ3NrNIo1HVK56LIsBtq3i6ePFwJJHcI9cL5opqTUec4VFFTivNC5Jo+pSOYkYEzk+eXacIMYUPd0T0AlzR4ZbH1BDcZuDjZpb+jbJdHzTPYlYCtyGUyFMm7hqrDsO4VTLYbl3f6ZutgODM1/IycDJ99Y3620n8dFXLjiIZCkvmJIKjMrmWoPWdSfKFcxL/jfdcvyue3j+tOPnCWwJFqoJPH+4N+8B+QG1SQGURrZ0sqFZcTrbDA4jzv1fHZJMe9Omh75wkHf7k="
 
 before_install:
+#  Use GPG encryption because it reduces the number of secrets that must be
+#  managed across repositories.  When forking a repository as a core
+#  contributor, contact maintainer to have a newly-encrypted GPG passphrase
+#  communicated.
+#
+#  Note: ${TRAVIS_REPO_SLUG%/*} is repository owner.
+#- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
+#    for i in ./tools/travis/tls/${TRAVIS_REPO_SLUG%/*}/*/*.pem.gpg; do
+#      echo "${multiarch_encryption_pwd}" | gpg --passphrase-fd 0 "$i";
+#    done && cp -R ./tools/travis/tls/${TRAVIS_REPO_SLUG%/*} ./tls;
+#  fi
   - "./tools/travis/setup.sh"
+
 install: true
 script:
-  - "./tools/travis/build.sh && ./tools/travis/deploy.sh && ./tools/travis/test.sh"
+  - "./tools/travis/build.sh && ./tools/travis/test.sh"
+
 deploy:
   - provider: script
-    script: "./tools/travis/publish.sh openwhisk ${TRAVIS_TAG%@*} ${TRAVIS_TAG##*@}"
+    #script: "./tools/travis/publish.sh openwhisk ${TRAVIS_TAG%@*} ${TRAVIS_TAG##*@}"
+    script: "/bin/true"
+    skip_cleanup: true
     on:
       tags: true
       all_branches: true
   - provider: script
-    script: "./tools/travis/publish.sh openwhisk 6 master && ./tools/travis/publish.sh openwhisk 8 master"
+    #script: "./tools/travis/publish.sh openwhisk 6 master && ./tools/travis/publish.sh openwhisk 8 master"
+    script: "/bin/true"
     on:
       branch: master
 
 env:
+  matrix:
+    - NODEJS_VERSION=6
+    - NODEJS_VERSION=8
   global:
-  - secure: Y1ldwIQ6bc3/3Pc0E+qQ6K2M830B9BObYDlsNilPwF/kak3YSfF7SuXuRbJGjTdhH2KOotZD2CwONgP2yvOSPBToC/HpnXYfAGtgblrxQORvgdik88CFWa3Lli1pwlpdzKQNWhBvglzq+IIS98wqzmwqGr8zKA+Iau2ByHdb1j3M9rrIY9V6oU9Gwim1apcRyfI/as3+QfPtt8BUAl2U7+PprxwJigyF/mcZnBJbd7IjrilE2gldZLxKlBiffoKVBinrEg3IQGJPt6k8riw264pBQEpcA0ZBsPUvMaISSxLb+d1ymp3WsiTJUjv+URR/HcdDa7P9jY+ouc8PQz4Yt+Ii38lM2tQU480APfVTyfj6drkjL/+54mYuxm8TzkBWcM2j6/FYT+8HvK/pF35wDJ3El+jGq7BARXg8HVxFsZgynJnhqhWDQb3xX9fK+N4K8+ct+HlsOSa5mP5i5Yo6WRTrWrFpyxVnv9crKePCiYqA2Y8ta8Wnh0sM06nLRtDbfbDjvXPQbaZqSnL4B2Cto08YoT/W/lu8QgJ3EEFlUdDOke4kv9yoXtuE0h7+8dwOvBNMVrBis3G2EYObgR4WmWjk4loYhqT9h3jrH0/5bGLzSKc++qYW0rOZ/RB21cRSe1ILQvSzWkImUoPI8b0i5baGSDq3EjTXYr3pIXSYpQk=
-  - secure: DOg+FgllLbyv7nEK3JJZfO/cvXy5K0L6QI/S9EJ/ivm4XBDCw1ayhrSQXvp1tMTPbWBEIv2gomPsHghJ+hVvX3dgwYdoNz9WZaNBB6lOO9U8OQW0LBsO5Eai6grzqOP35OuKtthuyR3dGJHAZo/XjhZM/jL0z6q1kNDzdS7ASwRwHJG0rHPGVlGeolH4nAity4KNJvyAspS1FYaIj9FEC/M7UT6nJVACbr9iMt/83teF/Oo2uoFI6Pa4K3nE2NViVFibToNOM3CV8kArDPDoNJviXxQ07ZM6fNijwehZ80waiPSaxFY8PLSntQNxGyB3DbomSTCcdVvtuHHQVmZgpVdvOJE8wk3R09+nq9U9FuUWLiRYSRbF1eF48YFnssPW1jEeVSenFRADcQ37e4B21ssLvXRHpQHpPVrYBZ8ffDamS7pKtEqocX/3Syc6irxHGCpxEdhvQ0Of5AWHhzB714VCijJJQiH9J4hEKXhMeAZ5SSt4eUavEHJKMhUcJ/aaku5w1+KtiYeOso1fTRbTYkEYb1A0bSNdXlGrYRRT+N683W7+ENiQIT+hTp617L/m5WQLIHfKe3gy5qt46kHFiUL3R3YlBI5OKjnLkDnUiA+0d4SFtJC/TBuPmH1fG4isAuvggTjzCb3kUG3ysin8AW0AKaXW0eqjKa/nsHWDKmc=
+  #  --- begin: DOCKER_REGISTRY (defaults to docker.io)
+  #      + for jonpspri/incubator-openwhisk-runtime-nodejs
+    - secure: "Dfy4x6wjDENBqgX2snUda5wESoGYi80EfwhXUhhrwUcYunO58W6XpAmDSnh572pABQH5dRXn6ohhKJnmTbxbBd546qW4//IgJ15WAZ75wR8nqm15VZ7TIqcEs8wmjvILpxQhVnsj3Xfy5O3sCH25oIuBjpHucQOzRkfEaYjHdMaV9JAFc8sb6EC+lCS9mt9uqmFfRNWk+qXuV9XrAMqFhc34vvB660UHlp3rFD+yPHui2hYxQJUL8a2i4AVbCvQDcHUykLsCmi2Sng8zEBPZGJe6HGg2mR9vbrpuT2WJ6kdHfzjHanFJIlOX5BYxztvpOI8bQnPtynlu1ziQGmrpRbt6IEoiBtyOKzCFJjsUaEOiCI8cuPoPddusTwZy/f9ucl9t9Vr1CmQ8HATdFfIQI6O1hQR+907n6oq5TreYYTMNTNHp9RFfViPZz50J7gDnAzs9eDJiGyQcvASaHUw+QzcpaQtYvC6KNdL6vOreWqj3gFoNBfmFwZ0FqW5dS78/Oqmu7BVoiRQA20IGp6+FJCTAfxDAQ5nq1pSY2CNiBdH9cr+uehusLCyMcVKcj7G83J51hfs398QeScHmSwrMJ7G7socLcs9muIIy/7lzL8HxoI00J7HwrIisi0lJFqHmCIbIQoQNdj23kQmIUO4rxJ6Vifhdoi7fdXw++xtexTY="
+    #  ---
+
+  #  --- begin: DOCKER_USER and DOCKER_PASSWORD
+  #      + for apache/incubator-openwhisk-runtime-nodejs
+    - secure: Y1ldwIQ6bc3/3Pc0E+qQ6K2M830B9BObYDlsNilPwF/kak3YSfF7SuXuRbJGjTdhH2KOotZD2CwONgP2yvOSPBToC/HpnXYfAGtgblrxQORvgdik88CFWa3Lli1pwlpdzKQNWhBvglzq+IIS98wqzmwqGr8zKA+Iau2ByHdb1j3M9rrIY9V6oU9Gwim1apcRyfI/as3+QfPtt8BUAl2U7+PprxwJigyF/mcZnBJbd7IjrilE2gldZLxKlBiffoKVBinrEg3IQGJPt6k8riw264pBQEpcA0ZBsPUvMaISSxLb+d1ymp3WsiTJUjv+URR/HcdDa7P9jY+ouc8PQz4Yt+Ii38lM2tQU480APfVTyfj6drkjL/+54mYuxm8TzkBWcM2j6/FYT+8HvK/pF35wDJ3El+jGq7BARXg8HVxFsZgynJnhqhWDQb3xX9fK+N4K8+ct+HlsOSa5mP5i5Yo6WRTrWrFpyxVnv9crKePCiYqA2Y8ta8Wnh0sM06nLRtDbfbDjvXPQbaZqSnL4B2Cto08YoT/W/lu8QgJ3EEFlUdDOke4kv9yoXtuE0h7+8dwOvBNMVrBis3G2EYObgR4WmWjk4loYhqT9h3jrH0/5bGLzSKc++qYW0rOZ/RB21cRSe1ILQvSzWkImUoPI8b0i5baGSDq3EjTXYr3pIXSYpQk=
+    - secure: DOg+FgllLbyv7nEK3JJZfO/cvXy5K0L6QI/S9EJ/ivm4XBDCw1ayhrSQXvp1tMTPbWBEIv2gomPsHghJ+hVvX3dgwYdoNz9WZaNBB6lOO9U8OQW0LBsO5Eai6grzqOP35OuKtthuyR3dGJHAZo/XjhZM/jL0z6q1kNDzdS7ASwRwHJG0rHPGVlGeolH4nAity4KNJvyAspS1FYaIj9FEC/M7UT6nJVACbr9iMt/83teF/Oo2uoFI6Pa4K3nE2NViVFibToNOM3CV8kArDPDoNJviXxQ07ZM6fNijwehZ80waiPSaxFY8PLSntQNxGyB3DbomSTCcdVvtuHHQVmZgpVdvOJE8wk3R09+nq9U9FuUWLiRYSRbF1eF48YFnssPW1jEeVSenFRADcQ37e4B21ssLvXRHpQHpPVrYBZ8ffDamS7pKtEqocX/3Syc6irxHGCpxEdhvQ0Of5AWHhzB714VCijJJQiH9J4hEKXhMeAZ5SSt4eUavEHJKMhUcJ/aaku5w1+KtiYeOso1fTRbTYkEYb1A0bSNdXlGrYRRT+N683W7+ENiQIT+hTp617L/m5WQLIHfKe3gy5qt46kHFiUL3R3YlBI5OKjnLkDnUiA+0d4SFtJC/TBuPmH1fG4isAuvggTjzCb3kUG3ysin8AW0AKaXW0eqjKa/nsHWDKmc=
+  #      + for jonpspri/incubator-openwhisk-runtime-nodejs
+    - secure: "pgzD4SNjIOox5KQGhTCsEWdoS9I1rYxZ/aQt47FDybe1XXg6qOr+IXVvxH2nnGpqNW/0Ts29tX4j+K3Wu3ywYDKi6UiJ8riKcvMaIg/Rb0dmer6aJc3vmWxD7LQ8+Z0KLpcu+92AvUxewT0MuxIPK3yQOoE7Y6C+h/4fB2cInW73J+fdlkg82fvukGart1mMQaHWZn2GZYhN1TQXkFd/LGirV1iFEG5y/VS85lBGE2Y3UVOj8G2i1DyGvo9LxwkCF5XohE/TNLdOQ8Gk9IMnH29rhrcP1BGHcc8rb9YGbi2gFfV1SPo0SSf6IJfU+jQLyzE4+jXmc9DheISd72QyuxyVDLdJOHKqecdYeWUsvPgWh1Dzir0Z15Wy2fZU2Eabv9tophQOHX8MHRZQghoas1S5nKISDXNWsd3Xi5da8kyRNxhrzaufE7X1bfpj1vFtd3xlPmif/4arGlRDkRkBUQaIClxDIF6iAMnaej4m+eUsXA6yTSy+60W5IulW8dgX2bp1X1jzkUmndlI1HkoZKxt53o7/cU+pr574/JOEv9tQ2AG+CzcugCsGIPaxxvaaMBFzutPDaOuGXsWJ08FDPIqZCzrpeAt+crshN3NGXD9uGXk33cAG4XGAFBgVuDzqNELE0gh1qWfoqO660iwIHKOBlUri8ROsnkR8COFD0rk="
+    - secure:  "tWlr/4wB7iR3KsbeipKu46YRwa4xcnPQK57bKchlmVmbKIpjXJpu79z+/iTVx3RG0o7wnyvuUni7KML9Je5Z4r+5b7Rttor/cmArmbfaPtaLPSiMbVKDMhkoBct8xM7FqrV+YhZ2G2YzeME0TikH8GQZLFIlPcrrAvvrRmeLzf0PBfpkVsU9+t5EcMHUC6/o9iMbiUUu0ETXGUIgfC8xIka/8u6lsSDpUt2Gl+NoxNy73/qO/3ujwNjCQFT75IpDx/g0eyF1SkVzt7Yv4+pxJSiwjCg0H3J7f7zHMI96XHQWQN6N2rqQcgF61ohvWClz1/4vM4/xlAKc+kQLxo+lrPmEQEtQRdTarjezV1P7hOXlyc3Z3yayesU2yrSo7HZyRK1S0tHJt793IXqIwQkUWxXEM1Ibn715CW7HjXjYGgaI/s1+SP1dge5XQIGA94mJSIdKZ3vYaQNFqaykjt3zYPYcWP8hU3zOEkIZYvQmqxfG9JxMQNSU1xTfacOg6Ik1lbzXem+MU6YjlWb63UOeh0CQrdotAHqrNIfyNTmBng+z7sEuQDp+9ohRCrh8pd6QNdO/4TgZhuci9tiQuDI3azeXazCHEpDzaw97OfyW5DLskRHswgFA2taErmKImkpglATGJMFHs5oOv02XZvfLit0ze46ZrRVUeZxmhW2qSr0="
+  #  ---
+
+  #  --- BEGIN: DOCKER_EMAIL
+  #      + for apache/incubator-openwhisk-runtime-nodejs
+    - secure: "neCtFjl2U1qHplelpewE9F04IxVssa5g8lKaE0StE7VGVUERjdovh7jegOhlxH6eL5qjGpa423OSOMGjz7ltmBCE1UcCjoC1bfK2zwyBMrmMTx2MPJacIvZDkhys7hRqx5wDdsCNAnqrTp5UYEiBdtc63tpxrO0XZ4NwtZlFecArPmE2uBuKGNRjDG2GBa6aJXu34MIYCqLXAJ/jJZI/gH3MUbV7nqu0ZpWepJiEC+vDqS/ZmHV4PCjVar3UffaCcIxS1Mp2F2yCXiL4GMPLrnezveJCRLHPAQOKeDA4LKrBRT3NKyC1LyW4fynyk1reyy/NY7MWktZqRgm546zbvQuRcRAPEWvAGxTsLvWE85t02infVyV9DcXSf5deT5JL6c8EcqbdKklu673caMT9sG4PBD6CW8b0azjso5W4tutKArkujYrxxidvbQbRTzkXRrekhw0ql35yWeSCDu3k0QFN7nliuPcaCzCNwl4sdL5N/47HSkWzOD7JNMf5JLjawtVzNwBV4s8c0olABbHmXpyK9xKg7tbVkdi74PvvC7k2EsJ+q2ySPzQDQy7r+UUMvuDuK56JACPomKWW6LrgvUoyh1fv5ltcN2RGm+TIqzBXSKrzJt9neA4nba8Muel7RyDmbtZmwuGF9EZVuWE5dfhpyKZWwgNB0ILBgWILiKA="
+    #      + for jonpspri/incubator-openwhisk-runtime-nodejs
+    - secure: "uIP6gUcEMC6SYocfQKKBAN6n15msg7V+8r5jWjjrqicPOBmmlKlSc0o0ItawPc0qBh4b4woW9tl7DgmnbxQlb64zPVcaoKxAy+kYSiQ+uftyPibd0K77RG+L8svQuPnyln4jkFuWomV4MhleufXznN28zlmjP/rHkWO0xEXgV1hYneiddx+Yh8BAcG7jdtgSIOW+odeMvBOU8HC7IpThuN868RcZUx285eqt7Xrg6KNkOlf5CFyf1PtUNkRTgr5RW6cyzGRRyX9czy2Qyw55XrmJXBB3QjJEu9UXHVu2RLz2FG0wM8RYy6g32mtN4NgixYm0yjBEeR3oBJL8p13J4hhm5kh28vTLlkBVz0XO0rrsw0pcFyXrjEkAaKqalKp7sLKHAFAfSiAVZ8jXeiQ2ny5Rlzk/q8NmYRDYOhblP9j/mglcop51qX9Mxs+IgYHqnbVEAyf/EsIv7UT1xvAvSG/AZ0XHOvDB1zHybnD9XxVkkMdX0RRyndPQwccrnLXTX/1KM+0ux8QgHyLegfUCs6Am94Wbm52U6oraVQcvB7/K4g9ToaJ4Jps3K+8KTpTxTU4ENX1Rsk4UGa7fBBcQIr56ScKRAoY/K/O9yDf3HllqfVnqIKr88G/G/Y2Wi5SLUfW2KPIK+C1ExuGNcJ2rZZFhFi56dTU/dyhTv49fGP8="
+    #  ---
+
+  #  --- BEGIN: The secure entry immediately below is where the GPG passphrase is encrypted by Travis
+  #      + for apache/incubator-openwhisk-runtime-nodejs
+    - secure: "Ho3G92I4OlIlmD4KHtGrDWGJRdbch516yz2iLPDmLqcYmDUKyIhkdevNJSDlCBnSRGJg5h2tkM8W7V3sHqYaW7rBi48kx+CU7kWwht24PeOH2w2EB8Smy4bwgYxKrcmwoUMd6qqsjN+tagm0LzCOHzw5kcp0+aWOWj78s5O3+KhuHTJ7r6APec8/AI4H+9fIRJOhNGVsVa2oLyE1UDTHFLc+vvKxOi4W9FFd2yDCYRFzqT90IbD5qvti6O3wtZRKWVd7QM+PwkGj00LK6bdRmi2D4I421ohCJ5rkIQpj5QrO/XPrmGCpb6ueypyYDPa8T3R685+LPpOaQZTpbex6d3fEGdjHJv1UepXTsGX2esMBEI6d0LDD0Go+9ZfDDOSHBfoGgr66a1egg7ztxvDS4voTE8aDu/8ytGsxXg24TZP61GC/LHN4fxUT9tqUpMHo8YzyN4p+GJ4/OPXHPsgpi8l+Smm1m+PeRFD0EULxLf0DsXlh3PKAIU6iDzCqAaIQ8yLpp+kWNm5KE92sY8+0jTTGG3pM4V5yGvG7eKaxK6LXhK57KaHAoIJyYCBY8atDXu9sHtxU/gLMNNCNocjTZSyIOF/LmdPba518lPokafGXoYdnMpRXmOfcFfcsQmYPMyCYclZo8LKA6f6sVPBv6Os9NuJCEn6H1xsQ7wXrxn4="
+    #      + for jonpspri/incubator-openwhisk-runtime-nodejs
+    - secure: "1rpU5pWVHvOCYcjZZyMdtn6mlokJec6/qLjahwa0m7O9cUEe14zvdmNmql9TG/lfbEupkGBGpyXLsk4gl4CktBtj+kuP9VIT1ad0lGGfM455tqDD2fk+x54U7tk5imkR9Pn6Zmi0MCpvZSzCiOds+T2U9o9/uFB7WxXSyv+mjyosg2pHrK4hldoIfnhXIfFnNCjBXyGudnCQ4JW4Q+/9cDsc/rEPFjXo377dLBBQITvkORtCYUSI7WD7lBrB5u2GwZwmsJBwh0HojKOPe1gS325lC/7/PZAuxvMSDeiGOBp0JHEt2GdfJK2I8xrApigNfqZ9HaRDbBl+7XWqLzuQm/fAnPCuZOR3fYLpJrhzqjz3AZ8gQo+d7qqkLHfJxrL89egPQrjoPDaUdwm7Gp21ZjiqL5m9IAeJmtVSb9ZoXf8nqKyAGHn/K829EbKqore/4+x/6GbTm1tksyncSBU0p1Ulsfoo1lzUza9XFZGOiW9lHDxXMwT4XHg4y60cpkyHOuo98hMprB0M4GIPrDdn8OPvhJ4qcq4CveOAZtTAqWkhI+fQAL+h2T/7fIimczZ2na9+OEwBp7AEcHfTlIqQcf++KZPGd3WSNUoMlBVyE/VlLP5CArbVERIZVCd+/MP3XmuuD8OLq8CB2/cHK1Jz+J1ERPC4g1Qpy9T2SKxJv8I="
+    #  ---
+
+  #  --- BEGIN: The 'docker_local_json' variable configures docker engines
+  #             to use for multi-architecture builds.
+  #      + for apache/incubator-openwhisk-runtime-nodejs
+    - secure: "n+MSRxmg/7DjjUmcp5pmsUye9ZiOvenMZCmLXtCzIE9JAvSNl3jTm1vxwXr0UEZIIu5icE0Q56q9CId4vea+llKDF1lY+1GY0evxrbJy/iR0LONV/71lhCjQnwX5P81qN5RtPDXClNs5sLumwMYARjenhm13EuzyGkZR9SpEr1PduTzP8ZrWnF0l5GEN9EeLDFa0gYgNx7vkcBCYtFvPYYLSz6f25cayo5uX6wVv+TLnMbrA9Bw8ljh00Ghu9vSjelgrSUsXcBA0ScRZwj77jF1sxJuXahTIn+nToOO0ZEQEqX5odaKFcG1ACYtptMrZyfTkwFNJhA7EFGsE0fhx6jPO7pEyQCWENOzFCWOjNsP4ZUNEG5QEG5obZmH98/p8k0BoP3+ooYV4mIO55p6WqKtiqezwrqphyd4Hkvi4w4xs6s4Kr0dX7RiGQczi6o7/Ze7Gtk4FiZPllTjlSTep+K71tP3ea4lXWevlYBqMXUqegzoPDxQhaOa43BzlrIrYVNcP3CARhkCh7BUOkYPU+FwzpGcQdpvheDvEBHHqoqRlx8NshUitfwnXRHhQPfPLk31pH7f1dFuZAXd03hzrVmk6AFFS1qcJZ2qGKhmUfNXfBhSvqVh+7hGOaqIX5E5UZfse3AAB6jb1fkqUBFieV//MBrEdlhw8bVdW63/s8WA="
+    #      + for jonpspri/incubator-openwhisk-runtime-nodejs
+    - secure: "CfhV9EeROPpa+iMDdXD/DIeP7ComANg9eDzAdXgy0nu1er5jxXwoS1CAxQLC9nPASXvDRuEqUpAvYQIK/tVP9+qaVaI6YvmuP8ikOaz1YsxioQmg2huHfrpvcRS6OdhmUeJZJwWKjECyw7ya7HShBVVXLzJ+ms0TNgB3FnBDB0EoHIxBtDERPWIsBPgwLIIspM7YQ+c/zLoxSCIWYnHyq6c89ZorsJbk2XhB6OYk6FKjtFRTrKXJrXDsgUau/sNfLguYha4YNZuN6O7D7BpLztG9qCZJhsaywcakzZ/lE9MrSac44CJ06MwYkqQny/l31xtx+FKkLTW9NcI6KnoWAQ948e4Clit7P4Tb12P0m1rMzRmzGjqFT3sBJbirA9jcEy069X0KYu4AqxzsSqdXAO+cnPGSlJXVHyrtxefYjsystX8jU83JCes/f56/stXtGWm4E4evmunn6O7NeJ1KqHgq6jqDyJO2dCWQHTb1gT2dl0Q9wPXbTjlnG/aMkxcAOaMuOnakjXHTm8IMhd6QpBsMBSdfE7zGrrRyv8bsdgJEXGafNQAfNx1qpqSHQNL3VIQmYCBikXlXpjsP1I5O6SMf8+22ud6smtahvGvDMgF4JHEY92ARn4WH2t5e7NtzzPmvyOthcr6MLXahhRmPwYlQ2gUsCd6pxwEros76AFk="
+
+  # ---
diff --git a/README.md b/README.md
index 8a08c22..b03b878 100644
--- a/README.md
+++ b/README.md
@@ -21,107 +21,39 @@
 [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
 [![Build Status](https://travis-ci.org/apache/incubator-openwhisk-runtime-nodejs.svg?branch=master)](https://travis-ci.org/apache/incubator-openwhisk-runtime-nodejs)
 
+## Give it a try today
 
-### Give it a try today
 To use as a docker action for Node.js 6
+
 ```
 wsk action update myAction myAction.js --docker openwhisk/nodejs6action
 ```
-To use as a docker action for Node.js 8
-```
-wsk action update myAction myAction.js --docker openwhisk/action-nodejs-v8
-```
-This works on any deployment of Apache OpenWhisk
 
-### To use on deployment that contains the rutime as a kind
-To use as a kind action using Node.js 6
-```
-wsk action update myAction myAction.js --kind nodejs:6
-```
-To use as a kind action using Node.js 8
-```
-wsk action update myAction myAction.js --kind nodejs:8
-```
+To use as a docker action for Node.js 8
 
-### Local development
-For Node.js 6
 ```
-./gradlew core:nodejs6Action:distDocker
+wsk action update myAction myAction.js --docker openwhisk/action-nodejs-v8
 ```
-This will produce the image `whisk/nodejs6action`
 
-For Node.js 8
-```
-./gradlew core:nodejs8Action:distDocker
-```
-This will produce the image `whisk/action-nodejs-v8`
+This works on any deployment of Apache OpenWhisk
 
+## To use on deployment that contains the rutime as a kind
 
-Build and Push image for Node.js 6
-```
-docker login
-./gradlew core:nodejs6Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io
-```
+To use as a kind action using Node.js 6
 
-Build and Push image for Node.js 8
-```
-docker login
-./gradlew core:nodejs8Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io
 ```
-Then create the action using your image from dockerhub
-```
-wsk action update myAction myAction.js --docker $user_prefix/nodejs6action
+wsk action update myAction myAction.js --kind nodejs:6
 ```
-The `$user_prefix` is usually your dockerhub user id.
 
-Deploy OpenWhisk using ansible environment that contains the kind `nodejs:6` and `nodejs:8`
-Assuming you have OpenWhisk already deployed locally and `OPENWHISK_HOME` pointing to root directory of OpenWhisk core repository.
-
-Set `ROOTDIR` to the root directory of this repository.
+To use as a kind action using Node.js 8
 
-Redeploy OpenWhisk
 ```
-cd $OPENWHISK_HOME/ansible
-ANSIBLE_CMD="ansible-playbook -i ${ROOTDIR}/ansible/environments/local"
-$ANSIBLE_CMD setup.yml
-$ANSIBLE_CMD couchdb.yml
-$ANSIBLE_CMD initdb.yml
-$ANSIBLE_CMD wipe.yml
-$ANSIBLE_CMD openwhisk.yml
+wsk action update myAction myAction.js --kind nodejs:8
 ```
 
-Or you can use `wskdev` and create a soft link to the target ansible environment, for example:
-```
-ln -s ${ROOTDIR}/ansible/environments/local ${OPENWHISK_HOME}/ansible/environments/local-nodejs
-wskdev fresh -t local-nodejs
-```
+## Building and hacking
 
-### Testing
-Install dependencies from the root directory on $OPENWHISK_HOME repository
-```
-./gradlew install
-```
+This runtime has been converted to a multi-architecture build.  For details on
+building it, see
+[here](https://github.com/apache/incubator-openwhisk-runtime-nodejs/blob/master/docs/runtimes-building.md)
 
-Using gradle for the ActionContainer tests you need to use a proxy if running on Mac, if Linux then don't use proxy options
-You can pass the flags `-Dhttp.proxyHost=localhost -Dhttp.proxyPort=3128` directly in gradle command.
-Or save in your `$HOME/.gradle/gradle.properties`
-```
-systemProp.http.proxyHost=localhost
-systemProp.http.proxyPort=3128
-```
-Using gradle to run all tests
-```
-./gradlew :tests:test
-```
-Using gradle to run some tests
-```
-./gradlew :tests:test --tests *ActionContainerTests*
-```
-Using IntelliJ:
-- Import project as gradle project.
-- Make sure working directory is root of the project/repo
-- Add the following Java VM properties in ScalaTests Run Configuration, easiest is to change the Defaults for all ScalaTests to use this VM properties
-```
--Dhttp.proxyHost=localhost
--Dhttp.proxyPort=3128
-```
diff --git a/build.gradle b/build.gradle
index 300a1ce..e110833 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,9 +1,306 @@
+/*
+    NOTE TO MAINTAINERS:
+
+    You should not need to change this file to configure your specific
+    action image.  Image specific configurations occur in:
+
+        ./docker-local.gradle -- for secrets/configurations that should NOT
+            be uploaded to GitHub for the world to see
+        ./settings.grade -- for listing the projects (and imageNames) to build
+        ./.../build.grade -- for subproject-specific configurations
+
+    Note:  The writing is on the wall that as other project builds come into
+           play this module may need to move, perhaps to core/build.gradle or
+           to an included directory.  But we shall persevere for the time
+           being in keeping it here where it's obvious that a lot is going on.
+ */
+
 buildscript {
     repositories {
         jcenter()
+        mavenLocal()
+        maven {
+            url  "http://dl.bintray.com/jonpspri/gradle-plugins/"
+        }
     }
     dependencies {
         classpath "cz.alenkacz:gradle-scalafmt:${gradle.scalafmt.version}"
+        classpath 'org.ajoberstar:grgit:2.1.0'
+        classpath 'com.bmuschko:gradle-docker-plugin:3.2.2'
+        classpath 'com.xanophis:gradle-fatmanifest-plugin:0.0.5'
+    }
+}
+
+import com.bmuschko.gradle.docker.DockerRegistryCredentials
+import com.bmuschko.gradle.docker.tasks.DockerInfo
+import com.bmuschko.gradle.docker.tasks.image.*
+
+//  There are some serious naming mismatchees to be dealt with in v0.0.6 of gradle-fatmanifest-plugin :(
+import com.s390x.gradle.multidocker.tasks.*
+
+/*
+    So you called a registry method without providing any registry
+    credentials?  Shame on you!  This warning (which likely needs editing) will
+    put you back on the straight and narrow.
+
+    Located here in the code so it's available within all necessary blocks.
+ */
+private def warnNoRegistryCredentials() {
+    logger.warning """
+    |You are attempting a registry task without having provided registry
+    |credentials.  Registry credentials should be provided in the
+    |'./docker-local.gradle' file.  See './docker-local.gradle.sample' for
+    |an example.  Meanwhile, this action does nothing.
+    |""".stripMargin()
+}
+
+/*
+    These two lists will hold the projects to be managed throughout the
+    remainder of the process -- the 'top-level' builds and the individual
+    architecture builds created beneath each of them.
+
+    Note:  The lists could possible be created by re-constructing the project
+    paths, which in a performance-oriented system I'd consider doing.  But
+    the findAll based approach works for now, and provides flexibility if we
+    add later capability to settings to gradle to select only certain builds
+    for certain projects (more of a matrix)
+ */
+Collection<Project> buildProjects = subprojects.findAll { subproject ->
+        gradle.dockerBuildProjects.containsKey(subproject.path)
+    }
+
+Collection<Project> individualArchProjects = buildProjects.collectMany { buildProject ->
+        buildProject.subprojects.findAll() {
+            gradle.architectures.containsKey(it.name)
+        }
+    }
+
+/*
+    Every project in ':core:* is some form of Docker build for the action image
+    and is therefore assumed to need to be able to push a Manifest List for
+    eventual publication.  This block activates the Docker and Manifest List
+    plugins and configures them from information provided in the docker-local
+    file.  Recall that the docker-local file was loaded in
+    settings.gradle and applied as extension properties to the gradle object.
+
+    The :core:* projects (parent projects) should all have been set up and
+    logged in 'grade.dockerBuildProjects' (by settings.gradle), along with the
+    image name for each.  This is where we pull that image name and configure it
+    into the project.
+
+    Why put it in a gradle extension property first?  Frankly, we don't want
+    to wait for it to be set in a project-specific build.gradle, and we want
+    to limit configuration to the project-specific build.gradle and to the
+    settings.gradle.  This (root) build.gradle should rarely have to change.
+
+    (Side note:  Maybe these need to be in a plugin or sub-file, but IMO that
+    actually makes maintenance more difficult.)
+ */
+
+configure (buildProjects) {
+    buildscript.repositories { jcenter() }
+
+    logger.info ("Configuring subproject '${path}'")
+
+    /*
+        We only configure Manifest List processing if registryCredentials exist,
+        because otherwise what's the point?
+    */
+    if (gradle?.registryCredentials) {
+        apply plugin: 'com.s390x.gradle.multidocker'
+
+        logger.debug "Applying registry credentials to ${it.path}"
+        multidocker.registry('default',gradle.registryCredentials)
+    }
+
+    /*
+        Retrieve project properties from the Map created in settings.gradle
+        and apply to the projects.  Now the properties are available during
+        configuration in the remainder of this script.
+     */
+    logger.info "Setting properties for ${path}"
+    gradle.dockerBuildProjects[path].each { k,v -> ext.set(k,v) }
+    ext.dockerRegistry = gradle.dockerRegistry
+    logger.info "Project ${path} has dockerImageName of ${dockerImageName}"
+}
+
+/*
+    Each architecture gets its own project sharing a project directory with the
+    root project.  It's EXPECTED (but not required) for the local build scripts
+    to override the build directory with architecture-specific build directories
+    as needed, which will also ease maintenance of the source tree for docker
+    builds.
+
+    Notice that in the block, we ensure that parent tasks are dependent on
+    the underlying architecture tasks.  That way, we can still
+    './gradlew core:<image>:<task>' and all architectures will be built.
+ */
+
+configure (individualArchProjects) {
+    buildscript.repositories { jcenter() }
+
+    logger.info ("Configuring subproject '${path}'")
+
+    //  Since this is an architecture-specific subproject, we can copy all the
+    //  properties from the parent project to make our code more readable
+    parent.ext.properties.each { k,v -> ext.set(k,v) }
+
+    apply plugin: 'com.bmuschko.docker-remote-api'
+    docker {
+        /*
+            Since we survived the findAll, we know this architecture exists,
+            which means fewer guard clauses.  Hooray!
+         */
+        if (gradle.architectures[name]?.url) {
+            // Don't overwrite a default if it was defined!
+            url = gradle.architectures[name].url
+        }
+        if (gradle.architectures[name]?.certPath) {
+            certPath = rootProject.file(gradle.architectures[name].certPath)
+        }
+
+        /*
+            Yet another place to check whether registryCredentials were
+            actually provided.  It could be simplified by providing meaningless
+            defaults, but it's a bit clearer this way that there's a
+            responsiblity for the runtime developer.
+         */
+        if (gradle?.registryCredentials) registryCredentials {
+            url = gradle.registryCredentials.url
+            username = gradle.registryCredentials.username
+            password = gradle.registryCredentials.password
+            email = gradle.registryCredentials.email
+        }
+    }
+
+    task dockerInfo(type: DockerInfo) {
+        onNext {
+            logger.quiet "Docker Info retreived for project ${project.name}:"
+            logger.quiet "  OSType = ${it.osType}"
+            logger.quiet "  Architecture = ${it.architecture}"
+            owner.ext.dockerInfo = it
+        }
+    }
+    (parent.tasks.find() {it.name=='dockerInfo'} ?: parent.task('dockerInfo'))\
+        .dependsOn dockerInfo
+
+    task dockerBuildImage(type: DockerBuildImage) {
+        /*
+            Recommended default for inputDir
+         */
+        inputDir = file("${project.projectDir}/build/docker/${project.name}")
+
+        /*
+            The tags list is out of hand and is doing a bang-up job of making
+            my local docker repository messy.  However, I haven't really
+            gotten a handle on what's needed and what's not.  For example,
+            the dockerRegistry tag should probably be conditional.  The
+            prefixed tag seems critical, however, to working with local-build
+            openwhisk deployments.
+         */
+
+        //  Is this an appropriate prefix?  Perhaps we should use 'whisk' as the
+        //  default local-build prefix?  Again -- not sure I have a sense of tags.
+        def prefix = findProperty('dockerImagePrefix') ?: 'openwhisk'
+        def tag = findProperty('dockerImageTag') ?: 'latest'
+
+        //  Note that project.name will be the docker engine name from settings.gradle
+        //  We could set up an option to declare suffixes in docker.gradle, but so far
+        //  this works.
+        tags = [   // Return a list of tags build from the environment
+                "${parent.dockerImageName}:${tag}",
+                "${parent.dockerImageName}:${tag}-${project.name}",
+                "${prefix}/${parent.dockerImageName}:${tag}-${project.name}"
+        ] as String[]
+
+        //  This tag is created to support uploads if there's a target registry.
+        //  Note that the script DOES NOT support multiple target registries.
+        //  As of the writing, there was no need to.
+        if (dockerRegistry) {
+            tags += new String("${dockerRegistry}/${prefix}/"+
+                "${parent.dockerImageName}:${tag}-${project.name}")
+        }
+    }
+    (parent.tasks.find() {it.name=='dockerBuildImage'} ?: parent.task('dockerBuildImage'))\
+        .dependsOn dockerBuildImage
+
+    /*
+        Pushing images and getting manifests only makes sense if registry
+        credentials were provided, we we make this block conditional.  If
+        no credentials were provided, the tasks will instead display a warning
+        and point to a tutorial on docker-local.gradle.
+     */
+    if (gradle?.registryCredentials) {
+        task dockerPushImage(type: DockerPushImage, dependsOn: dockerBuildImage) {
+            imageName = "${dockerRegistry}/" +
+                "${findProperty('dockerImagePrefix') ?: 'openwhisk'}/" +
+                "${parent.dockerImageName}"
+            tag = "${findProperty('dockerImageTag') ?: 'latest'}-${project.name}"
+        }
+        (parent.tasks.find() {it.name=='dockerPushImage'} ?: parent.task('dockerPushImage'))\
+            .dependsOn dockerPushImage
+
+        task getImageManifest(type: GetImageManifest, dependsOn: [ dockerPushImage, dockerInfo ] ) {
+
+            targetRegistry = 'default'
+
+            imageName =
+                "${findProperty('dockerImagePrefix') ?: 'openwhisk'}/${parent.dockerImageName}"
+            tag = "${findProperty('dockerImageTag') ?: 'latest'}-${project.name}"
+
+            //  Store the relevant details of the received manifest as an
+            //  extension of the task.
+            onNext {
+                logger.quiet "Manifest retreived for ${imageName}:${tag}"
+                logger.quiet "  Digest: ${it.digest}"
+                logger.quiet "  Media Type: ${it.mediaType}"
+                logger.quiet "  Size: ${it.size}"
+
+                owner.ext.manifestMap = [
+                    mediaType: it.mediaType,
+                    digest: it.digest,
+                    size: it.size,
+                    os: project.dockerInfo.dockerInfo.osType,
+                    architecture: project.dockerInfo.dockerInfo.architecture
+                ]
+            }
+        }
+        (parent.tasks.find() {it.name=='getImageManifest'} ?: parent.task('getImageManifest'))\
+            .dependsOn getImageManifest
+    } else {
+        task dockerPushImage() { warnNoRegistryCredentials() }
+        task getImageManifest() { warnNoRegistryCredentials() }
+    }
+}
+
+/*
+    And now, the Manifest List.  It's a multi-architecture manifest, so there's
+    no point to putting it in the individual architecture builds.  Instead, it
+    will collect the individual manifests from the getManifest steps.
+ */
+configure (buildProjects) {
+    if (gradle?.registryCredentials) {
+        task putManifestList(type: PutManifestList, dependsOn: 'getImageManifest') {
+
+            targetRegistry = 'default'
+
+            imageName = "${findProperty('dockerImagePrefix') ?: 'openwhisk'}/" +
+                "${project.dockerImageName}"
+            // TODO - Project property candidate to support release builds?
+            tag = "${findProperty('dockerImageTag') ?: 'latest' }"
+
+            /*
+                Collect the manifests to be built from the subprojects
+             */
+            project.subprojects.collectMany { subproject ->
+                subproject.tasks.withType(GetImageManifest)
+            }.each { task ->
+                logger.info "Adding manifest ${it.path} to Manifest List"
+                manifests << { task.manifestMap }
+            }
+        }
+    } else {
+        task putManifestList() { warnNoRegistryCredentials() }
     }
 }
 
diff --git a/core/nodejs6Action/Dockerfile b/core/nodejs6Action/Dockerfile
index 78d903f..81ceed4 100644
--- a/core/nodejs6Action/Dockerfile
+++ b/core/nodejs6Action/Dockerfile
@@ -18,10 +18,10 @@ FROM nodejsactionbase
 
 # based on https://github.com/nodejs/docker-node
 ENV NODE_VERSION 6.14.1
-RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" \
-  && tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
-  && rm "node-v$NODE_VERSION-linux-x64.tar.gz"
-
+RUN node_arch=$(uname -m | sed -e 's/x86_64/x64/g' -e 's/aarch64/arm64/g') \
+  && curl -sSLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-${node_arch}.tar.gz" \
+  && tar -xzf "node-v$NODE_VERSION-linux-${node_arch}.tar.gz" -C /usr/local --strip-components=1 \
+  && rm "node-v$NODE_VERSION-linux-${node_arch}.tar.gz"
 
 # workaround for this: https://github.com/npm/npm/issues/9863
 RUN cd $(npm root -g)/npm \
diff --git a/core/nodejs6Action/build.gradle b/core/nodejs6Action/build.gradle
index b271f5e..9042b98 100644
--- a/core/nodejs6Action/build.gradle
+++ b/core/nodejs6Action/build.gradle
@@ -6,6 +6,10 @@ eclipse {
     }
 }
 
-ext.dockerImageName = 'nodejs6action'
-apply from: '../../gradle/docker.gradle'
-distDocker.dependsOn ':core:nodejsActionBase:distDocker'
+subprojects {
+    dockerBuildImage.with() {
+        dependsOn ':core:nodejsActionBase:dockerBuildImage'
+        inputDir=projectDir
+    }
+}
+
diff --git a/core/nodejs8Action/.dockerignore b/core/nodejs8Action/.dockerignore
deleted file mode 100644
index 3081e3e..0000000
--- a/core/nodejs8Action/.dockerignore
+++ /dev/null
@@ -1,8 +0,0 @@
-node_modules
-package-lock.json
-Dockerfile
-build.gradle
-.project
-.settings
-build.xml
-logs
\ No newline at end of file
diff --git a/core/nodejs8Action/build.gradle b/core/nodejs8Action/build.gradle
index b4dd28a..01af14a 100644
--- a/core/nodejs8Action/build.gradle
+++ b/core/nodejs8Action/build.gradle
@@ -6,14 +6,6 @@ eclipse {
     }
 }
 
-ext.dockerImageName = 'action-nodejs-v8'
-apply from: '../../gradle/docker.gradle'
-
-distDocker.dependsOn 'copyProxy'
-distDocker.dependsOn 'copyRunner'
-distDocker.dependsOn 'copyService'
-distDocker.finalizedBy('cleanup')
-
 task copyProxy(type: Copy) {
     from '../nodejsActionBase/app.js'
     into '.'
@@ -34,3 +26,13 @@ task cleanup(type: Delete) {
     delete 'runner.js'
     delete 'src'
 }
+
+subprojects {
+    dockerBuildImage.with() {
+        dependsOn copyProxy
+        dependsOn copyRunner
+        dependsOn copyService
+        inputDir=projectDir
+        finalizedBy(cleanup)
+    }
+}
diff --git a/core/nodejsActionBase/.dockerignore b/core/nodejsActionBase/.dockerignore
deleted file mode 100644
index 6df164e..0000000
--- a/core/nodejsActionBase/.dockerignore
+++ /dev/null
@@ -1,2 +0,0 @@
-Dockerfile
-build.gradle
diff --git a/core/nodejsActionBase/Dockerfile b/core/nodejsActionBase/Dockerfile.in
similarity index 88%
rename from core/nodejsActionBase/Dockerfile
rename to core/nodejsActionBase/Dockerfile.in
index 4a1443a..6f6fc81 100644
--- a/core/nodejsActionBase/Dockerfile
+++ b/core/nodejsActionBase/Dockerfile.in
@@ -14,7 +14,8 @@
 # specific language governing permissions and limitations under the License.
 #
 
-FROM buildpack-deps:trusty-curl
+<% dockerInfo = dockerInfoClosure.call() %>
+FROM buildpack-deps:${(dockerInfo.architecture=='s390x')?'xenial':'trusty'}-curl
 
 ENV DEBIAN_FRONTEND noninteractive
 
diff --git a/core/nodejsActionBase/build.gradle b/core/nodejsActionBase/build.gradle
index f6ae7ee..a99ae03 100644
--- a/core/nodejsActionBase/build.gradle
+++ b/core/nodejsActionBase/build.gradle
@@ -6,5 +6,23 @@ eclipse {
     }
 }
 
-ext.dockerImageName = 'nodejsactionbase'
-apply from: '../../gradle/docker.gradle'
+import com.bmuschko.gradle.docker.tasks.DockerInfo
+
+subprojects {
+
+    task dockerFiles(type: Copy, dependsOn: dockerInfo) {
+
+        from('.') {
+            include '*.js'
+            include 'src/*.js'
+            include 'package.json'
+        }
+        from('./Dockerfile.in') {
+            rename { 'Dockerfile' }
+            expand( dockerInfoClosure: { project.dockerInfo.dockerInfo } )
+        }
+        into dockerBuildImage.inputDir
+    }
+
+    dockerBuildImage.dependsOn dockerFiles
+}
diff --git a/docs/runtimes-building.md b/docs/runtimes-building.md
new file mode 100644
index 0000000..c3f8067
--- /dev/null
+++ b/docs/runtimes-building.md
@@ -0,0 +1,150 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Working with Runtimes locally
+
+This document is designed to be applied to the various
+`incubator-openwhisk-runtimes-*` repositories as they are converted to
+multi-architecture build. The multi-architecture build supports building Docker
+multi-architecture manifest lists across (currently) amd64, Power 64
+(little-endian), and System Z (s390x) architectures.  The framework is designed
+to extend to other chips, including the Arm64 chipset.
+
+You can tell that a runtime has been converted to a multi-architecture build
+by referring to its root `README.md` file.
+
+## Reading this document
+
+For this document, we will assume an environment variable `ACTION_KIND` has
+been set to the directory name of an action in the runtime repository on which
+work is being done (i.e. set to `nodejs6Action` while working in
+`incubator-openwhisk-runtime-nodejs`).
+
+## Building a single-architecture Docker image locally
+
+To build a single-architecture image locally on the local machine, execute
+
+```
+docker_local_json='{"local":null}' ./gradlew core:$ACTION_KIND:dockerBuildImage
+```
+
+(For the curious at heart, the assignment of `docker_local_json` serves to
+document to the script that the command is to build a single-architecture image
+on the default docker connection).
+
+To check on what Docker images will be produced, look in the `settings.json` file in
+the runtime repository (look for `gradle.ext.dockerBuildProjects =`).  The
+default image prefix is currently 'whisk'.
+
+## Building a multi-architecture Docker image
+
+Multi-architecture builds work be repeating build steps on independent docker
+instances for each architecture. To ensure security, it's necessary to set up
+the docker instances to use TLS and to have the TLS client certificates (
+`ca.pem`, `cert.pem` and `key.pem`) available in some directory on the local
+machine.  For details on setting up docker with TLS see [the official docker
+documentation](https://docs.docker.com/engine/security/https/).
+
+To build a multi-architecture image requires providing more sophisticated
+JSON defining the build docker environment for each architecture.  While this
+could be done on the command line (as above), it is generally easier to create
+a `docker-local.json` file in the root directory of the runtime.  (Don't
+worry, it's `.gitignore`d and won't find its way into the open.)
+
+Here's a (sanitized) example of a docker-local.json file:
+
+```
+{
+    "amd64": null,
+    "ppc64le": {
+        "url": "https://ppc64le.example.com:2376",
+        "certPath": "./tls/ppc64le"
+    },
+    "s390x": {
+        "url": "https://s390x.example.com:2376",
+        "certPath": "./tls/s390x"
+    }
+}
+```
+
+In this example `amd64` is local/default (`null`), while `ppc64le` and `s390x`
+both use remote docker instances to build.  The certificates for the remote
+instances are stored in subdirectories of `tls` in the working directory.
+(`./tls` is also `.gitignore`d).
+
+Also, the names of the individual configuration objects do not matter.  While
+it is useful to name them after the target architecture, in reality the build
+script queries the individual dockers to determine their architectures.  The
+names _are_, however, used to construct image tag names in docker, so it is
+necessary that they be unique and legitimate symbols for docker tags.
+
+Once the `docker-local.json` file is set up, building is a matter of executing
+
+```
+./gradlew core:$ACTION_KIND:dockerBuildImage
+```
+
+By default, the images built will be tagged `latest-<arch>`, for example
+`latest-s390x`.
+
+## Pushing images (not to be confused with "Pushing Tin")
+
+Now we're ready for the big leagues, registering images that we build with
+a docker registry, either Docker Hub or an enterprise/private registry.
+
+To configure the docker registry, set these environment variables:
+
+- `DOCKER_REGISTRY` - the name of the registry (i.e. `docker.io`)
+- `DOCKER_USER`
+- `DOCKER_PASSWORD`
+- `DOCKER_EMAIL`
+
+One configured, pushing images is a matter of setting (or substituting) a
+`user_prefix` and executing
+
+```
+./gradlew core:$ACTION_KIND:dockerPushImage -PdockerImagePrefix=$user_prefix
+```
+
+This pushes *individual* images tagged for the separate architectures.
+
+Note: the `user_prefix` will default to `openwhisk` if not provided.
+
+## Putting the multi-architecture manifest list
+
+At this point, putting a multi-architecture manifest list that can be pulled
+by any supported architecture is simply a matter of executing
+
+```
+./gradlew core:$ACTION_KIND:dockerPushImage -PdockerImagePrefix=$user_prefix
+```
+
+The daring can feel free to start with this command which, if properly
+configured, will run all preliminaries.
+
+## Using an action in your OpenWhisk environment
+
+To use a locally built image in a running OpenWhisk environment, execute
+
+```
+wsk action update myAction myAction.js --docker $DOCKER_REGISTRY/$user_prefix/$image_name
+```
+
+where the action name and contect (e.g. myAction.js) are decided by you and
+appropriate, and the `$image_name` is that which was built above.
+
diff --git a/gradle/docker.gradle b/gradle/docker.gradle
deleted file mode 100644
index f716c7b..0000000
--- a/gradle/docker.gradle
+++ /dev/null
@@ -1,99 +0,0 @@
-import groovy.time.*
-
-/**
- * Utility to build docker images based in gradle projects
- *
- * This extends gradle's 'application' plugin logic with a 'distDocker' task which builds
- * a docker image from the Dockerfile of the project that applies this file. The image
- * is automatically tagged and pushed if a tag and/or a registry is given.
- *
- * Parameters that can be set on project level:
- * - dockerImageName (required): The name of the image to build (e.g. controller)
- * - dockerRegistry (optional): The registry to push to
- * - dockerImageTag (optional, default 'latest'): The tag for the image
- * - dockerImagePrefix (optional, default 'whisk'): The prefix for the image,
- *       'controller' becomes 'whisk/controller' per default
- * - dockerTimeout (optional, default 840): Timeout for docker operations in seconds
- * - dockerRetries (optional, default 3): How many times to retry docker operations
- * - dockerBinary (optional, default 'docker'): The binary to execute docker commands
- * - dockerBuildArgs (options, default ''): Project specific custom docker build arguments
- * - dockerHost (optional): The docker host to run commands on, default behaviour is
- *       docker's own DOCKER_HOST environment variable
- */
-
-ext {
-    dockerRegistry = project.hasProperty('dockerRegistry') ? dockerRegistry + '/' : ''
-    dockerImageTag = project.hasProperty('dockerImageTag') ? dockerImageTag : 'latest'
-    dockerImagePrefix = project.hasProperty('dockerImagePrefix') ? dockerImagePrefix : 'whisk'
-    dockerTimeout = project.hasProperty('dockerTimeout') ? dockerTimeout.toInteger() : 840
-    dockerRetries = project.hasProperty('dockerRetries') ? dockerRetries.toInteger() : 3
-    dockerBinary = project.hasProperty('dockerBinary') ? [dockerBinary] : ['docker']
-    dockerBuildArg = ['build']
-}
-ext.dockerTaggedImageName = dockerRegistry + dockerImagePrefix + '/' + dockerImageName + ':' + dockerImageTag
-
-if(project.hasProperty('dockerHost')) {
-    dockerBinary += ['--host', project.dockerHost]
-}
-
-if(project.hasProperty('dockerBuildArgs')) {
-    dockerBuildArgs.each { arg  ->
-        dockerBuildArg += ['--build-arg', arg]
-    }
-}
-
-task distDocker {
-    doLast {
-        def start = new Date()
-        def cmd = dockerBinary + dockerBuildArg + ['-t', dockerImageName, project.buildscript.sourceFile.getParentFile().getAbsolutePath()]
-        retry(cmd, dockerRetries, dockerTimeout)
-        println("Building '${dockerImageName}' took ${TimeCategory.minus(new Date(), start)}")
-    }
-}
-task tagImage {
-    doLast {
-        def versionString = (dockerBinary + ['-v']).execute().text
-        def matched = (versionString =~ /(\d+)\.(\d+)\.(\d+)/)
-
-        def major = matched[0][1] as int
-        def minor = matched[0][2] as int
-
-        def dockerCmd = ['tag']
-        if(major == 1 && minor < 12) {
-            dockerCmd += ['-f']
-        }
-        retry(dockerBinary + dockerCmd + [dockerImageName, dockerTaggedImageName], dockerRetries, dockerTimeout)
-    }
-}
-
-task pushImage {
-    doLast {
-        def cmd = dockerBinary + ['push', dockerTaggedImageName]
-        retry(cmd, dockerRetries, dockerTimeout)
-    }
-}
-pushImage.dependsOn tagImage
-pushImage.onlyIf { dockerRegistry != '' }
-distDocker.finalizedBy pushImage
-
-def retry(cmd, retries, timeout) {
-    println("${new Date()}: Executing '${cmd.join(" ")}'")
-    def proc = cmd.execute()
-    proc.consumeProcessOutput(System.out, System.err)
-    proc.waitForOrKill(timeout * 1000)
-    if(proc.exitValue() != 0) {
-        def message = "${new Date()}: Command '${cmd.join(" ")}' failed with exitCode ${proc.exitValue()}"
-        if(proc.exitValue() == 143) { // 143 means the process was killed (SIGTERM signal)
-            message = "${new Date()}: Command '${cmd.join(" ")}' was killed after ${timeout} seconds"
-        }
-
-        if(retries > 1) {
-            println("${message}, ${retries-1} retries left, retrying...")
-            retry(cmd, retries-1, timeout)
-        }
-        else {
-            println("${message}, no more retries left, aborting...")
-            throw new GradleException(message)
-        }
-    }
-}
diff --git a/settings.gradle b/settings.gradle
index c304cf9..e9db464 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,12 +1,77 @@
+import groovy.json.JsonSlurper
 include 'tests'
 
-include 'core:nodejsActionBase'
-include 'core:nodejs6Action'
+/* -- BEGIN: Project-specific configuration -- */
+/* TODO: Make this into a JSON configuration file? */
+rootProject.name = 'runtime-nodejs'
+
+gradle.ext.dockerBuildProjects =
+[
+    /* TODO: default imageName to project folder name in lowercase? */
+    ':core:nodejsActionBase': [ 'dockerImageName': 'nodejsactionbase' ],
+    ':core:nodejs6Action': [ 'dockerImageName': 'nodejs6action' ],
+
+    ':core:nodejs8Action': [ 'dockerImageName': 'action-nodejs-v8' ]
+]
+/* -- END: Project-specific configuration -- */
+
+/*
+    Quick example of the format of the architectures input:
+
+    {
+        "amd64": null,
+        "ppc64le": {
+            "url": "https://1.2.3.4:2376",
+            "certPath": "/home/myusername/tls/ppc64le"
+        },
+        "s390x": {
+            "url": "https://my-personal-docker.my-domain.com:2376",
+            "certPath": "/home/myusername/tls/s390x"
+        }
+    }
+ */
+
+new JsonSlurper().with { slurper ->
+    def dockerLocalJsonEnv = System.getenv('docker_local_json')
+    def dockerLocalJsonFile = new File(rootProject.projectDir, 'docker-local.json')
 
-include 'core:nodejs8Action'
+    if (dockerLocalJsonEnv) {
+        gradle.ext.architectures = slurper.parseText(dockerLocalJsonEnv)
+    } else if (dockerLocalJsonFile.exists()) {
+        gradle.ext.architectures = slurper.parse(dockerLocalJsonFile)
+    } else {
+        gradle.ext.architectures = [ 'amd64': null ]
+    }
+}
+
+gradle.ext.dockerRegistry = System.getenv('DOCKER_REGISTRY') ?: 'docker.io'
+
+gradle.ext.registryCredentials = [
+    name: gradle.ext.dockerRegistry,
+    url: "https://${gradle.ext.dockerRegistry}/v2/" as String,
+    username: System.getenv('DOCKER_USER'),
+    password: System.getenv('DOCKER_PASSWORD'),
+    email: System.getenv('DOCKER_EMAIL') ?: 'dev@openwhisk.apache.org',
+]
 
 rootProject.name = 'runtime-nodejs'
 
+gradle.dockerBuildProjects.each() { baseProjectName, extensions ->
+    include baseProjectName
+    def baseProject = findProject(baseProjectName)
+
+    gradle.architectures.each() { architectureName, architectureClosure ->
+        def architectureProjectName = "${baseProjectName}:${architectureName}"
+        include architectureProjectName
+        def architectureProject = findProject(architectureProjectName)
+
+        logger.debug 'Setup baseProject: ' + baseProject?.path
+        logger.debug 'Setup architectureProject: ' + architectureProject?.path
+
+        architectureProject.projectDir = baseProject.projectDir
+    }
+}
+
 gradle.ext.openwhisk = [
         version: '1.0.0-SNAPSHOT'
 ]
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index e06db53..58b4746 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -20,39 +20,48 @@ set -ex
 
 # Build script for Travis-CI.
 
-SCRIPTDIR=$(cd $(dirname "$0") && pwd)
-ROOTDIR="$SCRIPTDIR/../.."
-WHISKDIR="$ROOTDIR/../openwhisk"
-UTILDIR="$ROOTDIR/../incubator-openwhisk-utilities"
-
-export OPENWHISK_HOME=$WHISKDIR
+SCRIPTDIR=$(cd "$(dirname "$0")" && pwd)
+ROOTDIR=$(cd "$SCRIPTDIR/../.." && pwd)
+WHISKDIR=$(cd "$ROOTDIR/../openwhisk" && pwd)
+UTILDIR=$(cd "$ROOTDIR/../incubator-openwhisk-utilities" && pwd)
 
 IMAGE_PREFIX="testing"
 
 # run scancode using the ASF Release configuration
-cd $UTILDIR
-scancode/scanCode.py --config scancode/ASF-Release-v2.cfg $ROOTDIR
+echo "---------------------------------------------------------------------------------------"
+echo " Checking for well-formed code "
+echo "---------------------------------------------------------------------------------------"
+cd "$UTILDIR"
+scancode/scanCode.py --config scancode/ASF-Release-v2.cfg "$ROOTDIR"
 
 # Build OpenWhisk
-cd $WHISKDIR
-
-#pull down images
-docker pull openwhisk/controller
-docker tag openwhisk/controller ${IMAGE_PREFIX}/controller
-docker pull openwhisk/invoker
-docker tag openwhisk/invoker ${IMAGE_PREFIX}/invoker
-docker pull openwhisk/nodejs6action
-docker tag openwhisk/nodejs6action ${IMAGE_PREFIX}/nodejs6action
-
-TERM=dumb ./gradlew \
-:common:scala:install \
-:core:controller:install \
-:core:invoker:install \
-:tests:install
+echo "---------------------------------------------------------------------------------------"
+echo " Building common test components "
+echo "---------------------------------------------------------------------------------------"
+cd "$WHISKDIR"
+
+./gradlew --console=plain \
+  :common:scala:install \
+  :core:controller:install \
+  :core:invoker:install \
+  :tests:install
+
+# Determine which NodeJS(s) are to be built in this run
+# shellcheck disable=SC2016
+case "$NODEJS_VERSION" in
+  6) builds=( :core:nodejs6Action:dockerBuildImage );;
+  8) builds=( :core:nodejs8Action:dockerBuildImage );;
+  *) echo 'Must set a value $NODEJS_VERSION'; exit 256;;
+esac
 
 # Build runtime
-cd $ROOTDIR
-TERM=dumb ./gradlew \
-:core:nodejs6Action:distDocker \
-:core:nodejs8Action:distDocker \
--PdockerImagePrefix=${IMAGE_PREFIX}
+echo "---------------------------------------------------------------------------------------"
+echo " Building " "${builds[@]}"
+echo "---------------------------------------------------------------------------------------"
+
+cd "$ROOTDIR"
+export docker_local_json='{"local":null}'
+./gradlew --stacktrace --console=plain "${builds[@]}" -PdockerImagePrefix=${IMAGE_PREFIX}
+
+echo "---------------------------------------------------------------------------------------"
+
diff --git a/tools/travis/deploy.sh b/tools/travis/deploy.sh
deleted file mode 100755
index 58b0988..0000000
--- a/tools/travis/deploy.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-#
-# 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.
-#
-
-set -ex
-
-# Build script for Travis-CI.
-
-SCRIPTDIR=$(cd $(dirname "$0") && pwd)
-ROOTDIR="$SCRIPTDIR/../.."
-WHISKDIR="$ROOTDIR/../openwhisk"
-
-export OPENWHISK_HOME=$WHISKDIR
-
-IMAGE_PREFIX="testing"
-
-# Deploy OpenWhisk
-cd $WHISKDIR/ansible
-ANSIBLE_CMD="ansible-playbook -i ${ROOTDIR}/ansible/environments/local -e docker_image_prefix=${IMAGE_PREFIX}"
-$ANSIBLE_CMD setup.yml
-$ANSIBLE_CMD prereq.yml
-$ANSIBLE_CMD couchdb.yml
-$ANSIBLE_CMD initdb.yml
-$ANSIBLE_CMD wipe.yml
-$ANSIBLE_CMD openwhisk.yml -e cli_installation_mode=remote
-
-docker images
-docker ps
-
-cat $WHISKDIR/whisk.properties
-curl -s -k https://172.17.0.1 | jq .
-curl -s -k https://172.17.0.1/api/v1 | jq .
-
-#Deployment
-WHISK_APIHOST="172.17.0.1"
-WHISK_AUTH=`cat ${WHISKDIR}/ansible/files/auth.guest`
-WHISK_CLI="${WHISKDIR}/bin/wsk -i"
-
-${WHISK_CLI} property set --apihost ${WHISK_APIHOST} --auth ${WHISK_AUTH}
-${WHISK_CLI} property get
diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh
index cfdd8d3..d50bb7c 100755
--- a/tools/travis/publish.sh
+++ b/tools/travis/publish.sh
@@ -36,14 +36,9 @@ elif [ ${RUNTIME_VERSION} == "8" ]; then
   RUNTIME="nodejs8Action"
 fi
 
-if [[ ! -z ${DOCKER_USER} ]] && [[ ! -z ${DOCKER_PASSWORD} ]]; then
-docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
-fi
-
 if [[ ! -z ${RUNTIME} ]]; then
-TERM=dumb ./gradlew \
-:core:${RUNTIME}:distDocker \
--PdockerRegistry=docker.io \
+./gradlew --console=plain \
+:core:${RUNTIME}:putManifestList \
 -PdockerImagePrefix=${IMAGE_PREFIX} \
 -PdockerImageTag=${IMAGE_TAG}
 fi
diff --git a/tools/travis/setup.sh b/tools/travis/setup.sh
index 3424cdb..560e0ab 100755
--- a/tools/travis/setup.sh
+++ b/tools/travis/setup.sh
@@ -29,6 +29,7 @@ cd $HOMEDIR
 git clone https://github.com/apache/incubator-openwhisk-utilities.git
 
 # clone main openwhisk repo. for testing purposes
-git clone --depth=1 https://github.com/apache/incubator-openwhisk.git openwhisk
+git clone --branch "${OW_REPO_BRANCH:-master}" --depth=1 \
+  "https://github.com/${OW_REPO_OWNER:-apache}/incubator-openwhisk.git" openwhisk
 cd openwhisk
 ./tools/travis/setup.sh
diff --git a/tools/travis/test.sh b/tools/travis/test.sh
index f6f9f5c..c55b8fc 100755
--- a/tools/travis/test.sh
+++ b/tools/travis/test.sh
@@ -16,19 +16,17 @@
 # limitations under the License.
 #
 
-set -ex
-
-# Build script for Travis-CI.
+set -e
 
 SCRIPTDIR=$(cd $(dirname "$0") && pwd)
-ROOTDIR="$SCRIPTDIR/../.."
-WHISKDIR="$ROOTDIR/../openwhisk"
+ROOTDIR=$(cd "$SCRIPTDIR/../.." && pwd)
 
-export OPENWHISK_HOME=$WHISKDIR
+case "$NODEJS_VERSION" in
+  6) tests='*NodeJs6ActionContainerTests*' ;;
+  8) tests='*NodeJs8ActionContainerTests*' ;;
+  *) echo 'Must set a value $NODEJS_VERSION'; exit 256;;
+esac
 
-cd ${ROOTDIR}
+cd "$ROOTDIR"
 TERM=dumb ./gradlew :tests:checkScalafmtAll
-TERM=dumb ./gradlew :tests:test --tests *NodeJs*Tests
-
-
-
+TERM=dumb ./gradlew :tests:test --tests "${tests}"
diff --git a/tools/travis/tls/apache/ppc64le/ca.pem.gpg b/tools/travis/tls/apache/ppc64le/ca.pem.gpg
new file mode 100644
index 0000000..2d53007
Binary files /dev/null and b/tools/travis/tls/apache/ppc64le/ca.pem.gpg differ
diff --git a/tools/travis/tls/apache/ppc64le/cert.pem.gpg b/tools/travis/tls/apache/ppc64le/cert.pem.gpg
new file mode 100644
index 0000000..1ff14aa
Binary files /dev/null and b/tools/travis/tls/apache/ppc64le/cert.pem.gpg differ
diff --git a/tools/travis/tls/apache/ppc64le/key.pem.gpg b/tools/travis/tls/apache/ppc64le/key.pem.gpg
new file mode 100644
index 0000000..7b898a8
Binary files /dev/null and b/tools/travis/tls/apache/ppc64le/key.pem.gpg differ
diff --git a/tools/travis/tls/apache/s390x/ca.pem.gpg b/tools/travis/tls/apache/s390x/ca.pem.gpg
new file mode 100644
index 0000000..69776e6
Binary files /dev/null and b/tools/travis/tls/apache/s390x/ca.pem.gpg differ
diff --git a/tools/travis/tls/apache/s390x/cert.pem.gpg b/tools/travis/tls/apache/s390x/cert.pem.gpg
new file mode 100644
index 0000000..8910f66
Binary files /dev/null and b/tools/travis/tls/apache/s390x/cert.pem.gpg differ
diff --git a/tools/travis/tls/apache/s390x/key.pem.gpg b/tools/travis/tls/apache/s390x/key.pem.gpg
new file mode 100644
index 0000000..c8e11f6
Binary files /dev/null and b/tools/travis/tls/apache/s390x/key.pem.gpg differ
diff --git a/tools/travis/tls/jonpspri/ppc64le/ca.pem.gpg b/tools/travis/tls/jonpspri/ppc64le/ca.pem.gpg
new file mode 100644
index 0000000..2d53007
Binary files /dev/null and b/tools/travis/tls/jonpspri/ppc64le/ca.pem.gpg differ
diff --git a/tools/travis/tls/jonpspri/ppc64le/cert.pem.gpg b/tools/travis/tls/jonpspri/ppc64le/cert.pem.gpg
new file mode 100644
index 0000000..1ff14aa
Binary files /dev/null and b/tools/travis/tls/jonpspri/ppc64le/cert.pem.gpg differ
diff --git a/tools/travis/tls/jonpspri/ppc64le/key.pem.gpg b/tools/travis/tls/jonpspri/ppc64le/key.pem.gpg
new file mode 100644
index 0000000..7b898a8
Binary files /dev/null and b/tools/travis/tls/jonpspri/ppc64le/key.pem.gpg differ
diff --git a/tools/travis/tls/jonpspri/s390x/ca.pem.gpg b/tools/travis/tls/jonpspri/s390x/ca.pem.gpg
new file mode 100644
index 0000000..69776e6
Binary files /dev/null and b/tools/travis/tls/jonpspri/s390x/ca.pem.gpg differ
diff --git a/tools/travis/tls/jonpspri/s390x/cert.pem.gpg b/tools/travis/tls/jonpspri/s390x/cert.pem.gpg
new file mode 100644
index 0000000..8910f66
Binary files /dev/null and b/tools/travis/tls/jonpspri/s390x/cert.pem.gpg differ
diff --git a/tools/travis/tls/jonpspri/s390x/key.pem.gpg b/tools/travis/tls/jonpspri/s390x/key.pem.gpg
new file mode 100644
index 0000000..c8e11f6
Binary files /dev/null and b/tools/travis/tls/jonpspri/s390x/key.pem.gpg differ


 

----------------------------------------------------------------
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