diff --git a/Documentation/SwiftBundler/SwiftBundler.docc/configuration.md b/Documentation/SwiftBundler/SwiftBundler.docc/configuration.md index 78b71e39..7aa28d8a 100644 --- a/Documentation/SwiftBundler/SwiftBundler.docc/configuration.md +++ b/Documentation/SwiftBundler/SwiftBundler.docc/configuration.md @@ -22,7 +22,7 @@ prebuild_script = "./utils/prebuild.sh" postbuild_script = "./utils/postbuild.sh" [apps.HelloWorld.extra_plist_entries] -commit = "{COMMIT}" # This could be any key-value pair, 'commit' is just an example +commit = "{COMMIT_HASH}" # This could be any key-value pair, 'commit' is just an example ``` > Note: Only the `product` and `version` fields are required. diff --git a/Documentation/SwiftBundler/SwiftBundler.docc/installation.md b/Documentation/SwiftBundler/SwiftBundler.docc/installation.md index 0a31264c..7ff26da0 100644 --- a/Documentation/SwiftBundler/SwiftBundler.docc/installation.md +++ b/Documentation/SwiftBundler/SwiftBundler.docc/installation.md @@ -4,7 +4,7 @@ Installing Swift Bundler on your system. ## Recommended -Install the latest version of Swift Bundler from the main branch. +Install the latest release of Swift Bundler. ```sh sh <(curl -L https://stackotter.dev/swift-bundler/install.sh) @@ -12,15 +12,23 @@ sh <(curl -L https://stackotter.dev/swift-bundler/install.sh) ### Installing from a specific branch -Install the latest version of Swift Bundler from a specific branch. +Install Swift Bundler from the latest commit of a specific branch. ```sh sh <(curl -L https://stackotter.dev/swift-bundler/install.sh) [branch] ``` +### Installing from a specific commit + +Install Swift Bundler from a specific commit. + +```sh +sh <(curl -L https://stackotter.dev/swift-bundler/install.sh) [commit] +``` + ### Manual installation -Install any version of Swift Bundler that you want by doing it manually. +Install Swift Bundler however you want by doing it manually. ```sh git clone https://github.com/stackotter/swift-bundler diff --git a/README.md b/README.md index 6954c617..1f44fdc1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

