diff --git a/Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md b/Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md index 1ba9b5a6..8a667e17 100644 --- a/Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md +++ b/Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md @@ -405,7 +405,7 @@ written to this file as commands that can be run after the installation. Perform swiftly initialization into your user account. ``` -swiftly init [--no-modify-profile] [--overwrite] [--platform=] [--skip-install] [--assume-yes] [--verbose] [--version] [--help] +swiftly init [--no-modify-profile] [--overwrite] [--platform=] [--skip-install] [--quiet-shell-followup] [--assume-yes] [--verbose] [--version] [--help] ``` **--no-modify-profile:** @@ -428,6 +428,11 @@ swiftly init [--no-modify-profile] [--overwrite] [--platform=] [--skip *Skip installing the latest toolchain* +**--quiet-shell-followup:** + +*Quiet shell follow up commands* + + **--assume-yes:** *Disable confirmation prompts by assuming 'yes'* diff --git a/Sources/Swiftly/Init.swift b/Sources/Swiftly/Init.swift index e2c6fa9c..53dca68b 100644 --- a/Sources/Swiftly/Init.swift +++ b/Sources/Swiftly/Init.swift @@ -18,21 +18,23 @@ internal struct Init: SwiftlyCommand { var platform: String? @Flag(help: "Skip installing the latest toolchain") var skipInstall: Bool = false + @Flag(help: "Quiet shell follow up commands") + var quietShellFollowup: Bool = false @OptionGroup var root: GlobalOptions private enum CodingKeys: String, CodingKey { - case noModifyProfile, overwrite, platform, skipInstall, root + case noModifyProfile, overwrite, platform, skipInstall, root, quietShellFollowup } public mutating func validate() throws {} internal mutating func run() async throws { - try await Self.execute(assumeYes: self.root.assumeYes, noModifyProfile: self.noModifyProfile, overwrite: self.overwrite, platform: self.platform, verbose: self.root.verbose, skipInstall: self.skipInstall) + 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) } /// Initialize the installation of swiftly. - internal static func execute(assumeYes: Bool, noModifyProfile: Bool, overwrite: Bool, platform: String?, verbose: Bool, skipInstall: Bool) async throws { + internal static func execute(assumeYes: Bool, noModifyProfile: Bool, overwrite: Bool, platform: String?, verbose: Bool, skipInstall: Bool, quietShellFollowup: Bool) async throws { try Swiftly.currentPlatform.verifySwiftlySystemPrerequisites() var config = try? Config.load() @@ -237,7 +239,7 @@ internal struct Init: SwiftlyCommand { (postInstall, pathChanged) = try await Install.execute(version: latestVersion, &config, useInstalledToolchain: true, verifySignature: true, verbose: verbose, assumeYes: assumeYes) } - if addEnvToProfile { + if addEnvToProfile && !quietShellFollowup { try Data(sourceLine.utf8).append(to: profileHome) SwiftlyCore.print(""" @@ -247,7 +249,7 @@ internal struct Init: SwiftlyCommand { """) } - if pathChanged { + if pathChanged && !quietShellFollowup { SwiftlyCore.print(""" 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. diff --git a/Sources/Swiftly/Proxy.swift b/Sources/Swiftly/Proxy.swift index 9d46fcc9..a389e444 100644 --- a/Sources/Swiftly/Proxy.swift +++ b/Sources/Swiftly/Proxy.swift @@ -25,7 +25,7 @@ public enum Proxy { if CommandLine.arguments.count == 1 { // User ran swiftly with no extra arguments in an uninstalled environment, so we jump directly into // an simple init. - try await Init.execute(assumeYes: false, noModifyProfile: false, overwrite: false, platform: nil, verbose: false, skipInstall: false) + try await Init.execute(assumeYes: false, noModifyProfile: false, overwrite: false, platform: nil, verbose: false, skipInstall: false, quietShellFollowup: false) return } else if CommandLine.arguments.count >= 2 && CommandLine.arguments[1] == "init" { // Let the user run the init command with their arguments, if any. diff --git a/Tools/build-swiftly-release/BuildSwiftlyRelease.swift b/Tools/build-swiftly-release/BuildSwiftlyRelease.swift index ec5e203c..9f06ade5 100644 --- a/Tools/build-swiftly-release/BuildSwiftlyRelease.swift +++ b/Tools/build-swiftly-release/BuildSwiftlyRelease.swift @@ -78,13 +78,14 @@ public func runProgram(_ args: String..., quiet: Bool = false) throws { } public func runProgramOutput(_ program: String, _ args: String...) async throws -> String? { + print("\(program) \(args.joined(separator: " "))") + let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/env") process.arguments = [program] + args let outPipe = Pipe() process.standardInput = FileHandle.nullDevice - process.standardError = FileHandle.nullDevice process.standardOutput = outPipe try process.run() @@ -98,6 +99,7 @@ public func runProgramOutput(_ program: String, _ args: String...) async throws process.waitUntilExit() guard process.terminationStatus == 0 else { + print("\(args.first!) exited with non-zero status: \(process.terminationStatus)") throw Error(message: "\(args.first!) exited with non-zero status: \(process.terminationStatus)") } @@ -406,6 +408,10 @@ struct BuildSwiftlyRelease: AsyncParsableCommand { try? FileManager.default.createDirectory(atPath: swiftlyLicenseDir, withIntermediateDirectories: true) try await self.collectLicenses(swiftlyLicenseDir) + let cwd = FileManager.default.currentDirectoryPath + + let pkgFile = URL(fileURLWithPath: cwd + "/.build/release/swiftly-\(self.version).pkg") + if let cert { try runProgram( pkgbuild, @@ -435,5 +441,25 @@ struct BuildSwiftlyRelease: AsyncParsableCommand { ".build/release/swiftly-\(self.version).pkg" ) } + + // Re-configure the pkg to prefer installs into the current user's home directory with the help of productbuild. + // Note that command-line installs can override this preference, but the GUI install will limit the choices. + + let pkgFileReconfigured = URL(fileURLWithPath: cwd + "/.build/release/swiftly-\(self.version)-reconfigured.pkg") + let distFile = URL(fileURLWithPath: cwd + "/.build/release/distribution.plist") + + try runProgram("productbuild", "--synthesize", "--package", pkgFile.path, distFile.path) + + var distFileContents = try String(contentsOf: distFile, encoding: .utf8) + distFileContents = distFileContents.replacingOccurrences(of: "", with: "") + try distFileContents.write(to: distFile, atomically: true, encoding: .utf8) + + if let cert = cert { + try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.path, "--sign", cert, pkgFileReconfigured.path) + } else { + try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.path, pkgFileReconfigured.path) + } + try FileManager.default.removeItem(at: pkgFile) + try FileManager.default.copyItem(atPath: pkgFileReconfigured.path, toPath: pkgFile.path) } }