Skip to content

Commit 125e682

Browse files
committed
Update for feedback & remove --branch option
Make for the `--unmanaged` flag on `add` to have an optional argument of a commit or a branch. However this lead to complications around other flags being passed such as `add blah --unmanaged -u -v`. To resolve that issue, the properties on `Option` were move to `public` so the add command could try its best to still apply arguments when they were inadvertantly swallowed by the now `--unmanaged` optional value. Also the `unmanagedValue` is tracked in the `.modulo` file like `branch` was but is more generic supporting an entire commit hash or a branch name
1 parent 8a3f6a6 commit 125e682

File tree

8 files changed

+86
-51
lines changed

8 files changed

+86
-51
lines changed

Modules/ELCLI/ELCLI/Options.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import Foundation
1111
public typealias OptionClosure = (_ option: String?, _ value: String?) -> Void
1212

1313
public struct Option {
14-
let usage: String?
15-
let flags: Array<String>?
16-
let valueSignatures: Array<String>?
17-
let closure: OptionClosure
14+
public let usage: String?
15+
public let flags: Array<String>?
16+
public let valueSignatures: Array<String>?
17+
public let closure: OptionClosure
1818

1919
init(flags: Array<String>?, usage: String?, valueSignatures: Array<String>?, closure: @escaping OptionClosure) {
2020
self.flags = flags

ModuloKit/Actions.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ open class Actions {
2222
}
2323
}
2424

25-
open func addDependency(_ url: String, version: SemverRange?, branch: String?, unmanaged: Bool) -> ErrorCode {
26-
let dep = DependencySpec(repositoryURL: url, version: version, branch: branch)
25+
open func addDependency(_ url: String, version: SemverRange?, unmanagedValue: String?, unmanaged: Bool) -> ErrorCode {
26+
let dep = DependencySpec(repositoryURL: url, version: version, unmanagedValue: unmanagedValue)
2727
if var workingSpec = ModuleSpec.workingSpec() {
2828
// does this dep already exist in here??
2929
if let _ = workingSpec.dependencyForURL(url) {
@@ -77,8 +77,8 @@ open class Actions {
7777
exit(checkoutResult.errorMessage())
7878
}
7979
}
80-
if let branch = dep.branch {
81-
let checkoutResult = scm.checkout(branch: branch, path: clonePath)
80+
if let unmanagedValue = dep.unmanagedValue {
81+
let checkoutResult = scm.checkout(branchOrHash: unmanagedValue, path: clonePath)
8282
if checkoutResult != .success {
8383
exit(checkoutResult.errorMessage())
8484
}
@@ -102,7 +102,7 @@ open class Actions {
102102
}
103103

104104
// if they're unmanaged and on a branch, tracking a remote, just do a pull
105-
if dep.unmanaged == true, let currentBranch = scm.branchName(clonePath) {
105+
if dep.unmanaged == true && dep.unmanagedValue == nil, let currentBranch = scm.branchName(clonePath) {
106106
if scm.remoteTrackingBranch(currentBranch) != nil {
107107
let pullResult = scm.pull(clonePath, remoteData: nil)
108108
if pullResult != .success {
@@ -116,13 +116,13 @@ open class Actions {
116116
if checkoutResult != .success {
117117
exit(checkoutResult.errorMessage())
118118
}
119-
} else if let branch = dep.branch {
120-
let checkoutResult = scm.checkout(branch: branch, path: clonePath)
119+
} else if let unmanagedValue = dep.unmanagedValue {
120+
let checkoutResult = scm.checkout(branchOrHash: unmanagedValue, path: clonePath)
121121
if checkoutResult != .success {
122122
exit(checkoutResult.errorMessage())
123123
}
124124
} else {
125-
exit("\(dep.name()) doesn't have a version, branch, and isn't marked as 'unmanaged', not sure what to do.")
125+
exit("\(dep.name()) doesn't have a version and isn't marked as 'unmanaged', not sure what to do.")
126126
}
127127
}
128128
}

ModuloKit/Commands/AddCommand.swift

+20-11
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import Foundation
1616
open class AddCommand: NSObject, Command {
1717
// internal properties
1818
fileprivate var version: SemverRange? = nil
19-
fileprivate var branch: String? = nil
2019
fileprivate var repositoryURL: String! = nil
2120
fileprivate var shouldUpdate: Bool = false
2221
fileprivate var unmanaged: Bool = false
22+
fileprivate var unmanagedValue: String? = nil
2323

2424
// Protocol conformance
2525
open var name: String { return "add" }
@@ -41,15 +41,24 @@ open class AddCommand: NSObject, Command {
4141
self.version = SemverRange(value)
4242
}
4343
}
44-
45-
addOptionValue(["--branch"], usage: "specify the branch to track", valueSignature: "<branch>") { (option, value) in
46-
if let value = value {
47-
self.branch = value
48-
}
49-
}
5044

51-
addOption(["--unmanaged"], usage: "specifies that this module will be unmanaged") { (option, value) in
45+
addOptionValue(["--unmanaged"], usage: "specifies that this module will be unmanaged", valueSignature: "<[hash|branch|nothing]>") { (option, value) -> Void in
5246
self.unmanaged = true
47+
if let value = value {
48+
if !value.hasPrefix("-") {
49+
self.unmanagedValue = value
50+
} else {
51+
if self.verbose {
52+
writeln(.stderr, "Assuming '\(value)' is a flag and not a branch/commit hash since it begins with '-' and will not track it.")
53+
}
54+
// TODO: Somehow reprocess this `value` as a flag
55+
self.options.filter({ (option) -> Bool in
56+
option.flags?.contains(value) ?? false
57+
}).forEach({ (option) in
58+
option.closure(value, nil)
59+
})
60+
}
61+
}
5362
}
5463

5564
addOption(["-u", "--update"], usage: "performs the update command after adding a module") { (option, value) in
@@ -64,8 +73,8 @@ open class AddCommand: NSObject, Command {
6473
open func execute(_ otherParams: Array<String>?) -> Int {
6574
let actions = Actions()
6675

67-
if version == nil && branch == nil && unmanaged == false {
68-
writeln(.stderr, "A version or range must be specified via --version, a branch must be specified via --branch, or --unmanaged must be used.")
76+
if version == nil && unmanaged == false {
77+
writeln(.stderr, "A version or range must be specified via --version or --unmanaged must be used.")
6978
return ErrorCode.commandError.rawValue
7079
}
7180

@@ -76,7 +85,7 @@ open class AddCommand: NSObject, Command {
7685
}
7786
}
7887

79-
let result = actions.addDependency(repositoryURL, version: version, branch: branch, unmanaged: unmanaged)
88+
let result = actions.addDependency(repositoryURL, version: version, unmanagedValue: unmanagedValue, unmanaged: unmanaged)
8089
if result == .success {
8190
if shouldUpdate {
8291
writeln(.stdout, "Added \(String(describing: repositoryURL)).")

ModuloKit/SCM/Git.swift

+11-16
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ open class Git: SCM {
176176
return .success
177177
}
178178

179-
open func checkout(branch: String, path: String) -> SCMResult {
179+
open func checkout(branchOrHash: String, path: String) -> SCMResult {
180180
if !FileManager.fileExists(path) {
181181
return .error(code: 1, message: "Module path '\(path)' does not exist.")
182182
}
@@ -186,25 +186,20 @@ open class Git: SCM {
186186
let initialWorkingPath = FileManager.workingPath()
187187
FileManager.setWorkingPath(path)
188188

189-
let existingBranches = branches(".")
189+
// try fetching it directly
190+
let fetchResult = runCommand("git fetch origin \(branchOrHash)")
190191

191-
var neededFetch = false
192-
var fetchResult: Int32? = nil
193-
if !existingBranches.contains(branch) {
194-
// try fetching it directly
195-
fetchResult = runCommand("git fetch origin \(branch)")
196-
neededFetch = true
192+
if branches(".").contains(branchOrHash) {
193+
checkoutCommand = "git checkout origin/\(branchOrHash) --quiet"
194+
} else {
195+
checkoutCommand = "git checkout \(branchOrHash) --quiet"
197196
}
198197

199-
checkoutCommand = "git checkout origin/\(branch) --quiet"
200-
201-
if neededFetch,
202-
let fetchResult = fetchResult,
203-
fetchResult != 0 {
198+
if fetchResult != 0 {
204199
if verbose {
205-
writeln(.stderr, "Unable to find branch '\(branch)'.")
200+
writeln(.stderr, "Unable to find unmanaged value '\(branchOrHash)'.")
206201
}
207-
return .error(code: SCMDefaultError, message: "Unable to find a match for \(branch).")
202+
return .error(code: SCMDefaultError, message: "Unable to find a match for \(branchOrHash).")
208203
}
209204

210205
let status = runCommand(checkoutCommand)
@@ -214,7 +209,7 @@ open class Git: SCM {
214209
FileManager.setWorkingPath(initialWorkingPath)
215210

216211
if status != 0 {
217-
return .error(code: status, message: "Unable to checkout '\(branch)'.")
212+
return .error(code: status, message: "Unable to checkout '\(branchOrHash)'.")
218213
}
219214

220215
if submodulesResult != .success {

ModuloKit/SCM/SCM.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ public protocol SCM {
7676
func fetch(_ path: String) -> SCMResult
7777
func pull(_ path: String, remoteData: String?) -> SCMResult
7878
func checkout(version: SemverRange, path: String) -> SCMResult
79-
func checkout(branch: String, path: String) -> SCMResult
79+
/// Check out an arbitrary point or the HEAD of a branch (in git)
80+
/// or the equivalent in other SCM solutions
81+
func checkout(branchOrHash: String, path: String) -> SCMResult
8082
func remove(_ path: String) -> SCMResult
8183
func adjustIgnoreFile(pattern: String, removing: Bool) -> SCMResult
8284
func checkStatus(_ path: String) -> SCMResult

ModuloKit/Specs/DependencySpec.swift

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ public struct DependencySpec {
1717
var repositoryURL: String
1818
// version or version range
1919
var version: SemverRange?
20-
/// Branch to track
21-
var branch: String?
20+
/// Optional unmanaged property to track
21+
/// such as a branch name, commit hash, or nothing
22+
var unmanagedValue: String?
2223

2324
var unmanaged: Bool {
2425
get {
25-
return (version == nil) && (branch == nil)
26+
return (version == nil)
2627
}
2728
}
2829
}
@@ -32,7 +33,7 @@ extension DependencySpec: ELDecodable {
3233
return try DependencySpec(
3334
repositoryURL: json ==> "repositoryURL",
3435
version: json ==> "version",
35-
branch: json ==> "branch"
36+
unmanagedValue: json ==> "unmanagedValue"
3637
)
3738
}
3839

@@ -46,7 +47,7 @@ extension DependencySpec: ELEncodable {
4647
return try encodeToJSON([
4748
"repositoryURL" <== repositoryURL,
4849
"version" <== version,
49-
"branch" <== branch
50+
"unmanagedValue" <== unmanagedValue
5051
])
5152
}
5253
}

ModuloKitTests/TestAdd.swift

+33-5
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,15 @@ class TestAdd: XCTestCase {
7979
FileManager.setWorkingPath("..")
8080
}
8181

82-
func testAddModuleByBranch() {
82+
func testAddUnmanagedModuleWithBranch() {
8383
let status = Git().clone("[email protected]:modulo-dm/test-add.git", path: "test-add")
8484
XCTAssertTrue(status == .success)
8585

8686
FileManager.setWorkingPath("test-add")
8787

8888
let repoURL = "[email protected]:modulo-dm/test-add-update.git"
8989

90-
let result = Modulo.run(["add", repoURL, "--branch", "master", "-v"])
90+
let result = Modulo.run(["add", repoURL, "--unmanaged", "master", "-v"])
9191
XCTAssertTrue(result == .success)
9292

9393

@@ -99,9 +99,37 @@ class TestAdd: XCTestCase {
9999
XCTFail("Failed to find dependency for url \(repoURL) in spec \(spec)")
100100
return }
101101
XCTAssertNil(dep.version)
102-
XCTAssertFalse(dep.unmanaged)
103-
XCTAssertNotNil(dep.branch)
104-
XCTAssertTrue(dep.branch == "master")
102+
XCTAssertTrue(dep.unmanaged)
103+
XCTAssertNotNil(dep.unmanagedValue)
104+
XCTAssertTrue(dep.unmanagedValue == "master")
105+
106+
FileManager.setWorkingPath("..")
107+
108+
Git().remove("test-add")
109+
}
110+
111+
func testAddModuleUnmanagedNoArgs() {
112+
let status = Git().clone("[email protected]:modulo-dm/test-add.git", path: "test-add")
113+
XCTAssertTrue(status == .success)
114+
115+
FileManager.setWorkingPath("test-add")
116+
117+
let repoURL = "[email protected]:modulo-dm/test-add-update.git"
118+
119+
let result = Modulo.run(["add", repoURL, "--unmanaged", "-v"])
120+
XCTAssertTrue(result == .success)
121+
122+
123+
guard let spec = ModuleSpec.load(contentsOfFile: specFilename) else {
124+
XCTFail("Failed to get spec from file \(specFilename)")
125+
return }
126+
XCTAssertTrue(spec.dependencies.count > 0)
127+
guard let dep = spec.dependencyForURL(repoURL) else {
128+
XCTFail("Failed to find dependency for url \(repoURL) in spec \(spec)")
129+
return }
130+
XCTAssertNil(dep.version)
131+
XCTAssertTrue(dep.unmanaged)
132+
XCTAssertNil(dep.unmanagedValue)
105133

106134
FileManager.setWorkingPath("..")
107135

ModuloKitTests/TestDummyApp.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class TestDummyApp: XCTestCase {
4141
XCTAssertTrue(spec!.dependencyForURL("[email protected]:modulo-dm/test-add-update.git") != nil)
4242

4343
let checkedOut = Git().branchName("modules/test-add-update")
44-
XCTAssertTrue(checkedOut == "master")
44+
XCTAssertTrue(checkedOut == "master", "checkedOut should have been 'master' but was '\(String(describing: checkedOut))'")
4545

4646
XCTAssertTrue(FileManager.fileExists("modules/test-add-update"))
4747
XCTAssertTrue(FileManager.fileExists("modules/test-dep1"))

0 commit comments

Comments
 (0)