You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2020/10/30 20:35:05 UTC

[openwhisk-runtime-swift] branch master updated: add support for Swift 5.3 (#124)

This is an automated email from the ASF dual-hosted git repository.

rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-swift.git


The following commit(s) were added to refs/heads/master by this push:
     new 5025af1  add support for Swift 5.3 (#124)
5025af1 is described below

commit 5025af15b89139e5c0a12b47897e1db6a9406117
Author: David Grove <dg...@users.noreply.github.com>
AuthorDate: Fri Oct 30 16:34:55 2020 -0400

    add support for Swift 5.3 (#124)
    
    1. add support for Swift 5.3 by replicating Swift 5.1
    2. Cleanup vestigial Swift 3 testing artifacts
---
 .travis.yml                                        |   2 +-
 ansible/environments/local/group_vars/all          |   5 +
 .../build.sh => core/swift53Action/CHANGELOG.md    |  22 +--
 core/swift53Action/Dockerfile                      |  62 ++++++++
 .../swift53Action}/Package.swift                   |  19 ++-
 core/swift53Action/_Whisk.swift                    | 167 +++++++++++++++++++++
 .../swift53Action/build.gradle                     |   9 +-
 .../Sources => core/swift53Action}/main.swift      |   0
 core/swift53Action/swiftbuild.py                   | 119 +++++++++++++++
 core/swift53Action/swiftbuild.py.launcher.swift    | 133 ++++++++++++++++
 .../swift53Action/swiftbuildandlink.sh             |  21 +--
 settings.gradle                                    |   2 +
 tests/dat/actions/sdk/swift3/createRule.swift      |  30 ----
 tests/dat/actions/sdk/swift3/invoke.swift          |  33 ----
 .../dat/actions/sdk/swift3/invokeNonBlocking.swift |  33 ----
 tests/dat/build.sh                                 |   7 +-
 tests/dat/build/swift5.3/HelloSwift5.zip           | Bin 0 -> 23934 bytes
 tests/dat/build/swift5.3/HelloSwift5Codable.zip    | Bin 0 -> 36717 bytes
 tests/dat/build/swift5.3/SwiftyRequest5.zip        | Bin 0 -> 6607555 bytes
 tests/dat/build/swift5.3/SwiftyRequestCodable5.zip | Bin 0 -> 6617585 bytes
 .../Swift53ActionContainerTests.scala              | 121 +++++++++++++++
 .../Swift53CodableActionContainerTests.scala}      |  16 +-
 .../test/scala/runtime/sdk/Swift53SDKTests.scala}  |  17 +--
 tools/travis/build.sh                              |   1 +
 tools/travis/publish.sh                            |   2 +
 25 files changed, 657 insertions(+), 164 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 4f46b6b..b63fb38 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,7 +47,7 @@ deploy:
       branch: master
       repo: apache/openwhisk-runtime-swift
   - provider: script
-    script: "./tools/travis/publish.sh openwhisk 5.1 nightly"
+    script: "./tools/travis/publish.sh openwhisk 5.1 nightly && ./tools/travis/publish.sh openwhisk 5.3 nightly"
     on:
       branch: master
       repo: apache/openwhisk-runtime-swift
diff --git a/ansible/environments/local/group_vars/all b/ansible/environments/local/group_vars/all
index e071694..fa613d9 100644
--- a/ansible/environments/local/group_vars/all
+++ b/ansible/environments/local/group_vars/all
@@ -69,6 +69,11 @@ runtimes_manifest:
       image:
         name: "action-swift-v5.1"
       deprecated: false
+    - kind: "swift:5.3"
+      default: false
+      image:
+        name: "action-swift-v5.3"
+      deprecated: false
 
   blackboxes:
     - name: "dockerskeleton"
diff --git a/tests/dat/build.sh b/core/swift53Action/CHANGELOG.md
old mode 100755
new mode 100644
similarity index 54%
copy from tests/dat/build.sh
copy to core/swift53Action/CHANGELOG.md
index f016d00..932bd52
--- a/tests/dat/build.sh
+++ b/core/swift53Action/CHANGELOG.md
@@ -1,4 +1,4 @@
-#!/bin/bash
+<!--
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -15,21 +15,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+-->
 
-set -e
+# Apache OpenWhisk Swift 5.3 Runtime Container
 
