Skip to content

Commit 2330056

Browse files
authored
Installation workflow improvements (#249)
* Update swiftly version to 1.0.0 and add upgrade routine for 0.4.0 (#237) * Handle macOS system install of pkg * Prepare for shell one-liner that update the shell environment * Use a synthesized product build and custom distribution plist * Add more output to diagnose build release issue * Properly report the runProgramOutput() command line
1 parent 726ea90 commit 2330056

File tree

4 files changed

+41
-8
lines changed

4 files changed

+41
-8
lines changed

Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ written to this file as commands that can be run after the installation.
405405
Perform swiftly initialization into your user account.
406406

407407
```
408-
swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--skip-install] [--assume-yes] [--verbose] [--version] [--help]
408+
swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--skip-install] [--quiet-shell-followup] [--assume-yes] [--verbose] [--version] [--help]
409409
```
410410

411411
**--no-modify-profile:**
@@ -428,6 +428,11 @@ swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--skip
428428
*Skip installing the latest toolchain*
429429

430430

431+
**--quiet-shell-followup:**
432+
433+
*Quiet shell follow up commands*
434+
435+
431436
**--assume-yes:**
432437

433438
*Disable confirmation prompts by assuming 'yes'*

Sources/Swiftly/Init.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,23 @@ internal struct Init: SwiftlyCommand {
1818
var platform: String?
1919
@Flag(help: "Skip installing the latest toolchain")
2020
var skipInstall: Bool = false
21+
@Flag(help: "Quiet shell follow up commands")
22+
var quietShellFollowup: Bool = false
2123

2224
@OptionGroup var root: GlobalOptions
2325

2426
private enum CodingKeys: String, CodingKey {
25-
case noModifyProfile, overwrite, platform, skipInstall, root
27+
case noModifyProfile, overwrite, platform, skipInstall, root, quietShellFollowup
2628
}
2729

2830
public mutating func validate() throws {}
2931

3032
internal mutating func run() async throws {
31-
try await Self.execute(assumeYes: self.root.assumeYes, noModifyProfile: self.noModifyProfile, overwrite: self.overwrite, platform: self.platform, verbose: self.root.verbose, skipInstall: self.skipInstall)
33+
try await Self.execute(assumeYes: self.root.assumeYes, noModifyProfile: self.noModifyProfile, overwrite: self.overwrite, platform: self.platform, verbose: self.root.verbose, skipInstall: self.skipInstall, quietShellFollowup: self.quietShellFollowup)
3234
}
3335

3436
/// Initialize the installation of swiftly.
35-
internal static func execute(assumeYes: Bool, noModifyProfile: Bool, overwrite: Bool, platform: String?, verbose: Bool, skipInstall: Bool) async throws {
37+
internal static func execute(assumeYes: Bool, noModifyProfile: Bool, overwrite: Bool, platform: String?, verbose: Bool, skipInstall: Bool, quietShellFollowup: Bool) async throws {
3638
try Swiftly.currentPlatform.verifySwiftlySystemPrerequisites()
3739

3840
var config = try? Config.load()
@@ -238,7 +240,7 @@ internal struct Init: SwiftlyCommand {
238240
(postInstall, pathChanged) = try await Install.execute(version: latestVersion, &config, useInstalledToolchain: true, verifySignature: true, verbose: verbose, assumeYes: assumeYes)
239241
}
240242

241-
if addEnvToProfile {
243+
if addEnvToProfile && !quietShellFollowup {
242244
try Data(sourceLine.utf8).append(to: profileHome)
243245

244246
SwiftlyCore.print("""
@@ -248,7 +250,7 @@ internal struct Init: SwiftlyCommand {
248250
""")
249251
}
250252

251-
if pathChanged {
253+
if pathChanged && !quietShellFollowup {
252254
SwiftlyCore.print("""
253255
Your shell caches items on your path for better performance. Swiftly has added items to your path that may not get picked up right away. You can run this command to update your shell to get these items.
254256

Sources/Swiftly/Proxy.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public enum Proxy {
2525
if CommandLine.arguments.count == 1 {
2626
// User ran swiftly with no extra arguments in an uninstalled environment, so we jump directly into
2727
// an simple init.
28-
try await Init.execute(assumeYes: false, noModifyProfile: false, overwrite: false, platform: nil, verbose: false, skipInstall: false)
28+
try await Init.execute(assumeYes: false, noModifyProfile: false, overwrite: false, platform: nil, verbose: false, skipInstall: false, quietShellFollowup: false)
2929
return
3030
} else if CommandLine.arguments.count >= 2 && CommandLine.arguments[1] == "init" {
3131
// Let the user run the init command with their arguments, if any.

Tools/build-swiftly-release/BuildSwiftlyRelease.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@ public func runProgram(_ args: String..., quiet: Bool = false) throws {
7878
}
7979

8080
public func runProgramOutput(_ program: String, _ args: String...) async throws -> String? {
81+
print("\(program) \(args.joined(separator: " "))")
82+
8183
let process = Process()
8284
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
8385
process.arguments = [program] + args
8486

8587
let outPipe = Pipe()
8688
process.standardInput = FileHandle.nullDevice
87-
process.standardError = FileHandle.nullDevice
8889
process.standardOutput = outPipe
8990

9091
try process.run()
@@ -98,6 +99,7 @@ public func runProgramOutput(_ program: String, _ args: String...) async throws
9899
process.waitUntilExit()
99100

100101
guard process.terminationStatus == 0 else {
102+
print("\(args.first!) exited with non-zero status: \(process.terminationStatus)")
101103
throw Error(message: "\(args.first!) exited with non-zero status: \(process.terminationStatus)")
102104
}
103105

@@ -406,6 +408,10 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
406408
try? FileManager.default.createDirectory(atPath: swiftlyLicenseDir, withIntermediateDirectories: true)
407409
try await self.collectLicenses(swiftlyLicenseDir)
408410

411+
let cwd = FileManager.default.currentDirectoryPath
412+
413+
let pkgFile = URL(fileURLWithPath: cwd + "/.build/release/swiftly-\(self.version).pkg")
414+
409415
if let cert {
410416
try runProgram(
411417
pkgbuild,
@@ -435,5 +441,25 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
435441
".build/release/swiftly-\(self.version).pkg"
436442
)
437443
}
444+
445+
// Re-configure the pkg to prefer installs into the current user's home directory with the help of productbuild.
446+
// Note that command-line installs can override this preference, but the GUI install will limit the choices.
447+
448+
let pkgFileReconfigured = URL(fileURLWithPath: cwd + "/.build/release/swiftly-\(self.version)-reconfigured.pkg")
449+
let distFile = URL(fileURLWithPath: cwd + "/.build/release/distribution.plist")
450+
451+
try runProgram("productbuild", "--synthesize", "--package", pkgFile.path, distFile.path)
452+
453+
var distFileContents = try String(contentsOf: distFile, encoding: .utf8)
454+
distFileContents = distFileContents.replacingOccurrences(of: "<choices-outline>", with: "<domains enable_anywhere=\"false\" enable_currentUserHome=\"true\" enable_localSystem=\"false\"/><choices-outline>")
455+
try distFileContents.write(to: distFile, atomically: true, encoding: .utf8)
456+
457+
if let cert = cert {
458+
try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.path, "--sign", cert, pkgFileReconfigured.path)
459+
} else {
460+
try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.path, pkgFileReconfigured.path)
461+
}
462+
try FileManager.default.removeItem(at: pkgFile)
463+
try FileManager.default.copyItem(atPath: pkgFileReconfigured.path, toPath: pkgFile.path)
438464
}
439465
}

0 commit comments

Comments
 (0)