Skip to content

Commit

Permalink
Linux build (#53)
Browse files Browse the repository at this point in the history
* Update test.yml
* Remove import FoundationNetworking
* if !os(Linux)
* add linux-build.sh
* fix Linux tests
* Add HTTPRequest
* Add Linux build to release workflow
  • Loading branch information
Alexander-Ignition authored Feb 17, 2022
1 parent b590424 commit 3a93bf2
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 51 deletions.
17 changes: 17 additions & 0 deletions .github/scripts/linux-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

# Usage: .github/scripts/linux-build.sh

set -eo pipefail

BUILD_OPTIONS=(
--configuration release
--package-path Packages/CatbirdApp
--disable-sandbox
--static-swift-stdlib
)

SWIFT_BUILD="swift build ${BUILD_OPTIONS[*]}"
$SWIFT_BUILD
BIN_PATH=$($SWIFT_BUILD --show-bin-path)
cp "$BIN_PATH"/catbird ./catbird-linux
16 changes: 16 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,19 @@ jobs:
run: gh workflow run bump-formula.yml --repo RedMadRobot/homebrew-formulae --field formula=catbird --field version=${{ github.event.release.tag_name }}
env:
GITHUB_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }}
linux-build:
name: Build on Linux
runs-on: ubuntu-20.04
container:
image: swift:5.5.1-focal
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build catbird app
id: build
run: .github/scripts/linux-build.sh
shell: bash
- name: Upload GitHub Release Assets
run: gh release upload ${{ github.event.release.tag_name }} catbird-linux
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 changes: 17 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,20 @@ jobs:
uses: actions/checkout@v2
- name: Test catbird app
run: swift test --package-path Packages/CatbirdApp --disable-automatic-resolution
test-linux-build:
name: Build on Linux
runs-on: ubuntu-20.04
container:
image: swift:5.5.1-focal
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build catbird app
id: build
run: .github/scripts/linux-build.sh
shell: bash
- name: Upload binary
uses: actions/upload-artifact@v2
with:
name: catbird-linux
path: catbird-linux
40 changes: 35 additions & 5 deletions Packages/CatbirdAPI/Sources/CatbirdAPI/Catbird.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#if !os(Linux)
/*
On Linux, URLSession and URLRequest are not in Foundation, but in FoundationNetworking.
FoundationNetworking has transitive dependencies that prevent compiling a static binary.
Catbird uses only models from CatbirdAPI, so URLSession was removed from the Linux build.
*/
import Foundation

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

/// API Client to mock server.
public final class Catbird {

Expand Down Expand Up @@ -76,7 +78,7 @@ public final class Catbird {
case (_, let error?):
completion(error)
case (let http as HTTPURLResponse, _):
completion(CatbirdError(response: http, data: data))
completion(CatbirdError(statusCode: http.statusCode, data: data))
default:
completion(nil)
}
Expand All @@ -86,3 +88,31 @@ public final class Catbird {
}

}

extension URLSessionTask {
/// Wait until task completed.
fileprivate func wait() {
guard let timeout = currentRequest?.timeoutInterval else { return }
let limitDate = Date(timeInterval: timeout, since: Date())
while state == .running && RunLoop.current.run(mode: .default, before: limitDate) {
// wait
}
}
}

extension CatbirdAction {
func makeRequest(to url: URL, parallelId: String? = nil) throws -> URLRequest {
let request = try makeHTTPRequest(to: url, parallelId: parallelId)

var urlRequest = URLRequest(url: request.url)
urlRequest.httpMethod = request.httpMethod
for (key, value) in request.headers {
urlRequest.addValue(value, forHTTPHeaderField: key)
}
urlRequest.httpBody = request.httpBody
return urlRequest
}
}

#endif

36 changes: 20 additions & 16 deletions Packages/CatbirdAPI/Sources/CatbirdAPI/CatbirdAction.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import Foundation

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

/// Catbird API action.
public enum CatbirdAction: Equatable {
/// Add, or insert `ResponseMock` for `RequestPattern`.
Expand Down Expand Up @@ -46,29 +42,37 @@ extension CatbirdAction {
}
}

// MARK: - CatbirdAction + URLRequest
// MARK: - CatbirdAction + Request