-../../tools/build/compile.sh  HelloSwift3 swift:3.1.1 "-v"
-
-../../tools/build/compile.sh  HelloSwift4 swift:4.1 "-v"
-../../tools/build/compile.sh  SwiftyRequest swift:4.1 "-v"
-../../tools/build/compile.sh  SwiftyRequestCodable swift:4.1 "-v"
-../../tools/build/compile.sh  HelloSwift4Codable swift:4.1 "-v"
-
-../../tools/build/compile5.sh  action-swift-v5.1 HelloSwift5 swift5.1 "-v"
-../../tools/build/compile5.sh  action-swift-v5.1 HelloSwift5Codable swift5.1 "-v"
-../../tools/build/compile5.sh  action-swift-v5.1 SwiftyRequest5 swift:5.1 "-v"
-../../tools/build/compile5.sh  action-swift-v5.1 SwiftyRequestCodable5 swift:5.1 "-v"
-
-
-cd actions
-make all
+## NEXT RELEASE
+ - Initial Release
diff --git a/core/swift53Action/Dockerfile b/core/swift53Action/Dockerfile
new file mode 100644
index 0000000..b7437d5
--- /dev/null
+++ b/core/swift53Action/Dockerfile
@@ -0,0 +1,62 @@
+#
+# 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.
+#
+
+# build go proxy from source
+FROM golang:1.15 AS builder_source
+RUN env CGO_ENABLED=0 go get github.com/apache/openwhisk-runtime-go/main && mv /go/bin/main /bin/proxy
+
+# or build it from a release
+FROM golang:1.15 AS builder_release
+ARG GO_PROXY_RELEASE_VERSION=1.15@1.16.0
+RUN curl -sL \
+  https://github.com/apache/openwhisk-runtime-go/archive/{$GO_PROXY_RELEASE_VERSION}.tar.gz\
+  | tar xzf -\
+  && cd openwhisk-runtime-go-*/main\
+  && GO111MODULE=on go build -o /bin/proxy
+
+FROM swift:5.3
+
+# select the builder to use
+ARG GO_PROXY_BUILD_FROM=release
+
+RUN rm -rf /var/lib/apt/lists/* && apt-get clean && apt-get -qq update \
+	&& apt-get install -y --no-install-recommends locales python3 vim libssl-dev libicu-dev \
+	&& rm -rf /var/lib/apt/lists/* \
+	&& locale-gen en_US.UTF-8
+
+ENV LANG="en_US.UTF-8" \
+	LANGUAGE="en_US:en" \
+	LC_ALL="en_US.UTF-8"
+
+RUN mkdir -p /swiftAction
+WORKDIR /swiftAction
+
+COPY --from=builder_source /bin/proxy /bin/proxy_source
+COPY --from=builder_release /bin/proxy /bin/proxy_release
+RUN mv /bin/proxy_${GO_PROXY_BUILD_FROM} /bin/proxy
+ADD swiftbuild.py /bin/compile
+ADD swiftbuild.py.launcher.swift /bin/compile.launcher.swift
+COPY _Whisk.swift /swiftAction/Sources/
+COPY Package.swift /swiftAction/
+COPY swiftbuildandlink.sh /swiftAction/
+COPY main.swift /swiftAction/Sources/
+RUN swift build -c release; \
+	touch /swiftAction/Sources/main.swift; \
+	rm /swiftAction/.build/release/Action
+
+ENV OW_COMPILER=/bin/compile
+ENTRYPOINT [ "/bin/proxy" ]
diff --git a/tests/dat/actions/HelloSwift3/Package.swift b/core/swift53Action/Package.swift
similarity index 75%
rename from tests/dat/actions/HelloSwift3/Package.swift
rename to core/swift53Action/Package.swift
index e2437ec..181e352 100644
--- a/tests/dat/actions/HelloSwift3/Package.swift
+++ b/core/swift53Action/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:3.1.0
+// swift-tools-version:4.2
 // The swift-tools-version declares the minimum version of Swift required to build this package.
 
 /*
@@ -22,9 +22,16 @@ import PackageDescription
 
 let package = Package(
     name: "Action",
-        dependencies: [
-            .Package(url: "https://github.com/IBM-Swift/CCurl.git", "0.2.3"),
-            .Package(url: "https://github.com/IBM-Swift/Kitura-net.git", "1.7.10"),
-            .Package(url: "https://github.com/IBM-Swift/SwiftyJSON.git", "15.0.1")
-        ]
+    products: [
+      .executable(
+        name: "Action",
+        targets:  ["Action"]
+      )
+    ],
+    targets: [
+      .target(
+        name: "Action",
+        path: "."
+      )
+    ]
 )
diff --git a/core/swift53Action/_Whisk.swift b/core/swift53Action/_Whisk.swift
new file mode 100644
index 0000000..3f18e9d
--- /dev/null
+++ b/core/swift53Action/_Whisk.swift
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+import Foundation
+import Dispatch
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
+class Whisk {
+
+    static var baseUrl = ProcessInfo.processInfo.environment["__OW_API_HOST"]
+    static var apiKey = ProcessInfo.processInfo.environment["__OW_API_KEY"]
+    // This will allow user to modify the default JSONDecoder and JSONEncoder used by epilogue
+    static var jsonDecoder = JSONDecoder()
+    static var jsonEncoder = JSONEncoder()
+
+    class func invoke(actionNamed action : String, withParameters params : [String:Any], blocking: Bool = true) -> [String:Any] {
+        let parsedAction = parseQualifiedName(name: action)
+        let strBlocking = blocking ? "true" : "false"
+        let path = "/api/v1/namespaces/\(parsedAction.namespace)/actions/\(parsedAction.name)?blocking=\(strBlocking)"
+
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "POST")
+    }
+
+    class func trigger(eventNamed event : String, withParameters params : [String:Any]) -> [String:Any] {
+        let parsedEvent = parseQualifiedName(name: event)
+        let path = "/api/v1/namespaces/\(parsedEvent.namespace)/triggers/\(parsedEvent.name)?blocking=true"
+
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "POST")
+    }
+
+    class func createTrigger(triggerNamed trigger: String, withParameters params : [String:Any]) -> [String:Any] {
+        let parsedTrigger = parseQualifiedName(name: trigger)
+        let path = "/api/v1/namespaces/\(parsedTrigger.namespace)/triggers/\(parsedTrigger.name)"
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "PUT")
+    }
+
+    class func createRule(ruleNamed ruleName: String, withTrigger triggerName: String, andAction actionName: String) -> [String:Any] {
+        let parsedRule = parseQualifiedName(name: ruleName)
+        let path = "/api/v1/namespaces/\(parsedRule.namespace)/rules/\(parsedRule.name)"
+        let params = ["trigger":triggerName, "action":actionName]
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "PUT")
+    }
+
+    // handle the GCD dance to make the post async, but then obtain/return
+    // the result from this function sync
+    private class func sendWhiskRequestSyncronish(uriPath path: String, params : [String:Any], method: String) -> [String:Any] {
+        var response : [String:Any]!
+
+        let queue = DispatchQueue.global()
+        let invokeGroup = DispatchGroup()
+
+        invokeGroup.enter()
+        queue.async {
+            postUrlSession(uriPath: path, params: params, method: method, group: invokeGroup) { result in
+                response = result
+            }
+        }
+
+        // On one hand, FOREVER seems like an awfully long time...
+        // But on the other hand, I think we can rely on the system to kill this
+        // if it exceeds a reasonable execution time.
+        switch invokeGroup.wait(timeout: DispatchTime.distantFuture) {
+        case DispatchTimeoutResult.success:
+            break
+        case DispatchTimeoutResult.timedOut:
+            break
+        }
+
+        return response
+    }
+
+
+    /**
+     * Using new UrlSession
+     */
+    private class func postUrlSession(uriPath: String, params : [String:Any], method: String,group: DispatchGroup, callback : @escaping([String:Any]) -> Void) {
+
+        guard let encodedPath = uriPath.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else {
+            callback(["error": "Error encoding uri path to make openwhisk REST call."])
+            return
+        }
+
+        let urlStr = "\(baseUrl!)\(encodedPath)"
+        if let url = URL(string: urlStr) {
+            var request = URLRequest(url: url)
+            request.httpMethod = method
+
+            do {
+                request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+                request.httpBody = try JSONSerialization.data(withJSONObject: params)
+
+                let loginData: Data = apiKey!.data(using: String.Encoding.utf8, allowLossyConversion: false)!
+                let base64EncodedAuthKey  = loginData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
+                request.addValue("Basic \(base64EncodedAuthKey)", forHTTPHeaderField: "Authorization")
+                let session = URLSession(configuration: URLSessionConfiguration.default)
+
+                let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
+
+                    // exit group after we are done
+                    defer {
+                        group.leave()
+                    }
+
+                    if let error = error {
+                        callback(["error":error.localizedDescription])
+                    } else {
+
+                        if let data = data {
+                            do {
+                                //let outputStr  = String(data: data, encoding: String.Encoding.utf8) as String!
+                                //print(outputStr)
+                                let respJson = try JSONSerialization.jsonObject(with: data)
+                                if respJson is [String:Any] {
+                                    callback(respJson as! [String:Any])
+                                } else {
+                                    callback(["error":" response from server is not a dictionary"])
+                                }
+                            } catch {
+                                callback(["error":"Error creating json from response: \(error)"])
+                            }
+                        }
+                    }
+                })
+
+                task.resume()
+            } catch {
+                callback(["error":"Got error creating params body: \(error)"])
+            }
+        }
+    }
+
+    // separate an OpenWhisk qualified name (e.g. "/whisk.system/samples/date")
+    // into namespace and name components
+    private class func parseQualifiedName(name qualifiedName : String) -> (namespace : String, name : String) {
+        let defaultNamespace = "_"
+        let delimiter = "/"
+
+        let segments :[String] = qualifiedName.components(separatedBy: delimiter)
+
+        if segments.count > 2 {
+            return (segments[1], Array(segments[2..<segments.count]).joined(separator: delimiter))
+        } else if segments.count == 2 {
+            // case "/action" or "package/action"
+            let name = qualifiedName.hasPrefix(delimiter) ? segments[1] : segments.joined(separator: delimiter)
+            return (defaultNamespace, name)
+        } else {
+            return (defaultNamespace, segments[0])
+        }
+    }
+
+}
diff --git a/tests/dat/actions/sdk/swift3/createTrigger.swift b/core/swift53Action/build.gradle
similarity index 72%
rename from tests/dat/actions/sdk/swift3/createTrigger.swift
rename to core/swift53Action/build.gradle
index 5d31971..5a8c378 100644
--- a/tests/dat/actions/sdk/swift3/createTrigger.swift
+++ b/core/swift53Action/build.gradle
@@ -15,10 +15,5 @@
  * limitations under the License.
  */
 
