Skip to content

Commit 449f1d2

Browse files
authored
Replace scattered defaults subcommands with system property. (apple#604)
Common subcommands for all defaults. - Closes apple#384. - Replaces `registry default` and `system dns default` subcommands with `system property`. - Users can use `system property ls` to see details about each supported default value. - `system property set` implements reasonable validation for all properties. - NOTE: Probing of the registry for `registry default set` was removed, which means users will find out about a botched setting when pulling or pushing. - Updates docs. ## Type of Change - [ ] Bug fix - [x] New feature - [x] Breaking change - [x] Documentation update ## Motivation and Context See apple#384. ## Testing - [x] Tested locally - [x] Added/updated tests - [x] Added/updated docs
1 parent 386fd87 commit 449f1d2

19 files changed

Lines changed: 628 additions & 380 deletions

Sources/CLI/Registry/RegistryCommand.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ extension Application {
2424
subcommands: [
2525
Login.self,
2626
Logout.self,
27-
RegistryDefault.self,
2827
],
2928
aliases: ["r"]
3029
)

Sources/CLI/Registry/RegistryDefault.swift

Lines changed: 0 additions & 99 deletions
This file was deleted.

Sources/CLI/System/DNS/DNSDefault.swift

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
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 ContainerClient
19+
import ContainerPersistence
20+
import ContainerizationError
21+
import Foundation
22+
23+
extension Application {
24+
struct PropertyClear: AsyncParsableCommand {
25+
static let configuration = CommandConfiguration(
26+
commandName: "clear",
27+
abstract: "Clear a property value"
28+
)
29+
30+
@OptionGroup
31+
var global: Flags.Global
32+
33+
@Argument(help: "the property ID")
34+
var id: String
35+
36+
func run() async throws {
37+
guard let key = DefaultsStore.Keys(rawValue: id) else {
38+
throw ContainerizationError(.invalidArgument, message: "invalid property ID: \(id)")
39+
}
40+
41+
DefaultsStore.unset(key: key)
42+
}
43+
}
44+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
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 ContainerClient
19+
import ContainerPersistence
20+
import ContainerizationError
21+
import Foundation
22+
23+
extension Application {
24+
struct PropertyGet: AsyncParsableCommand {
25+
static let configuration = CommandConfiguration(
26+
commandName: "get",
27+
abstract: "Retrieve a property value"
28+
)
29+
30+
@OptionGroup
31+
var global: Flags.Global
32+
33+
@Argument(help: "the property ID")
34+
var id: String
35+
36+
func run() async throws {
37+
let value = DefaultsStore.allValues()
38+
.filter { id == $0.id }
39+
.first
40+
guard let value else {
41+
throw ContainerizationError(.invalidArgument, message: "property ID \(id) not found")
42+
}
43+
44+
guard let val = value.value?.description else {
45+
return
46+
}
47+
48+
print(val)
49+
}
50+
}
51+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
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 ContainerClient
19+
import ContainerPersistence
20+
import Foundation
21+
22+
extension Application {
23+
struct PropertyList: AsyncParsableCommand {
24+
static let configuration = CommandConfiguration(
25+
commandName: "list",
26+
abstract: "List system properties",
27+
aliases: ["ls"]
28+
)
29+
30+
@Flag(name: .shortAndLong, help: "Only output the network name")
31+
var quiet = false
32+
33+
@Option(name: .long, help: "Format of the output")
34+
var format: ListFormat = .table
35+
36+
@OptionGroup
37+
var global: Flags.Global
38+
39+
func run() async throws {
40+
let vals = DefaultsStore.allValues()
41+
try printValues(vals, format: format)
42+
}
43+
44+
private func createHeader() -> [[String]] {
45+
[["ID", "TYPE", "VALUE", "DESCRIPTION"]]
46+
}
47+
48+
private func printValues(_ vals: [DefaultsStoreValue], format: ListFormat) throws {
49+
if format == .json {
50+
let data = try JSONEncoder().encode(vals)
51+
print(String(data: data, encoding: .utf8)!)
52+
return
53+
}
54+
55+
if self.quiet {
56+
vals.forEach {
57+
print($0.id)
58+
}
59+
return
60+
}
61+
62+
var rows = createHeader()
63+
for property in vals {
64+
rows.append(property.asRow)
65+
}
66+
67+
let formatter = TableOutput(rows: rows)
68+
print(formatter.format())
69+
}
70+
}
71+
}
72+
73+
extension DefaultsStoreValue {
74+
var asRow: [String] {
75+
[id, String(describing: type), value?.description.elided(to: 40) ?? "*undefined*", description]
76+
}
77+
}
78+
79+
extension String {
80+
func elided(to maxCount: Int) -> String {
81+
let ellipsis = "..."
82+
guard self.count > maxCount else {
83+
return self
84+
}
85+
86+
if maxCount < ellipsis.count {
87+
return ellipsis
88+
}
89+
90+
let prefixCount = maxCount - ellipsis.count
91+
return self.prefix(prefixCount) + ellipsis
92+
}
93+
}

0 commit comments

Comments
 (0)