extension CatbirdAction {
/// Header name for parallel ID.
public static let parallelIdHeaderField = "X-Catbird-Parallel-Id"

private static let encoder = JSONEncoder()

/// Create a new `URLRequest`.
///
/// - Parameter url: Catbird server base url.
/// - Returns: Request to mock server.
func makeRequest(to url: URL, parallelId: String? = nil) throws -> URLRequest {
var request = URLRequest(url: url.appendingPathComponent("catbird/api/mocks"))
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
struct HTTPRequest {
var httpMethod: String
var url: URL
var headers: [String: String]
var httpBody: Data?

func value(forHTTPHeaderField name: String) -> String? {
headers[name]
}
}

func makeHTTPRequest(to url: URL, parallelId: String? = nil) throws -> HTTPRequest {
var request = HTTPRequest(
httpMethod: "POST",
url: url.appendingPathComponent("catbird/api/mocks"),
headers: ["Content-Type": "application/json"],
httpBody: try CatbirdAction.encoder.encode(self))

if let parallelId = parallelId {
request.addValue(parallelId, forHTTPHeaderField: CatbirdAction.parallelIdHeaderField)
request.headers[CatbirdAction.parallelIdHeaderField] = parallelId
}
request.httpBody = try CatbirdAction.encoder.encode(self)
return request
}

}

// MARK: - Codable
Expand Down
16 changes: 8 additions & 8 deletions Packages/CatbirdAPI/Sources/CatbirdAPI/CatbirdError.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import Foundation

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

/// From `Vapor.ErrorMiddleware`.
private struct ErrorResponse: Codable {

Expand All @@ -19,23 +15,27 @@ public struct CatbirdError: LocalizedError, CustomNSError {
/// The domain of the error.
public static var errorDomain = "com.redmadrobot.catbird.APIErrorDomain"

/// HTTP ststus code.
/// HTTP status code.
public let errorCode: Int

/// A localized message describing the reason for the failure.
public let failureReason: String?

init?(response: HTTPURLResponse, data: Data?) {
guard !(200..<300).contains(response.statusCode) else { return nil }
self.errorCode = response.statusCode
init?(statusCode: Int, data: Data?) {
guard !(200..<300).contains(statusCode) else { return nil }
self.errorCode = statusCode
self.failureReason = data.flatMap { (body: Data) in
try? JSONDecoder().decode(ErrorResponse.self, from: body).reason
}
}

/// A localized message describing what error occurred.
public var errorDescription: String? {
#if !os(Linux)
return HTTPURLResponse.localizedString(forStatusCode: errorCode)
#else
return "Status code: \(errorCode)"
#endif
}

/// The user-info dictionary.
Expand Down
18 changes: 0 additions & 18 deletions Packages/CatbirdAPI/Sources/CatbirdAPI/URLSessionTask+Wait.swift

This file was deleted.

4 changes: 4 additions & 0 deletions Packages/CatbirdAPI/Tests/CatbirdAPITests/CatbirdTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#if !os(Linux)

@testable import CatbirdAPI
import XCTest

Expand Down Expand Up @@ -120,3 +122,5 @@ final class CatbirdTests: XCTestCase {
return HTTPURLResponse(url: url, statusCode: status, httpVersion: nil, headerFields: nil)!
}
}

#endif
4 changes: 4 additions & 0 deletions Packages/CatbirdAPI/Tests/CatbirdAPITests/Network.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#if !os(Linux)

import Foundation

final class Network: URLProtocol {
Expand Down Expand Up @@ -46,3 +48,5 @@ final class Network: URLProtocol {

override func stopLoading() {}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import XCTVapor
extension Application {

func perform(_ action: CatbirdAction, parallelId: String? = nil, file: StaticString = #file, line: UInt = #line) throws {
let request = try action.makeRequest(to: URL(string: "/")!, parallelId: parallelId)
let method = try XCTUnwrap(request.httpMethod.map { HTTPMethod(rawValue: $0) }, file: file, line: line)
let path = try XCTUnwrap(request.url?.path, file: file, line: line)
let request = try action.makeHTTPRequest(to: URL(string: "/")!, parallelId: parallelId)
let method = HTTPMethod(rawValue: request.httpMethod)
let path = request.url.path
var headers = HTTPHeaders()
request.allHTTPHeaderFields?.forEach { key, value in
request.headers.forEach { key, value in
headers.add(name: key, value: value)
}
let body = request.httpBody.map { (data: Data) -> ByteBuffer in
Expand Down

0 comments on commit 3a93bf2

Please sign in to comment.