Skip to content

Commit 8a3f6a6

Browse files
committed
Support tracking dependencies by branch
In addition to `version` and all the features around that, we can also explicitly track a remote `branch` that will be fetched/updated when `modulo update` is run.
1 parent 85e88d3 commit 8a3f6a6

File tree

7 files changed

+116
-12
lines changed

7 files changed

+116
-12
lines changed

ModuloKit/Actions.swift

+18-3
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?, unmanaged: Bool) -> ErrorCode {
26-
let dep = DependencySpec(repositoryURL: url, version: version)
25+
open func addDependency(_ url: String, version: SemverRange?, branch: String?, unmanaged: Bool) -> ErrorCode {
26+
let dep = DependencySpec(repositoryURL: url, version: version, branch: branch)
2727
if var workingSpec = ModuleSpec.workingSpec() {
2828
// does this dep already exist in here??
2929
if let _ = workingSpec.dependencyForURL(url) {
@@ -77,6 +77,16 @@ open class Actions {
7777
exit(checkoutResult.errorMessage())
7878
}
7979
}
80+
if let branch = dep.branch {
81+
let checkoutResult = scm.checkout(branch: branch, path: clonePath)
82+
if checkoutResult != .success {
83+
exit(checkoutResult.errorMessage())
84+
}
85+
let pullResult = scm.pull(clonePath, remoteData: nil)
86+
if pullResult != .success {
87+
exit(pullResult.errorMessage())
88+
}
89+
}
8090

8191
// things worked, so add it to the approprate place in the overall state.
8292
if explicit {
@@ -106,8 +116,13 @@ open class Actions {
106116
if checkoutResult != .success {
107117
exit(checkoutResult.errorMessage())
108118
}
119+
} else if let branch = dep.branch {
120+
let checkoutResult = scm.checkout(branch: branch, path: clonePath)
121+
if checkoutResult != .success {
122+
exit(checkoutResult.errorMessage())
123+
}
109124
} else {
110-
exit("\(dep.name()) doesn't have a version and isn't unmanaged, not sure what to do.")
125+
exit("\(dep.name()) doesn't have a version, branch, and isn't marked as 'unmanaged', not sure what to do.")
111126
}
112127
}
113128
}

ModuloKit/Commands/AddCommand.swift

+10-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Foundation
1616
open class AddCommand: NSObject, Command {
1717
// internal properties
1818
fileprivate var version: SemverRange? = nil
19+
fileprivate var branch: String? = nil
1920
fileprivate var repositoryURL: String! = nil
2021
fileprivate var shouldUpdate: Bool = false
2122
fileprivate var unmanaged: Bool = false
@@ -40,6 +41,12 @@ open class AddCommand: NSObject, Command {
4041
self.version = SemverRange(value)
4142
}
4243
}
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+
}
4350

4451
addOption(["--unmanaged"], usage: "specifies that this module will be unmanaged") { (option, value) in
4552
self.unmanaged = true
@@ -57,8 +64,8 @@ open class AddCommand: NSObject, Command {
5764
open func execute(_ otherParams: Array<String>?) -> Int {
5865
let actions = Actions()
5966

60-
if version == nil && unmanaged == false {
61-
writeln(.stderr, "A version or range must be specified via --version, or --unmanaged must be used.")
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.")
6269
return ErrorCode.commandError.rawValue
6370
}
6471

@@ -69,7 +76,7 @@ open class AddCommand: NSObject, Command {
6976
}
7077
}
7178

72-
let result = actions.addDependency(repositoryURL, version: version, unmanaged: unmanaged)
79+
let result = actions.addDependency(repositoryURL, version: version, branch: branch, unmanaged: unmanaged)
7380
if result == .success {
7481
if shouldUpdate {
7582
writeln(.stdout, "Added \(String(describing: repositoryURL)).")

ModuloKit/SCM/Git.swift

+49-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ open class Git: SCM {
100100
let initialWorkingPath = FileManager.workingPath()
101101
FileManager.setWorkingPath(path)
102102

103-
let updateCommand = "git fetch --recurse-submodules --all --tags"
103+
let updateCommand = "git fetch --recurse-submodules --all"
104104
let status = runCommand(updateCommand)
105105

106106
FileManager.setWorkingPath(initialWorkingPath)
@@ -175,6 +175,54 @@ open class Git: SCM {
175175

176176
return .success
177177
}
178+
179+
open func checkout(branch: String, path: String) -> SCMResult {
180+
if !FileManager.fileExists(path) {
181+
return .error(code: 1, message: "Module path '\(path)' does not exist.")
182+
}
183+
184+
var checkoutCommand = ""
185+
186+
let initialWorkingPath = FileManager.workingPath()
187+
FileManager.setWorkingPath(path)
188+
189+
let existingBranches = branches(".")
190+
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
197+
}
198+
199+
checkoutCommand = "git checkout origin/\(branch) --quiet"
200+
201+
if neededFetch,
202+
let fetchResult = fetchResult,
203+
fetchResult != 0 {
204+
if verbose {
205+
writeln(.stderr, "Unable to find branch '\(branch)'.")
206+
}
207+
return .error(code: SCMDefaultError, message: "Unable to find a match for \(branch).")
208+
}
209+
210+
let status = runCommand(checkoutCommand)
211+
212+
let submodulesResult = collectAnySubmodules()
213+
214+
FileManager.setWorkingPath(initialWorkingPath)
215+
216+
if status != 0 {
217+
return .error(code: status, message: "Unable to checkout '\(branch)'.")
218+
}
219+
220+
if submodulesResult != .success {
221+
return submodulesResult
222+
}
223+
224+
return .success
225+
}
178226

179227
open func adjustIgnoreFile(pattern: String, removing: Bool) -> SCMResult {
180228
let localModulesPath = State.instance.modulePathName

ModuloKit/SCM/SCM.swift

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ 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
7980
func remove(_ path: String) -> SCMResult
8081
func adjustIgnoreFile(pattern: String, removing: Bool) -> SCMResult
8182
func checkStatus(_ path: String) -> SCMResult

ModuloKit/Specs/DependencySpec.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ public struct DependencySpec {
1717
var repositoryURL: String
1818
// version or version range
1919
var version: SemverRange?
20+
/// Branch to track
21+
var branch: String?
2022

2123
var unmanaged: Bool {
2224
get {
23-
return (version == nil)
25+
return (version == nil) && (branch == nil)
2426
}
2527
}
2628
}
@@ -29,7 +31,8 @@ extension DependencySpec: ELDecodable {
2931
public static func decode(_ json: JSON?) throws -> DependencySpec {
3032
return try DependencySpec(
3133
repositoryURL: json ==> "repositoryURL",
32-
version: json ==> "version"
34+
version: json ==> "version",
35+
branch: json ==> "branch"
3336
)
3437
}
3538

@@ -42,7 +45,8 @@ extension DependencySpec: ELEncodable {
4245
public func encode() throws -> JSON {
4346
return try encodeToJSON([
4447
"repositoryURL" <== repositoryURL,
45-
"version" <== version
48+
"version" <== version,
49+
"branch" <== branch
4650
])
4751
}
4852
}

ModuloKitTests/TestAdd.swift

+29
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,33 @@ class TestAdd: XCTestCase {
7878

7979
FileManager.setWorkingPath("..")
8080
}
81+
82+
func testAddModuleByBranch() {
83+
let status = Git().clone("[email protected]:modulo-dm/test-add.git", path: "test-add")
84+
XCTAssertTrue(status == .success)
85+
86+
FileManager.setWorkingPath("test-add")
87+
88+
let repoURL = "[email protected]:modulo-dm/test-add-update.git"
89+
90+
let result = Modulo.run(["add", repoURL, "--branch", "master", "-v"])
91+
XCTAssertTrue(result == .success)
92+
93+
94+
guard let spec = ModuleSpec.load(contentsOfFile: specFilename) else {
95+
XCTFail("Failed to get spec from file \(specFilename)")
96+
return }
97+
XCTAssertTrue(spec.dependencies.count > 0)
98+
guard let dep = spec.dependencyForURL(repoURL) else {
99+
XCTFail("Failed to find dependency for url \(repoURL) in spec \(spec)")
100+
return }
101+
XCTAssertNil(dep.version)
102+
XCTAssertFalse(dep.unmanaged)
103+
XCTAssertNotNil(dep.branch)
104+
XCTAssertTrue(dep.branch == "master")
105+
106+
FileManager.setWorkingPath("..")
107+
108+
Git().remove("test-add")
109+
}
81110
}

modulo.xcodeproj/xcshareddata/xcschemes/modulo.xcscheme

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@
6969
</CommandLineArgument>
7070
<CommandLineArgument
7171
argument = "defaults --set --alwaysVerbose true"
72-
isEnabled = "YES">
72+
isEnabled = "NO">
7373
</CommandLineArgument>
7474
<CommandLineArgument
7575
argument = "update --verbose"
76-
isEnabled = "NO">
76+
isEnabled = "YES">
7777
</CommandLineArgument>
7878
<CommandLineArgument
7979
argument = "update"

0 commit comments

Comments
 (0)