Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 82 additions & 9 deletions Sources/VaporToolbox/New.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ArgumentParser
import Foundation
import Yams

extension Vapor {
struct New: ParsableCommand {
Expand All @@ -16,13 +17,13 @@ extension Vapor {
/// They control the build process of the project.
struct BuildOptions: ParsableArguments {
@Option(name: .shortAndLong, help: ArgumentHelp("The URL of a Git repository to use as a template.", valueName: "url"))
var template: String?
var template: String = "https://github.com/vapor/template"

@Option(help: "Template repository branch to use.")
var branch: String?

@Option(help: ArgumentHelp("The path of the manifest file. Defaults to `manifest.yml`.", valueName: "file"))
var manifest: String?
@Option(help: ArgumentHelp("The path of the manifest file.", valueName: "file"))
var manifest: String = "manifest.yml"

@Option(
name: .shortAndLong,
Expand Down Expand Up @@ -56,7 +57,80 @@ extension Vapor {
@OptionGroup(title: "Build Options")
var buildOptions: BuildOptions

static let gitURL = {
do {
let path = try Process.shell.which("git")
return path
} catch {
print("error:".colored(.red) + " unable to find git")
print("Do you have git installed?")
Vapor.New.exit(withError: ExitCode(1))
}
}()

struct PreProcessArgs {
let template: String
let branch: String?
let manifest: String

init(template: String, branch: String?, manifest: String) {
self.template = template
self.branch = branch
self.manifest = manifest
}
}

/// Get the template's manifest file, decode it and save it.
///
/// Clones the template repository, decodes the manifest file and stores it in the ``Vapor/manifest`` `static` property for later use.
///
///
func preprocess(options: PreProcessArgs, gitURL: URL, templateURL: URL) throws {
if options.template == "https://github.com/vapor/template",
FileManager.default.fileExists(atPath: templateURL.path)
{
let pullArgs = ["-C", templateURL.path, "pull"]
try Process.runUntilExit(gitURL, arguments: pullArgs)
} else {
try? FileManager.default.removeItem(at: templateURL)
var cloneArgs = ["clone", "--depth", "1"]
if options.branch != nil,
let branch = options.branch
{
cloneArgs.append("--branch")
cloneArgs.append(branch)
}
cloneArgs.append(options.template)
cloneArgs.append(templateURL.path())
print("Cloning template...".colored(.cyan))
try Process.runUntilExit(gitURL, arguments: cloneArgs)
}

let manifestURL = templateURL.appending(path: options.manifest)

var result: TemplateManifest? = nil

if FileManager.default.fileExists(atPath: manifestURL.path()) {
let manifestData = try Data(contentsOf: manifestURL)
result =
if manifestURL.pathExtension == "json" {
try JSONDecoder().decode(TemplateManifest.self, from: manifestData)
} else {
try YAMLDecoder().decode(TemplateManifest.self, from: manifestData)
}
}
Vapor.manifest = result

}

mutating func run() throws {

let preProcessArgs = PreProcessArgs(
template: self.buildOptions.template,
branch: self.buildOptions.branch,
manifest: self.buildOptions.manifest)
try preprocess(options: preProcessArgs, gitURL: Self.gitURL, templateURL: Vapor.templateURL)

if self.buildOptions.dumpVariables {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
Expand All @@ -78,7 +152,6 @@ extension Vapor {
}

if let manifest = Vapor.manifest {
defer { try? FileManager.default.removeItem(at: Vapor.templateURL) }

try FileManager.default.createDirectory(at: projectURL, withIntermediateDirectories: false)

Expand All @@ -95,7 +168,7 @@ extension Vapor {
)
} else {
// If the template doesn't have a manifest (AKA doesn't need templating), just move the files
try FileManager.default.moveItem(at: Vapor.templateURL, to: projectURL)
try FileManager.default.copyItem(at: Vapor.templateURL, to: projectURL)
}

if !self.buildOptions.noGit {
Expand All @@ -105,14 +178,15 @@ extension Vapor {
if (try? gitDir.checkResourceIsReachable()) ?? false {
try FileManager.default.removeItem(at: gitDir) // Clear existing git history
}
try Process.runUntilExit(Vapor.gitURL, arguments: ["--git-dir=\(gitDir.path(percentEncoded: false))", "init"])
try Process.runUntilExit(Vapor.New.gitURL, arguments: ["--git-dir=\(gitDir.path(percentEncoded: false))", "init"])

if !self.buildOptions.noCommit {
print("Adding first commit".colored(.cyan))
let gitDirFlag = "--git-dir=\(gitDir.path())"
let workTreeFlag = "--work-tree=\(projectURL.path())"
try Process.runUntilExit(Vapor.gitURL, arguments: [gitDirFlag, workTreeFlag, "add", "."])
try Process.runUntilExit(Vapor.gitURL, arguments: [gitDirFlag, workTreeFlag, "commit", "-m", "Generate Vapor project"])
try Process.runUntilExit(Vapor.New.gitURL, arguments: [gitDirFlag, workTreeFlag, "add", "."])
try Process.runUntilExit(
Vapor.New.gitURL, arguments: [gitDirFlag, workTreeFlag, "commit", "-m", "Generate Vapor project"])
}
}

Expand Down Expand Up @@ -244,7 +318,6 @@ extension Vapor.New: CustomReflectable {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(Argument.self, forKey: .name).wrappedValue
self.buildOptions = try container.decode(OptionGroup<BuildOptions>.self, forKey: .buildOptions).wrappedValue

guard let variables = Vapor.manifest?.variables else { return }

func decodeVariable(_ variable: TemplateManifest.Variable, path: String) throws -> Any? {
Expand Down
2 changes: 1 addition & 1 deletion Sources/VaporToolbox/TemplateRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ struct TemplateRenderer {
try MustacheTemplate(string: template).render(context)
.write(to: destinationFileURL, atomically: true, encoding: .utf8)
} else {
try FileManager.default.moveItem(at: sourceURL.appending(path: file.name), to: destinationFileURL)
try FileManager.default.copyItem(at: sourceURL.appending(path: file.name), to: destinationFileURL)
}
if self.verbose { print("+ " + file.name) }
case .folder(let files):
Expand Down
68 changes: 8 additions & 60 deletions Sources/VaporToolbox/Vapor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,86 +11,34 @@ struct Vapor: ParsableCommand {
defaultSubcommand: New.self
)

nonisolated(unsafe) static var manifest: TemplateManifest? = nil
static let templateURL = URL.temporaryDirectory.appending(path: ".vapor-template", directoryHint: .isDirectory)
static let gitURL = try! Process.shell.which("git")
static let templateURL = URL.homeDirectory.appending(path: ".vapor-template", directoryHint: .isDirectory)
nonisolated(unsafe) public static var manifest: TemplateManifest? = nil

static func main() {
do {
try Self.preprocess(CommandLine.arguments)
try loadManifest()
var command = try parseAsRoot(nil)
try command.run()
} catch {
exit(withError: error)
}
}

/// Get the template's manifest file, decode it and save it.
///
/// Clones the template repository, decodes the manifest file and stores it in the ``Vapor/manifest`` `static` property for later use.
///
/// - Parameter arguments: The command line arguments.
static func preprocess(_ arguments: [String]) throws {
guard !arguments.contains("--version") else {
return
}

let templateWebURL =
if let index = arguments.firstIndex(of: "--template") {
arguments[index + 1]
} else if let index = arguments.firstIndex(of: "-t") {
arguments[index + 1]
} else {
"https://github.com/vapor/template"
}

let branch: String? =
if let index = arguments.firstIndex(of: "--branch") {
arguments[index + 1]
} else {
nil
}
static func loadManifest() throws {
let manifestURL = templateURL.appending(path: "manifest.yml")

try? FileManager.default.removeItem(at: Self.templateURL)

if !arguments.contains("-h"),
!arguments.contains("--help"),
!arguments.contains("-help"),
!arguments.contains("--help-hidden"),
!arguments.contains("-help-hidden"),
!arguments.contains("--generate-completion-script"),
!arguments.contains("--dump-variables")
{
print("Cloning template...".colored(.cyan))
}
var cloneArgs = ["clone"]
if let branch {
cloneArgs.append("--branch")
cloneArgs.append(branch)
}
cloneArgs.append(templateWebURL)
cloneArgs.append(Self.templateURL.path())
try Process.runUntilExit(Self.gitURL, arguments: cloneArgs)

var manifestURL: URL
if let index = arguments.firstIndex(of: "--manifest") {
manifestURL = Self.templateURL.appending(path: arguments[index + 1])
} else {
manifestURL = Self.templateURL.appending(path: "manifest.yml")
if !FileManager.default.fileExists(atPath: manifestURL.path()) {
manifestURL = Self.templateURL.appending(path: "manifest.json")
}
}
var result: TemplateManifest? = nil

if FileManager.default.fileExists(atPath: manifestURL.path()) {
let manifestData = try Data(contentsOf: manifestURL)
Self.manifest =
result =
if manifestURL.pathExtension == "json" {
try JSONDecoder().decode(TemplateManifest.self, from: manifestData)
} else {
try YAMLDecoder().decode(TemplateManifest.self, from: manifestData)
}
}
Vapor.manifest = result
}

/// The version of this Vapor Toolbox.
Expand Down
6 changes: 4 additions & 2 deletions Tests/VaporToolboxTests/VaporToolboxTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import Yams
@Suite("VaporToolbox Tests")
struct VaporToolboxTests {
#if !os(Android)
@Test("Vapor.preprocess")
@Test("Vapor.New.preprocess")
func preprocess() throws {
let options = Vapor.New.PreProcessArgs(template: "https://github.com/vapor/template", branch: nil, manifest: "manifest.yml")
let vaporNew = Vapor.New()
#expect(Vapor.manifest == nil)
try Vapor.preprocess([])
try vaporNew.preprocess(options: options, gitURL: Vapor.New.gitURL, templateURL: Vapor.templateURL)
#expect(Vapor.manifest != nil)
}
#endif
Expand Down
Loading