Skip to content

Commit 222d5da

Browse files
authored
Installation workflow improvements (#238)
Synthesize a product build and custom distribution plist. Prepare for shell one-liner that update the shell environment. Copy swiftly to the user home directory instead of install it in their home directory Add more output to diagnose build release issues.
1 parent 5a0b96b commit 222d5da

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()
@@ -237,7 +239,7 @@ internal struct Init: SwiftlyCommand {
237239
(postInstall, pathChanged) = try await Install.execute(version: latestVersion, &config, useInstalledToolchain: true, verifySignature: true, verbose: verbose, assumeYes: assumeYes)
238240
}
239241

240-
if addEnvToProfile {
242+
if addEnvToProfile && !quietShellFollowup {
241243
try Data(sourceLine.utf8).append(to: profileHome)
242244

243245
SwiftlyCore.print("""
@@ -247,7 +249,7 @@ internal struct Init: SwiftlyCommand {
247249
""")
248250
}
249251

250-
if pathChanged {
252+
if pathChanged && !quietShellFollowup {
251253
SwiftlyCore.print("""
252254
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.
253255

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)