-func main(args: [String:Any]) -> [String:Any] {
- guard let triggerName = args["triggerName"] as? String else {
-    return ["error": "You must specify a triggerName parameter!"]
-  }
-  print("Trigger Name: \(triggerName)")
-  return Whisk.createTrigger(triggerNamed: triggerName, withParameters: [:])
-}
+ext.dockerImageName = 'action-swift-v5.3'
+apply from: '../../gradle/docker.gradle'
diff --git a/tests/dat/actions/HelloSwift3/Sources/main.swift b/core/swift53Action/main.swift
similarity index 100%
rename from tests/dat/actions/HelloSwift3/Sources/main.swift
rename to core/swift53Action/main.swift
diff --git a/core/swift53Action/swiftbuild.py b/core/swift53Action/swiftbuild.py
new file mode 100755
index 0000000..9b40903
--- /dev/null
+++ b/core/swift53Action/swiftbuild.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+"""Swift Action Compiler
+#
+# 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.
+#
+"""
+
+from __future__ import print_function
+import os
+import re
+import sys
+import codecs
+import subprocess
+from io import StringIO
+
+def eprint(*args, **kwargs):
+    print(*args, file=sys.stderr, **kwargs)
+
+def sources(launcher, source_dir, main):
+    actiondir = "%s/Sources" % source_dir
+    # copy the launcher fixing the main
+    dst = "%s/main.swift" % actiondir
+    with codecs.open(dst, 'a', 'utf-8') as d:
+        with codecs.open(launcher, 'r', 'utf-8') as e:
+            code = e.read()
+            code += "while let inputStr: String = readLine() {\n"
+            code += "  let json = inputStr.data(using: .utf8, allowLossyConversion: true)!\n"
+            code += "  let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any]\n"
+            code += "  for (key, value) in parsed {\n"
+            code += "    if key != \"value\" {\n"
+            code += "      setenv(\"__OW_\\(key.uppercased())\",value as! String,1)\n"
+            code += "    }\n"
+            code += "  }\n"
+            code += "  let jsonData = try JSONSerialization.data(withJSONObject: parsed[\"value\"] as Any, options: [])\n"
+            code += "  _run_main(mainFunction: %s, json: jsonData)\n" % main
+            code += "} \n"
+            d.write(code)
+
+def swift_build(dir, buildcmd):
+    # compile...
+    env = {
+      "PATH": os.environ["PATH"]
+    }
+    p = subprocess.Popen(buildcmd,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        cwd=dir,
+        env=env)
+    (o, e) = p.communicate()
+    # stdout/stderr may be either text or bytes, depending on Python
+    # version, so if bytes, decode to text. Note that in Python 2
+    # a string will match both types; so also skip decoding in that case
+    if isinstance(o, bytes) and not isinstance(o, str):
+        o = o.decode('utf-8')
+    if isinstance(e, bytes) and not isinstance(e, str):
+        e = e.decode('utf-8')
+    return p.returncode, o, e
+
+def build(source_dir, target_file, buildcmd):
+    r, o, e = swift_build(source_dir, buildcmd)
+    #if e: print(e)
+    #if o: print(o)
+    if r != 0:
+        print(e)
+        print(o)
+        print(r)
+        return
+
+    bin_file = "%s/.build/release/Action" % source_dir
+    os.rename(bin_file, target_file)
+    if not os.path.isfile(target_file):
+        print("failed %s -> %s" % (bin_file, target_file))
+        return
+
+
+def main(argv):
+    if len(argv) < 4:
+        print("usage: <main-function> <source-dir> <target-dir>")
+        sys.exit(1)
+
+    main = argv[1]
+    source_dir = os.path.abspath(argv[2])
+    target = os.path.abspath("%s/exec" % argv[3])
+    launch = os.path.abspath(argv[0]+".launcher.swift")
+
+    src = "%s/exec" % source_dir
+
+    #check if single source
+    if os.path.isfile(src):
+        actiondir = os.path.abspath("Sources")
+        if not os.path.isdir(actiondir):
+            os.makedirs(actiondir, mode=0o755)
+        dst = "%s/main.swift" % actiondir
+        os.rename(src, dst)
+        sources(launch, os.path.abspath("."), main)
+        build(os.path.abspath("."), target, ["./swiftbuildandlink.sh"])
+    else:
+        actiondir = "%s/Sources" % source_dir
+        if not os.path.isdir(actiondir):
+            os.makedirs(actiondir, mode=0o755)
+        os.rename(os.path.abspath("Sources/_Whisk.swift"),"%s/Sources/_Whisk.swift" % source_dir)
+        sources(launch, source_dir, main)
+        build(source_dir, target, ["swift", "build", "-c", "release"])
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/core/swift53Action/swiftbuild.py.launcher.swift b/core/swift53Action/swiftbuild.py.launcher.swift
new file mode 100644
index 0000000..a1a2cf0
--- /dev/null
+++ b/core/swift53Action/swiftbuild.py.launcher.swift
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+// Imports
+import Foundation
+#if os(Linux)
+    import Glibc
+#else
+    import Darwin
+#endif
+
+func _whisk_print_error(message: String, error: Error?){
+    var errStr =  "{\"error\":\"\(message)\"}\n"
+    if let error = error {
+        errStr = "{\"error\":\"\(message) \(error.localizedDescription)\"\n}"
+    }
+    _whisk_print_buffer(jsonString: errStr)
+}
+func _whisk_print_result(jsonData: Data){
+    let jsonString = String(data: jsonData, encoding: .utf8)!
+    _whisk_print_buffer(jsonString: jsonString)
+}
+func _whisk_print_buffer(jsonString: String){
+    var buf : [UInt8] = Array(jsonString.utf8)
+    buf.append(10)
+    fflush(stdout)
+    fflush(stderr)
+    write(3, buf, buf.count)
+}
+
+// snippet of code "injected" (wrapper code for invoking traditional main)
+func _run_main(mainFunction: ([String: Any]) -> [String: Any], json: Data) -> Void {
+    do {
+        let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any]
+        let result = mainFunction(parsed)
+        if JSONSerialization.isValidJSONObject(result) {
+            do {
+                let jsonData = try JSONSerialization.data(withJSONObject: result, options: [])
+                 _whisk_print_result(jsonData: jsonData)
+            } catch {
+                _whisk_print_error(message: "Failed to encode Dictionary type to JSON string:", error: error)
+            }
+        } else {
+            _whisk_print_error(message: "Error serializing JSON, data does not appear to be valid JSON", error: nil)
+        }
+    } catch {
+        _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+        return
+    }
+}
+
+// Codable main signature input Codable
+func _run_main<In: Decodable, Out: Encodable>(mainFunction: (In, @escaping (Out?, Error?) -> Void) -> Void, json: Data) {
+    do {
+        let input = try Whisk.jsonDecoder.decode(In.self, from: json)
+        let resultHandler = { (out: Out?, error: Error?) in
+            if let error = error {
+                _whisk_print_error(message: "Action handler callback returned an error:", error: error)
+                return
+            }
+            guard let out = out else {
+                _whisk_print_error(message: "Action handler callback did not return response or error.", error: nil)
+                return
+            }
+            do {
+                let jsonData = try Whisk.jsonEncoder.encode(out)
+                _whisk_print_result(jsonData: jsonData)
+            } catch let error as EncodingError {
+                _whisk_print_error(message: "JSONEncoder failed to encode Codable type to JSON string:", error: error)
+                return
+            } catch {
+                _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+                return
+            }
+        }
+        let _ = mainFunction(input, resultHandler)
+    } catch let error as DecodingError {
+        _whisk_print_error(message: "JSONDecoder failed to decode JSON string \(String(data: json, encoding: .utf8)!.replacingOccurrences(of: "\"", with: "\\\"")) to Codable type:", error: error)
+        return
+    } catch {
+        _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+        return
+    }
+}
+
+// Codable main signature no input
+func _run_main<Out: Encodable>(mainFunction: ( @escaping (Out?, Error?) -> Void) -> Void, json: Data) {
+    let resultHandler = { (out: Out?, error: Error?) in
+        if let error = error {
+            _whisk_print_error(message: "Action handler callback returned an error:", error: error)
+            return
+        }
+        guard let out = out else {
+            _whisk_print_error(message: "Action handler callback did not return response or error.", error: nil)
+            return
+        }
+        do {
+            let jsonData = try Whisk.jsonEncoder.encode(out)
+            _whisk_print_result(jsonData: jsonData)
+        } catch let error as EncodingError {
+            _whisk_print_error(message: "JSONEncoder failed to encode Codable type to JSON string:", error: error)
+            return
+        } catch {
+            _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+            return
+        }
+    }
+    let _ = mainFunction(resultHandler)
+}
+
+// snippets of code "injected", dependending on the type of function the developer
+// wants to use traditional vs codable
+
+
+
+
+
+
+
diff --git a/tests/dat/build.sh b/core/swift53Action/swiftbuildandlink.sh
similarity index 53%
copy from tests/dat/build.sh
copy to core/swift53Action/swiftbuildandlink.sh
index f016d00..c35a1ba 100755
--- a/tests/dat/build.sh
+++ b/core/swift53Action/swiftbuildandlink.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -15,21 +15,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-set -e
-
-../../tools/build/compile.sh  HelloSwift3 swift:3.1.1 "-v"
-
-../../tools/build/compile.sh  HelloSwift4 swift:4.1 "-v"
-../../tools/build/compile.sh  SwiftyRequest swift:4.1 "-v"
-../../tools/build/compile.sh  SwiftyRequestCodable swift:4.1 "-v"
-../../tools/build/compile.sh  HelloSwift4Codable swift:4.1 "-v"
-
-../../tools/build/compile5.sh  action-swift-v5.1 HelloSwift5 swift5.1 "-v"
-../../tools/build/compile5.sh  action-swift-v5.1 HelloSwift5Codable swift5.1 "-v"
-../../tools/build/compile5.sh  action-swift-v5.1 SwiftyRequest5 swift:5.1 "-v"
-../../tools/build/compile5.sh  action-swift-v5.1 SwiftyRequestCodable5 swift:5.1 "-v"
-
-
-cd actions
-make all
+swift build -c release
diff --git a/settings.gradle b/settings.gradle
index c3edc42..78a9f66 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,6 +23,8 @@ include 'core:swift42Action'
 
 include 'core:swift51Action'
 
