Skip to content

Commit ef99389

Browse files
authored
feat: add help for cli plugin (apple#1068)
- Closes apple#387. - Override the swift-argument-parser `help` command with a version that prints the reformatted help that includes plugin information. --------- Signed-off-by: ChengHao Yang <17496418+tico88612@users.noreply.github.com>
1 parent 2d1d158 commit ef99389

4 files changed

Lines changed: 77 additions & 4 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ integration: init-block
193193
{ \
194194
exit_code=0; \
195195
CLITEST_LOG_ROOT=$(LOG_ROOT) && export CLITEST_LOG_ROOT ; \
196+
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIHelp || exit_code=1 ; \
196197
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIStatus || exit_code=1 ; \
197198
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIVersion || exit_code=1 ; \
198199
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLINetwork || exit_code=1 ; \

Sources/ContainerCommands/Application.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public struct Application: AsyncLoggableCommand {
4545
abstract: "A container platform for macOS",
4646
version: ReleaseVersion.singleLine(appName: "container CLI"),
4747
subcommands: [
48-
DefaultCommand.self
48+
DefaultCommand.self,
49+
HelpCommand.self,
4950
],
5051
groupedSubcommands: [
5152
CommandGroup(
@@ -108,16 +109,15 @@ public struct Application: AsyncLoggableCommand {
108109
let args = Array(fullArgs.dropFirst())
109110

110111
do {
111-
// container -> defaultHelpCommand
112112
var command = try Application.parseAsRoot(args)
113113
if var asyncCommand = command as? AsyncParsableCommand {
114114
try await asyncCommand.run()
115115
} else {
116116
try command.run()
117117
}
118118
} catch {
119-
// Regular ol `command` with no args will get caught by DefaultCommand. --help
120-
// on the root command will land here.
119+
// --help/-h on the root command (e.g. `container --help`) is intercepted
120+
// by ArgumentParser and lands here.
121121
let containsHelp = fullArgs.contains("-h") || fullArgs.contains("--help")
122122
if fullArgs.count <= 2 && containsHelp {
123123
let pluginLoader = try? await createPluginLoader()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2026 Apple Inc. and the container project authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//===----------------------------------------------------------------------===//
16+
17+
import ArgumentParser
18+
import ContainerAPIClient
19+
20+
struct HelpCommand: AsyncLoggableCommand {
21+
public static let configuration = CommandConfiguration(
22+
commandName: "help",
23+
shouldDisplay: false
24+
)
25+
26+
@OptionGroup(visibility: .hidden)
27+
public var logOptions: Flags.Logging
28+
29+
func run() async throws {
30+
let pluginLoader = try? await Application.createPluginLoader()
31+
await Application.printModifiedHelpText(pluginLoader: pluginLoader)
32+
}
33+
}

Tests/CLITests/TestCLIHelp.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2026 Apple Inc. and the container project authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//===----------------------------------------------------------------------===//
16+
17+
import Foundation
18+
import Testing
19+
20+
class TestCLIHelp: CLITest {
21+
@Test func testHelp() throws {
22+
let (_, output, error, status) = try run(arguments: ["help"])
23+
#expect(status == 0, "help should succeed, stderr: \(error)")
24+
25+
#expect(
26+
output.contains("OVERVIEW: A container platform for macOS"),
27+
"output should contain overview section"
28+
)
29+
}
30+
@Test func testDebugHelp() throws {
31+
let (_, output, error, status) = try run(arguments: ["--debug", "help"])
32+
#expect(status == 0, "help should succeed, stderr: \(error)")
33+
34+
#expect(
35+
output.contains("OVERVIEW: A container platform for macOS"),
36+
"output should contain overview section"
37+
)
38+
}
39+
}

0 commit comments

Comments
 (0)