-A tool to create macOS apps with Swift packages instead of Xcode projects. The end goal is to be able to create apps for all desktop platforms with a single Swift codebase. +A tool for creating macOS apps with Swift packages instead of Xcode projects. The end goal is to be able to create apps for all desktop platforms with a single Swift codebase. You may also be interested in [SwiftCrossUI](https://github.com/stackotter/swift-cross-ui), a UI framework with a similar goal. @@ -22,6 +22,8 @@ The documentation is hosted on [GitHub pages](https://stackotter.github.io/swift ## Installation 📦 +Install the latest version of Swift Bundler with a single command: + ```sh sh <(curl -L https://stackotter.dev/swift-bundler/install.sh) ``` @@ -58,7 +60,7 @@ open Package.swift ### Learning more -The [documentation](https://stackotter.github.io/swift-bundler/documentation/swiftbundler) contains everything you need to know about Swift Bundler. +To learn more about Swift Bundler refer to the [documentation](https://stackotter.github.io/swift-bundler/documentation/swiftbundler). ## Contributing 🛠 diff --git a/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildArchitecture.swift b/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildArchitecture.swift index a81cc6a9..d658b724 100644 --- a/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildArchitecture.swift +++ b/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildArchitecture.swift @@ -3,16 +3,16 @@ import ArgumentParser /// An architecture to build for. enum BuildArchitecture: String, CaseIterable, ExpressibleByArgument { - case x86_64 // swiftlint:disable:this identifier_name - case arm64 + case x86_64 // swiftlint:disable:this identifier_name + case arm64 #if arch(x86_64) - static let current: BuildArchitecture = .x86_64 + static let current: BuildArchitecture = .x86_64 #elseif arch(arm64) - static let current: BuildArchitecture = .arm64 + static let current: BuildArchitecture = .arm64 #endif - var defaultValueDescription: String { - rawValue - } + var defaultValueDescription: String { + rawValue + } } diff --git a/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildConfiguration.swift b/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildConfiguration.swift index d2abc598..5b130a88 100644 --- a/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildConfiguration.swift +++ b/Sources/swift-bundler/Bundler/SwiftPackageManager/BuildConfiguration.swift @@ -2,6 +2,6 @@ import Foundation /// A Swift build configuration. enum BuildConfiguration: String, CaseIterable { - case debug - case release + case debug + case release } diff --git a/Sources/swift-bundler/Bundler/SwiftPackageManager/SwiftTargetInfo.swift b/Sources/swift-bundler/Bundler/SwiftPackageManager/SwiftTargetInfo.swift index b343c6fc..d14bc523 100644 --- a/Sources/swift-bundler/Bundler/SwiftPackageManager/SwiftTargetInfo.swift +++ b/Sources/swift-bundler/Bundler/SwiftPackageManager/SwiftTargetInfo.swift @@ -2,12 +2,12 @@ import Foundation /// An extremely simplified version of the output of `swift -print-target-info`. struct SwiftTargetInfo: Codable { - /// Info about a target platform. - struct Target: Codable { - /// The platform's unversioned triple. - var unversionedTriple: String - } + /// Info about a target platform. + struct Target: Codable { + /// The platform's unversioned triple. + var unversionedTriple: String + } - /// Info about the target platform. - var target: Target + /// Info about the target platform. + var target: Target } diff --git a/Sources/swift-bundler/Bundler/Templater/SystemDependency.swift b/Sources/swift-bundler/Bundler/Templater/SystemDependency.swift index aa9923e6..c50b0271 100644 --- a/Sources/swift-bundler/Bundler/Templater/SystemDependency.swift +++ b/Sources/swift-bundler/Bundler/Templater/SystemDependency.swift @@ -2,6 +2,6 @@ import Foundation /// A system dependency required by a template. struct SystemDependency: Codable { - /// The name (or names separated by spaces) of the brew package/s that satisfies this dependency. - var brew: String? + /// The name (or names separated by spaces) of the brew package/s that satisfies this dependency. + var brew: String? } diff --git a/Sources/swift-bundler/Bundler/Templater/TemplateManifest.swift b/Sources/swift-bundler/Bundler/Templater/TemplateManifest.swift index 4641f5da..7f8cb98e 100644 --- a/Sources/swift-bundler/Bundler/Templater/TemplateManifest.swift +++ b/Sources/swift-bundler/Bundler/Templater/TemplateManifest.swift @@ -10,15 +10,15 @@ struct TemplateManifest: Codable { var platforms: [String] /// The minimum Swift version required to use the template. var minimumSwiftVersion: Version - /// The system dependencies required by this template (keyed by the user-facing dependency name). - var systemDependencies: [String: SystemDependency]? + /// The system dependencies required by this template (keyed by the user-facing dependency name). + var systemDependencies: [String: SystemDependency]? - private enum CodingKeys: String, CodingKey { - case description - case platforms - case minimumSwiftVersion = "minimum_swift_version" - case systemDependencies = "system_dependencies" - } + private enum CodingKeys: String, CodingKey { + case description + case platforms + case minimumSwiftVersion = "minimum_swift_version" + case systemDependencies = "system_dependencies" + } /// Loads a template's manifest file. /// - Parameters: diff --git a/Sources/swift-bundler/Bundler/Templater/Templater.swift b/Sources/swift-bundler/Bundler/Templater/Templater.swift index 91010b09..6934e04e 100644 --- a/Sources/swift-bundler/Bundler/Templater/Templater.swift +++ b/Sources/swift-bundler/Bundler/Templater/Templater.swift @@ -27,25 +27,25 @@ enum Templater { return .failure(.packageDirectoryAlreadyExists(outputDirectory)) } - // If no template is specified, create the most basic package that just prints 'Hello, World!' + // If no template is specified, create the most basic package that just prints 'Hello, World!' guard let template = template else { - log.info("Creating package") - - return SwiftPackageManager.createPackage(in: outputDirectory, name: packageName) - .mapError { error -> TemplaterError in - .failedToCreateBareMinimumPackage(error) - } - .flatMap { _ in - log.info("Updating indentation to '\(indentationStyle.defaultValueDescription)'") - return updateIndentationStyle(in: outputDirectory, from: .spaces(4), to: indentationStyle) - } - .mapError { error -> TemplaterError in - attemptCleanup(outputDirectory) - return error - } - .map { (_: Void) -> Template? in - return nil // No template was used - } + log.info("Creating package") + + return SwiftPackageManager.createPackage(in: outputDirectory, name: packageName) + .mapError { error -> TemplaterError in + .failedToCreateBareMinimumPackage(error) + } + .flatMap { _ in + log.info("Updating indentation to '\(indentationStyle.defaultValueDescription)'") + return updateIndentationStyle(in: outputDirectory, from: .spaces(4), to: indentationStyle) + } + .mapError { error -> TemplaterError in + attemptCleanup(outputDirectory) + return error + } + .map { (_: Void) -> Template? in + return nil // No template was used + } } // If a template was specified: Get the default templates directory (and download if not present), and then create the package @@ -59,9 +59,9 @@ enum Templater { forceCreation: forceCreation, indentationStyle: indentationStyle) } - .map { template in - return .some(template) - } + .map { template in + return .some(template) + } } /// Creates a package from the specified template from the specified template repository. @@ -90,7 +90,7 @@ enum Templater { return .failure(.cannotCreatePackageFromBaseTemplate) } - log.info("Creating package from the '\(template)' template") + log.info("Creating package from the '\(template)' template") // Check that the template exists let templateDirectory = templatesDirectory.appendingPathComponent(template) @@ -134,7 +134,7 @@ enum Templater { indentationStyle: .tabs) if case let .failure(error) = result { attemptCleanup(outputDirectory) - return .failure(error) + return .failure(error) } } @@ -147,9 +147,9 @@ enum Templater { ).mapError { error in attemptCleanup(outputDirectory) return error - }.map { _ in - return Template(name: template, manifest: manifest) - } + }.map { _ in + return Template(name: template, manifest: manifest) + } } /// Verifies that the given template supports the current OS and Swift version. @@ -259,7 +259,7 @@ enum Templater { let templateName = directory.lastPathComponent // Skip `Base` template and `.git` directory - guard templateName != "Base" && !templateName.starts(with: ".") else { + guard templateName != "Base" && !templateName.starts(with: ".") else { continue } diff --git a/Sources/swift-bundler/Bundler/Templater/TemplaterError.swift b/Sources/swift-bundler/Bundler/Templater/TemplaterError.swift index 0219f18f..1602e506 100644 --- a/Sources/swift-bundler/Bundler/Templater/TemplaterError.swift +++ b/Sources/swift-bundler/Bundler/Templater/TemplaterError.swift @@ -39,14 +39,14 @@ enum TemplaterError: LocalizedError { case .failedToCreateOutputDirectory(let directory, _): return "Failed to create package directory at '\(directory.relativePath)'" case .failedToDecodeTemplateManifest(let template, _, _): - return Output { - "Failed to decode the manifest for the '\(template)' template" - "" - Section("Troubleshooting") { - "Have you updated your templates recently?" - ExampleCommand("swift bundler templates update") - } - }.description + return Output { + "Failed to decode the manifest for the '\(template)' template" + "" + Section("Troubleshooting") { + "Have you updated your templates recently?" + ExampleCommand("swift bundler templates update") + } + }.description case .failedToReadTemplateManifest(let template, _, _): return "Failed to read the contents of the manifest for the '\(template)' template" case .templateDoesNotSupportCurrentPlatform(let template, let platform, let supportedPlatforms): diff --git a/Sources/swift-bundler/Commands/Command.swift b/Sources/swift-bundler/Commands/Command.swift index 7cd4b53c..d4ec7719 100644 --- a/Sources/swift-bundler/Commands/Command.swift +++ b/Sources/swift-bundler/Commands/Command.swift @@ -3,15 +3,15 @@ import ArgumentParser /// An extension to the `ParsableCommand` API with custom error handling. protocol Command: ParsableCommand { - /// Implement this instead of `validate()` to get custom Swift Bundler error handling. - func wrappedValidate() throws + /// Implement this instead of `validate()` to get custom Swift Bundler error handling. + func wrappedValidate() throws /// Implement this instead of `run()` to get custom Swift Bundler error handling. func wrappedRun() throws } extension Command { - func wrappedValidate() {} + func wrappedValidate() {} } extension Command { @@ -20,21 +20,21 @@ extension Command { try wrappedRun() } catch { log.error("\(error.localizedDescription)") - log.debug("Error details: \(error)") + log.debug("Error details: \(error)") Foundation.exit(1) } } - func validate() { - do { - try wrappedValidate() - } catch { - if let error = error as? ValidationError { - log.error("\(error)") - } else { - log.error("\(error.localizedDescription)") - } - Foundation.exit(1) - } - } + func validate() { + do { + try wrappedValidate() + } catch { + if let error = error as? ValidationError { + log.error("\(error)") + } else { + log.error("\(error.localizedDescription)") + } + Foundation.exit(1) + } + } } diff --git a/Sources/swift-bundler/Commands/CreateCommand.swift b/Sources/swift-bundler/Commands/CreateCommand.swift index 5a879de6..0719c6ff 100644 --- a/Sources/swift-bundler/Commands/CreateCommand.swift +++ b/Sources/swift-bundler/Commands/CreateCommand.swift @@ -15,14 +15,14 @@ struct CreateCommand: Command { /// A custom directory to create the app in. Default: create a new directory at './[app-name]'. @Option( name: [.customShort("d"), .customLong("directory")], - help: "Directory to create the app in. Default: create a new directory at './[app-name]'.", + help: "Directory to create the app in. Default: create a new directory at './[app-name]'.", transform: URL.init(fileURLWithPath:)) var packageDirectory: URL? /// Template to create the app from. @Option( - name: [.customShort("t"), .customLong("template")], - help: "Template to create the app from.") + name: [.customShort("t"), .customLong("template")], + help: "Template to create the app from.") var templateName: String? /// A directory to search for the template in. @@ -44,21 +44,21 @@ struct CreateCommand: Command { help: "Force creation even if the template does not support the current OS and installed Swift version.") var force = false - func wrappedValidate() throws { - guard Self.isValidAppName(appName) else { - throw ValidationError("Invalid app name, app names must only include uppercase and lowercase characters from the English alphabet") - } + func wrappedValidate() throws { + guard Self.isValidAppName(appName) else { + throw ValidationError("Invalid app name, app names must only include uppercase and lowercase characters from the English alphabet") + } - if templateName == nil && templateRepository != nil { - throw ValidationError("The '--template-repository' option can only be used with the '--template' option") - } - } + if templateName == nil && templateRepository != nil { + throw ValidationError("The '--template-repository' option can only be used with the '--template' option") + } + } func wrappedRun() throws { let defaultPackageDirectory = URL(fileURLWithPath: ".").appendingPathComponent(appName) let packageDirectory = packageDirectory ?? defaultPackageDirectory - var template: Template? + var template: Template? let elapsed = try Stopwatch.time { // Create package from template if let templateRepository = templateRepository, let templateName = templateName { @@ -83,45 +83,45 @@ struct CreateCommand: Command { log.info("Done in \(elapsed.secondsString). Package located at '\(packageDirectory.relativePath)'") - Self.printNextSteps(packageDirectory: packageDirectory, template: template) + Self.printNextSteps(packageDirectory: packageDirectory, template: template) } - /// Prints a helpful message telling the user what to try next. Also notifies them of any required system dependencies. - /// - Parameters: - /// - packageDirectory: The package's root directory. - /// - template: The template that the package was created from. - static func printNextSteps(packageDirectory: URL, template: Template?) { - print(Output { - if let template = template, let dependencies = template.manifest.systemDependencies { - "" - Section("System dependencies") { - "The '\(template.name)' template requires the following system dependencies to be installed:" - "" - KeyedList { - for (key, value) in dependencies { - KeyedList.Entry(key) { - Line { - if let packages = value.brew { - "Can be installed via '" - ExampleCommand("brew install \(packages)", withPrompt: false) - "'" - } else { - "Must be manually installed" - } - } - } - } - } - } - } else { - "" - } - Section("Getting started") { - ExampleCommand("cd \(packageDirectory.relativePath.quotedIfNecessary)") - ExampleCommand("swift bundler run") - } - }) - } + /// Prints a helpful message telling the user what to try next. Also notifies them of any required system dependencies. + /// - Parameters: + /// - packageDirectory: The package's root directory. + /// - template: The template that the package was created from. + static func printNextSteps(packageDirectory: URL, template: Template?) { + print(Output { + if let template = template, let dependencies = template.manifest.systemDependencies { + "" + Section("System dependencies") { + "The '\(template.name)' template requires the following system dependencies to be installed:" + "" + KeyedList { + for (key, value) in dependencies { + KeyedList.Entry(key) { + Line { + if let packages = value.brew { + "Can be installed via '" + ExampleCommand("brew install \(packages)", withPrompt: false) + "'" + } else { + "Must be manually installed" + } + } + } + } + } + } + } else { + "" + } + Section("Getting started") { + ExampleCommand("cd \(packageDirectory.relativePath.quotedIfNecessary)") + ExampleCommand("swift bundler run") + } + }) + } /// App names can only contain characters from the English alphabet (to avoid things getting a bit complex when figuring out the product name). /// - Parameter name: The name to verify. diff --git a/Sources/swift-bundler/Commands/Templates/TemplatesUpdateCommand.swift b/Sources/swift-bundler/Commands/Templates/TemplatesUpdateCommand.swift index 8062dde2..96954560 100644 --- a/Sources/swift-bundler/Commands/Templates/TemplatesUpdateCommand.swift +++ b/Sources/swift-bundler/Commands/Templates/TemplatesUpdateCommand.swift @@ -8,10 +8,10 @@ struct TemplatesUpdateCommand: Command { abstract: "Update the default templates to the latest version.") func wrappedRun() throws { - let elapsed = try Stopwatch.time { - try Templater.updateTemplates().unwrap() - } + let elapsed = try Stopwatch.time { + try Templater.updateTemplates().unwrap() + } - log.info("Done in \(elapsed.secondsString).") + log.info("Done in \(elapsed.secondsString).") } } diff --git a/Sources/swift-bundler/Commands/TemplatesCommand.swift b/Sources/swift-bundler/Commands/TemplatesCommand.swift index f6de8f75..dd6a7024 100644 --- a/Sources/swift-bundler/Commands/TemplatesCommand.swift +++ b/Sources/swift-bundler/Commands/TemplatesCommand.swift @@ -11,5 +11,5 @@ struct TemplatesCommand: ParsableCommand { TemplatesInfoCommand.self, TemplatesUpdateCommand.self ], - defaultSubcommand: TemplatesListCommand.self) + defaultSubcommand: TemplatesListCommand.self) } diff --git a/Sources/swift-bundler/Extensions/Sequence.swift b/Sources/swift-bundler/Extensions/Sequence.swift index 652e428f..b37664a9 100644 --- a/Sources/swift-bundler/Extensions/Sequence.swift +++ b/Sources/swift-bundler/Extensions/Sequence.swift @@ -1,7 +1,7 @@ import Foundation extension Sequence where Element == String { - var joinedList: String { - ListFormatter().string(from: Array(self)) ?? self.joined(separator: ", ") - } + var joinedList: String { + ListFormatter().string(from: Array(self)) ?? self.joined(separator: ", ") + } } diff --git a/Sources/swift-bundler/Utility/OutputBuilder/ExampleCommand.swift b/Sources/swift-bundler/Utility/OutputBuilder/ExampleCommand.swift index 88eaee6f..5959ba79 100644 --- a/Sources/swift-bundler/Utility/OutputBuilder/ExampleCommand.swift +++ b/Sources/swift-bundler/Utility/OutputBuilder/ExampleCommand.swift @@ -2,19 +2,19 @@ struct ExampleCommand: OutputComponent { /// The command to display. var command: String - /// Includes a prompt before the command if `true`. - var includePrompt: Bool + /// Includes a prompt before the command if `true`. + var includePrompt: Bool var body: String { - (includePrompt ? "$ " : "") + command.cyan + (includePrompt ? "$ " : "") + command.cyan } /// Creates a component that displays a command in an obvious way. - /// - Parameters: - /// - command: The command to display. - /// - includePrompt: Include a prompt before the command. - init(_ command: String, withPrompt includePrompt: Bool = true) { + /// - Parameters: + /// - command: The command to display. + /// - includePrompt: Include a prompt before the command. + init(_ command: String, withPrompt includePrompt: Bool = true) { self.command = command - self.includePrompt = includePrompt + self.includePrompt = includePrompt } } diff --git a/Sources/swift-bundler/Utility/OutputBuilder/Line.swift b/Sources/swift-bundler/Utility/OutputBuilder/Line.swift index bea28ce2..76b1806f 100644 --- a/Sources/swift-bundler/Utility/OutputBuilder/Line.swift +++ b/Sources/swift-bundler/Utility/OutputBuilder/Line.swift @@ -1,15 +1,15 @@ /// A component that combines multiple components into one line. struct Line: OutputComponent { - /// The child components. - var content: String + /// The child components. + var content: String - var body: String { - content - } + var body: String { + content + } - /// Creates a component that combines multiple components into one line. - /// - Parameter content: The child components. - init(@LineBuilder _ content: () -> String) { - self.content = content() - } + /// Creates a component that combines multiple components into one line. + /// - Parameter content: The child components. + init(@LineBuilder _ content: () -> String) { + self.content = content() + } } diff --git a/Sources/swift-bundler/Utility/OutputBuilder/LineBuilder.swift b/Sources/swift-bundler/Utility/OutputBuilder/LineBuilder.swift index aca3e86a..3126f0b8 100644 --- a/Sources/swift-bundler/Utility/OutputBuilder/LineBuilder.swift +++ b/Sources/swift-bundler/Utility/OutputBuilder/LineBuilder.swift @@ -1,42 +1,42 @@ /// A convenient way of building a line of output. @resultBuilder struct LineBuilder { - static func buildBlock(_ components: OutputComponent...) -> String { - components.map(\.body).joined(separator: "") - } + static func buildBlock(_ components: OutputComponent...) -> String { + components.map(\.body).joined(separator: "") + } - static func buildArray(_ components: [OutputComponent]) -> String { - components.map(\.body).joined(separator: "") - } + static func buildArray(_ components: [OutputComponent]) -> String { + components.map(\.body).joined(separator: "") + } - static func buildOptional(_ component: OutputComponent?) -> String { - component?.body ?? "" - } + static func buildOptional(_ component: OutputComponent?) -> String { + component?.body ?? "" + } - static func buildEither(first component: OutputComponent) -> String { - component.body - } + static func buildEither(first component: OutputComponent) -> String { + component.body + } - static func buildEither(second component: OutputComponent) -> String { - component.body - } + static func buildEither(second component: OutputComponent) -> String { + component.body + } - static func buildBlock(_ components: OutputComponent...) -> [String] { - components.map(\.body) - } + static func buildBlock(_ components: OutputComponent...) -> [String] { + components.map(\.body) + } - static func buildArray(_ components: [OutputComponent]) -> [String] { - components.map(\.body) - } + static func buildArray(_ components: [OutputComponent]) -> [String] { + components.map(\.body) + } - static func buildOptional(_ component: OutputComponent?) -> [String] { - [component?.body ?? ""] - } + static func buildOptional(_ component: OutputComponent?) -> [String] { + [component?.body ?? ""] + } - static func buildEither(first component: OutputComponent) -> [String] { - [component.body] - } + static func buildEither(first component: OutputComponent) -> [String] { + [component.body] + } - static func buildEither(second component: OutputComponent) -> [String] { - [component.body] - } + static func buildEither(second component: OutputComponent) -> [String] { + [component.body] + } } diff --git a/Sources/swift-bundler/Utility/OutputBuilder/Section.swift b/Sources/swift-bundler/Utility/OutputBuilder/Section.swift index 402dbc46..848288b8 100644 --- a/Sources/swift-bundler/Utility/OutputBuilder/Section.swift +++ b/Sources/swift-bundler/Utility/OutputBuilder/Section.swift @@ -7,7 +7,7 @@ struct Section: OutputComponent { var body: String { if let title = title { - title.bold.underline + "\n" + title.bold.underline + "\n" } content + "\n" }