+include 'core:swift53Action'
+
 rootProject.name = 'runtime-swift'
 
 gradle.ext.openwhisk = [
diff --git a/tests/dat/actions/sdk/swift3/createRule.swift b/tests/dat/actions/sdk/swift3/createRule.swift
deleted file mode 100644
index a6506d5..0000000
--- a/tests/dat/actions/sdk/swift3/createRule.swift
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-func main(args: [String:Any]) -> [String:Any] {
-  guard let triggerName = args["triggerName"] as? String else {
-      return ["error": "You must specify a triggerName parameter!"]
-  }
-  guard let actionName = args["actionName"] as? String else {
-      return ["error": "You must specify a actionName parameter!"]
-  }
-  guard let ruleName = args["ruleName"] as? String else {
-      return ["error": "You must specify a ruleName parameter!"]
-  }
-  print("Rule Name: \(ruleName), Trigger Name: \(triggerName), actionName: \(actionName)")
-  return Whisk.createRule(ruleNamed: ruleName, withTrigger: triggerName, andAction: actionName)
-}
diff --git a/tests/dat/actions/sdk/swift3/invoke.swift b/tests/dat/actions/sdk/swift3/invoke.swift
deleted file mode 100644
index 411157e..0000000
--- a/tests/dat/actions/sdk/swift3/invoke.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-import SwiftyJSON
-
-func main(args: [String:Any]) -> [String:Any] {
-  let invokeResult = Whisk.invoke(actionNamed: "/whisk.system/utils/date", withParameters: [:])
-  let dateActivation = JSON(invokeResult)
-
-  // the date we are looking for is the result inside the date activation
-  if let dateString = dateActivation["response"]["result"]["date"].string {
-    print("It is now \(dateString)")
-  } else {
-    print("Could not parse date of of the response.")
-  }
-
-  // return the entire invokeResult
-  return invokeResult
-}
diff --git a/tests/dat/actions/sdk/swift3/invokeNonBlocking.swift b/tests/dat/actions/sdk/swift3/invokeNonBlocking.swift
deleted file mode 100644
index 9e12d9f..0000000
--- a/tests/dat/actions/sdk/swift3/invokeNonBlocking.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-import SwiftyJSON
-
-func main(args: [String:Any]) -> [String:Any] {
-  let invokeResult = Whisk.invoke(actionNamed: "/whisk.system/utils/date", withParameters: [:], blocking: false)
-  let dateActivation = JSON(invokeResult)
-
-  // the date we are looking for is the result inside the date activation
-  if let activationId = dateActivation["activationId"].string {
-    print("Invoked.")
-  } else {
-    print("Failed to invoke.")
-  }
-
-  // return the entire invokeResult
-  return invokeResult
-}
diff --git a/tests/dat/build.sh b/tests/dat/build.sh
index f016d00..7dd9180 100755
--- a/tests/dat/build.sh
+++ b/tests/dat/build.sh
@@ -18,8 +18,6 @@
 
 set -e
 
-../../tools/build/compile.sh  HelloSwift3 swift:3.1.1 "-v"
-
 ../../tools/build/compile.sh  HelloSwift4 swift:4.1 "-v"
 ../../tools/build/compile.sh  SwiftyRequest swift:4.1 "-v"
 ../../tools/build/compile.sh  SwiftyRequestCodable swift:4.1 "-v"
@@ -30,6 +28,11 @@ set -e
 ../../tools/build/compile5.sh  action-swift-v5.1 SwiftyRequest5 swift:5.1 "-v"
 ../../tools/build/compile5.sh  action-swift-v5.1 SwiftyRequestCodable5 swift:5.1 "-v"
 
+../../tools/build/compile5.sh  action-swift-v5.3 HelloSwift5 swift5.3 "-v"
+../../tools/build/compile5.sh  action-swift-v5.3 HelloSwift5Codable swift5.3 "-v"
+../../tools/build/compile5.sh  action-swift-v5.3 SwiftyRequest5 swift:5.3 "-v"
+../../tools/build/compile5.sh  action-swift-v5.3 SwiftyRequestCodable5 swift:5.3 "-v"
+
 
 cd actions
 make all
diff --git a/tests/dat/build/swift5.3/HelloSwift5.zip b/tests/dat/build/swift5.3/HelloSwift5.zip
new file mode 100644
index 0000000..a16e1be
Binary files /dev/null and b/tests/dat/build/swift5.3/HelloSwift5.zip differ
diff --git a/tests/dat/build/swift5.3/HelloSwift5Codable.zip b/tests/dat/build/swift5.3/HelloSwift5Codable.zip
new file mode 100644
index 0000000..2e462ec
Binary files /dev/null and b/tests/dat/build/swift5.3/HelloSwift5Codable.zip differ
diff --git a/tests/dat/build/swift5.3/SwiftyRequest5.zip b/tests/dat/build/swift5.3/SwiftyRequest5.zip
new file mode 100644
index 0000000..cc05056
Binary files /dev/null and b/tests/dat/build/swift5.3/SwiftyRequest5.zip differ
diff --git a/tests/dat/build/swift5.3/SwiftyRequestCodable5.zip b/tests/dat/build/swift5.3/SwiftyRequestCodable5.zip
new file mode 100644
index 0000000..e3c01a9
Binary files /dev/null and b/tests/dat/build/swift5.3/SwiftyRequestCodable5.zip differ
diff --git a/tests/src/test/scala/runtime/actionContainers/Swift53ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift53ActionContainerTests.scala
new file mode 100644
index 0000000..0814f9a
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/Swift53ActionContainerTests.scala
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package runtime.actionContainers
+
+import java.io.File
+
+import actionContainers.ResourceHelpers
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import spray.json.{JsObject, JsString}
+
+@RunWith(classOf[JUnitRunner])
+class Swift53ActionContainerTests extends SwiftActionContainerTests {
+  override lazy val swiftContainerImageName = "action-swift-v5.3"
+  override lazy val swiftBinaryName = "tests/dat/build/swift5.3/HelloSwift5.zip"
+  lazy val partyCompile = "tests/dat/build/swift5.3/SwiftyRequest5.zip"
+  lazy val partyCompileCodable = "tests/dat/build/swift5.3/SwiftyRequestCodable5.zip"
+
+  val httpCode = """
+                   | import Foundation
+                   | import Dispatch
+                   | #if canImport(FoundationNetworking)
+                   |  import FoundationNetworking
+                   | #endif
+                   | func main(args:[String: Any]) -> [String:Any] {
+                   |     var resp :[String:Any] = ["error":"getUrl failed"]
+                   |     guard let urlStr = args["getUrl"] as? String else {
+                   |         return ["error":"getUrl not found in action input"]
+                   |     }
+                   |     guard let url = URL(string: urlStr) else {
+                   |         return ["error":"invalid url string \(urlStr)"]
+                   |     }
+                   |     let request = URLRequest(url: url)
+                   |     let session = URLSession(configuration: .default)
+                   |     let semaphore = DispatchSemaphore(value: 0)
+                   |     let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
+                   |         print("done with http request")
+                   |         if let error = error {
+                   |             print("There was an error \(error)")
+                   |         } else if let data = data,
+                   |             let response = response as? HTTPURLResponse,
+                   |             response.statusCode == 200 {
+                   |             do {
+                   |                 let respJson = try JSONSerialization.jsonObject(with: data)
+                   |                 if respJson is [String:Any] {
+                   |                     resp = respJson as! [String:Any]
+                   |                 } else {
+                   |                     resp = ["error":"Response from server is not a dictionary"]
+                   |                 }
+                   |             } catch {
+                   |                 resp = ["error":"Error creating json from response: \(error)"]
+                   |             }
+                   |         }
+                   |         semaphore.signal()
+                   |     })
+                   |     task.resume()
+                   |     _ = semaphore.wait(timeout: .distantFuture)
+                   |     return resp
+                   | }
+                 """.stripMargin
+
+  it should "support ability to use 3rd party packages like SwiftyRequest" in {
+    val zip = new File(partyCompile).toPath
+    val code = ResourceHelpers.readAsBase64(zip)
+
+    val (out, err) = withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code))
+      initCode should be(200)
+
+      val args = JsObject("message" -> (JsString("serverless")))
+      val (runCode, runRes) = c.run(runPayload(args))
+
+      runCode should be(200)
+      val json = runRes.get.fields.get("json")
+      json shouldBe Some(args)
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+
+  it should "support ability to use escaping completion in Codable" in {
+    val zip = new File(partyCompileCodable).toPath
+    val code = ResourceHelpers.readAsBase64(zip)
+
+    val (out, err) = withActionContainer() { c =>
+      val (initCode, initRes) = c.init(initPayload(code, main = "mainCodable"))
+      initCode should be(200)
+
+      val (runCode, runRes) = c.run(runPayload(JsObject()))
+
+      runCode should be(200)
+      runRes.get.fields.get("greeting") shouldBe Some(JsString("success"))
+
+    }
+
+    checkStreams(out, err, {
+      case (o, e) =>
+        if (enforceEmptyOutputStream) o shouldBe empty
+        e shouldBe empty
+    })
+  }
+}
diff --git a/tests/dat/actions/sdk/swift3/trigger.swift b/tests/src/test/scala/runtime/actionContainers/Swift53CodableActionContainerTests.scala
similarity index 68%
rename from tests/dat/actions/sdk/swift3/trigger.swift
rename to tests/src/test/scala/runtime/actionContainers/Swift53CodableActionContainerTests.scala
index c3f7599..4158896 100644
--- a/tests/dat/actions/sdk/swift3/trigger.swift
+++ b/tests/src/test/scala/runtime/actionContainers/Swift53CodableActionContainerTests.scala
@@ -15,11 +15,13 @@
  * limitations under the License.
  */
 
