Skip to content

Commit 2533fd5

Browse files
authored
Examples: Add an example which uses a resource bundle (#82)
Motivation ---------- An example showing how to use resources (#78) is helpful for new users and can be used as an end to end tests for this feature. Modifications ------------- * Add a new `HelloWorldWithResources` example * Add the new example to the `End to end tests` GitHub Actions job * Restructure the end to end tests to check the HTTP response code instead of the content which is returned, because the new test returns one of 3 JPEGs selected at random, whereas the existing tests returned strings. * Adopt the code structure used by the [official Hummingbird examples](https://github.com/hummingbird-project/hummingbird-examples), to make it more familiar to users who are used to the official examples. Result ------ * The repository contains a new example showing how to use resources in a container image * The resource feature is tested as part of the automated end to end test suite. Test Plan --------- The new test passes and all existing tests continue to pass.
1 parent 2886ef2 commit 2533fd5

File tree

8 files changed

+135
-1
lines changed

8 files changed

+135
-1
lines changed

.github/workflows/endtoend_tests.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
example:
2222
- Examples/HelloWorldVapor
2323
- Examples/HelloWorldHummingbird
24+
- Examples/HelloWorldWithResources
2425
steps:
2526
- name: Checkout repository
2627
uses: actions/checkout@v4
@@ -55,4 +56,14 @@ jobs:
5556
5657
- name: Check that the service is running
5758
run: |
58-
curl -v localhost:8080 | grep "Hello World"
59+
# The curious combination of --verbose and --silent causes
60+
# curl to print the request and response (--verbose) but not
61+
# the transmission progress messages (--silent).
62+
#
63+
# --fail-with-body causes curl to exit with a nonzero exit code
64+
# if the HTTP response code is >= 400. Without this flag, curl
65+
# only returns a nonzero exit code if something went wrong while
66+
# connecting to the server - a successful HTTP transaction which
67+
# indicates a server error is still considered to be a successful
68+
# transaction from the client's point of view.
69+
curl --verbose --silent --output /dev/null --fail-with-body localhost:8080

.licenseignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
**/*.docc/*
2+
**/*.jpg
23
**/*.md
34
**/.gitignore
45
**/Package.resolved
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// swift-tools-version: 6.0
2+
3+
//===----------------------------------------------------------------------===//
4+
//
5+
// This source file is part of the SwiftContainerPlugin open source project
6+
//
7+
// Copyright (c) 2025 Apple Inc. and the SwiftContainerPlugin project authors
8+
// Licensed under Apache License v2.0
9+
//
10+
// See LICENSE.txt for license information
11+
// See CONTRIBUTORS.txt for the list of SwiftContainerPlugin project authors
12+
//
13+
// SPDX-License-Identifier: Apache-2.0
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
import PackageDescription
18+
19+
let package = Package(
20+
name: "hello-world",
21+
platforms: [.macOS(.v14)],
22+
dependencies: [
23+
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.1.0"),
24+
.package(url: "https://github.com/apple/swift-container-plugin", from: "0.5.0"),
25+
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
26+
],
27+
targets: [
28+
.executableTarget(
29+
name: "hello-world",
30+
dependencies: [
31+
.product(name: "Hummingbird", package: "hummingbird"),
32+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
33+
],
34+
resources: [.process("resources")]
35+
)
36+
]
37+
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftContainerPlugin open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the SwiftContainerPlugin project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftContainerPlugin project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import ArgumentParser
16+
17+
@main
18+
struct Hello: AsyncParsableCommand {
19+
@Option(name: .shortAndLong)
20+
var hostname: String = "0.0.0.0"
21+
22+
@Option(name: .shortAndLong)
23+
var port: Int = 8080
24+
25+
func run() async throws {
26+
let app = buildApplication(
27+
configuration: .init(
28+
address: .hostname(hostname, port: port),
29+
serverName: "Hummingbird"
30+
)
31+
)
32+
try await app.runService()
33+
}
34+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftContainerPlugin open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the SwiftContainerPlugin project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftContainerPlugin project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
import Hummingbird
17+
import Logging
18+
19+
let myos = ProcessInfo.processInfo.operatingSystemVersionString
20+
21+
func buildApplication(configuration: ApplicationConfiguration) -> some ApplicationProtocol {
22+
let router = Router()
23+
router.addMiddleware { LogRequestsMiddleware(.info) }
24+
router.get("/") { _, _ in
25+
let faces = [
26+
"happy-cat-face",
27+
"slightly-smiling-face",
28+
"smiling-face-with-sunglasses",
29+
]
30+
31+
guard let resourceURL = Bundle.module.url(forResource: faces.randomElement(), withExtension: "jpg") else {
32+
throw HTTPError(.internalServerError)
33+
}
34+
35+
let image = try Data(contentsOf: resourceURL)
36+
37+
return Response(
38+
status: .ok,
39+
headers: [.contentType: "image/jpg"],
40+
body: .init(byteBuffer: ByteBuffer(bytes: image))
41+
)
42+
}
43+
44+
let app = Application(
45+
router: router,
46+
configuration: configuration,
47+
logger: Logger(label: "hello-with-resources")
48+
)
49+
50+
return app
51+
}
Loading
Loading
Loading

0 commit comments

Comments
 (0)