-func main(args: [String:Any]) -> [String:Any] {
-  if let triggerName = args["triggerName"] as? String {
-    print("Trigger Name: \(triggerName)")
-    return Whisk.trigger(eventNamed: triggerName, withParameters: [:])
-  } else {
-    return ["error": "You must specify a triggerName parameter!"]
-  }
+package runtime.actionContainers
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Swift53CodableActionContainerTests extends SwiftCodableActionContainerTests {
+  override lazy val swiftContainerImageName = "action-swift-v5.3"
+  override lazy val swiftBinaryName = "tests/dat/build/swift5.3/HelloSwift5Codable.zip"
 }
diff --git a/tests/dat/actions/sdk/swift3/hello.swift b/tests/src/test/scala/runtime/sdk/Swift53SDKTests.scala
similarity index 75%
rename from tests/dat/actions/sdk/swift3/hello.swift
rename to tests/src/test/scala/runtime/sdk/Swift53SDKTests.scala
index 2f5d944..25226c9 100644
--- a/tests/dat/actions/sdk/swift3/hello.swift
+++ b/tests/src/test/scala/runtime/sdk/Swift53SDKTests.scala
@@ -15,13 +15,12 @@
  * limitations under the License.
  */
 
-/**
- * Hello world as a Swift Whisk action.
- */
-func main(args: [String:Any]) -> [String:Any] {
-    if let name = args["name"] as? String {
-        return [ "greeting" : "Hello \(name)!" ]
-    } else {
-        return [ "greeting" : "Hello stranger!" ]
-    }
+package runtime.sdk
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Swift53SDKTests extends SwiftSDKTests {
+  override lazy val actionKind = "swift:5.3"
 }
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index 67962d1..6b9fbbc 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -57,4 +57,5 @@ TERM=dumb ./gradlew \
 :core:swift41Action:distDocker \
 :core:swift42Action:distDocker \
 :core:swift51Action:distDocker \
+:core:swift53Action:distDocker \
 -PdockerImagePrefix=${IMAGE_PREFIX}
diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh
index 6bfce5a..9498c1e 100755
--- a/tools/travis/publish.sh
+++ b/tools/travis/publish.sh
@@ -36,6 +36,8 @@ elif [ ${RUNTIME_VERSION} == "4.2" ]; then
   RUNTIME="swift42Action"
 elif [ ${RUNTIME_VERSION} == "5.1" ]; then
   RUNTIME="swift51Action"
+elif [ ${RUNTIME_VERSION} == "5.3" ]; then
+  RUNTIME="swift53Action"
 fi
 
 if [[ ! -z ${DOCKER_USER} ]] && [[ ! -z ${DOCKER_PASSWORD} ]]; then