Skip to content

Commit

Permalink
#43: Inform user, if new version of an already discovered artifact is…
Browse files Browse the repository at this point in the history
… available (#44)

* #43 Rename canSkip to isAlreadyLatestVersion

- also use filterNot instead of negation in method

* #43 Refactoring: Use logger in mojo

- instead of println

* #43 Add test for isAlreadyLatestVersion

Co-Authored-By: Sandra Parsick <[email protected]>

* #43: Add UpdateBranch value, extract update candidates

Co-Authored-By: Sandra Parsick <[email protected]>

* #43 Refactoring: Add UpdateBranchName data class

- also extract update candidates

Co-Authored-By: Sandra Parsick <[email protected]>

* #43 Refactoring: Use prefix() in toString()

- also add TODO for next steps

Co-Authored-By: Sandra Parsick <[email protected]>

* Add Codespaces Settings

* update init command

* Move dependency:go-offline to init

* #43 Fix test after rebase

* #43 Remove Gitpod and Devcontainer files

* #43 Add check if remote branch with prefix exists

Co-Authored-By: Sandra Parsick <[email protected]>

* #43: Add skipping dependency update if a branch already exists for a previous version
Co-Authored-By: Georg Berky <[email protected]>

Co-authored-by: Sandra Parsick <[email protected]>
Co-authored-by: Sandra Parsick <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2022
1 parent 122ce21 commit 24da1c9
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 102 deletions.
5 changes: 0 additions & 5 deletions .gitpod.Dockerfile

This file was deleted.

12 changes: 0 additions & 12 deletions .gitpod.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package io.github.georgberky.maven.plugins.depsupdate

interface GitProvider : AutoCloseable {

fun hasRemoteBranch(remoteBranchName: String) : Boolean

fun hasRemoteBranch(remoteBranchName: String): Boolean
fun hasRemoteBranchWithPrefix(remoteBranchNamePrefix: String): Boolean
fun checkoutNewBranch(branchName: String)
fun add(filePattern: String)
fun commit(author: String, email: String, message: String)
fun push(localBranchName: String)
fun checkoutInitialBranch()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.georgberky.maven.plugins.depsupdate

import com.jcraft.jsch.Session
import org.apache.maven.settings.Settings
import org.bouncycastle.cms.RecipientId.password
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ListBranchCommand
import org.eclipse.jgit.api.TransportConfigCallback
Expand All @@ -12,73 +13,80 @@ import org.eclipse.jgit.util.FS
import java.nio.file.Path

class JGitProvider(localRepositoryPath: Path, val settings: Settings, val connection: String) : GitProvider {
val git : Git = Git(
RepositoryBuilder()
.readEnvironment()
.findGitDir(localRepositoryPath.toFile())
.setMustExist(true)
.build())
val git: Git = Git(
RepositoryBuilder()
.readEnvironment()
.findGitDir(localRepositoryPath.toFile())
.setMustExist(true)
.build()
)

private val initialBranch: String = git.repository.branch

override fun hasRemoteBranch(remoteBranchName: String) =
git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call().any { it.name == "refs/remotes/origin/$remoteBranchName" }
override fun hasRemoteBranch(remoteBranchName: String): Boolean {
return git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call()
.any { it.name == "refs/remotes/origin/$remoteBranchName" }
}

override fun hasRemoteBranchWithPrefix(remoteBranchNamePrefix: String): Boolean {
return git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call()
.any { it.name.startsWith("refs/remotes/origin/$remoteBranchNamePrefix") }
}

override fun checkoutNewBranch(newBranchName: String) {
git
.checkout()
.setStartPoint(git.repository.fullBranch)
.setCreateBranch(true)
.setName(newBranchName)
.call()
git.checkout()
.setStartPoint(git.repository.fullBranch)
.setCreateBranch(true)
.setName(newBranchName)
.call()

}

override fun add(filePattern: String) {
git.add()
.addFilepattern(filePattern)
.call()
.addFilepattern(filePattern)
.call()
}

override fun commit(author: String, email: String, message: String) {
git.commit()
.setAuthor(PersonIdent(author, email))
.setMessage(message)
.call()
.setAuthor(PersonIdent(author, email))
.setMessage(message)
.call()
}

override fun push(branchName: String) {
git
.push()
.setRefSpecs(RefSpec("$branchName:$branchName"))
.apply {
val uri = URIish(connection.removePrefix("scm:git:"))

when (uri.scheme) {
"https" -> {
val (username, password) = usernamePassword(uri)

setCredentialsProvider(UsernamePasswordCredentialsProvider(username, password))
}
else -> {
setTransportConfigCallback(TransportConfigCallback { transport ->
if (transport !is SshTransport) return@TransportConfigCallback

transport.sshSessionFactory = object : JschConfigSessionFactory() {
override fun configure(hc: OpenSshConfig.Host?, session: Session?) {
}
git.push()
.setRefSpecs(RefSpec("$branchName:$branchName"))
.apply {
val uri = URIish(connection.removePrefix("scm:git:"))

when (uri.scheme) {
"https" -> {
val (username, password) = usernamePassword(uri)

setCredentialsProvider(UsernamePasswordCredentialsProvider(username, password))
}

override fun createDefaultJSch(fs: FS?) =
settings.getServer(uri.host).run {
super.createDefaultJSch(fs).apply { addIdentity(privateKey, passphrase) }
}
else -> {
setTransportConfigCallback(TransportConfigCallback { transport ->
if (transport !is SshTransport) return@TransportConfigCallback

transport.sshSessionFactory = object : JschConfigSessionFactory() {
override fun configure(hc: OpenSshConfig.Host?, session: Session?) {
}
})
}

override fun createDefaultJSch(fs: FS?) =
settings.getServer(uri.host).run {
super.createDefaultJSch(fs).apply { addIdentity(privateKey, passphrase) }
}
}
})
}
}
.setPushOptions(listOf("merge_request.create"))
.call()
}
.setPushOptions(listOf("merge_request.create"))
.call()
}

override fun checkoutInitialBranch() {
Expand All @@ -94,5 +102,5 @@ class JGitProvider(localRepositoryPath: Path, val settings: Settings, val connec
}

private fun usernamePassword(uri: URIish) =
if (uri.user != null) uri.user to uri.pass else settings.getServer(uri.host).run { username to password }
}
if (uri.user != null) uri.user to uri.pass else settings.getServer(uri.host).run { username to password }
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ open class NativeGitProvider(val localRepositoryDirectory: Path) : GitProvider {
return processOutput.contains("remotes/origin/" + remoteBranchName)
}

override fun hasRemoteBranchWithPrefix(remoteBranchNamePrefix: String): Boolean {
val processResult = runInProcessWithOutput(gitCommand, "branch", "--all")
val processOutput = processResult.second
return processOutput.lines()
.map { it.trim() }
.any { it.startsWith("remotes/origin/" + remoteBranchNamePrefix) }
}

override fun checkoutNewBranch(newBranchName: String) {
runInProcess(gitCommand, "checkout", "-b", newBranchName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,28 @@ import org.apache.maven.settings.Settings
class UpdateMojo : AbstractMojo() {
@Parameter(defaultValue = "\${project}", required = true)
lateinit var mavenProject: MavenProject

@Parameter(defaultValue = "\${localRepository}", required = true)
lateinit var localRepository: ArtifactRepository

@Parameter(defaultValue = "\${settings}", required = true)
lateinit var settings: Settings

@Parameter(property = "connectionUrl", defaultValue = "\${project.scm.connection}")
lateinit var connectionUrl: String

@Parameter(property = "developerConnectionUrl", defaultValue = "\${project.scm.developerConnection}")
lateinit var developerConnectionUrl: String

@Parameter(property = "connectionType", defaultValue = "connection", required = true)
lateinit var connectionType: String
@Parameter(property = "dependencyUpdate.git.provider", defaultValue="NATIVE", required = false)
lateinit var gitProvider : GitProviderChoice

@Parameter(property = "dependencyUpdate.git.provider", defaultValue = "NATIVE", required = false)
lateinit var gitProvider: GitProviderChoice

@Component
lateinit var artifactFactory: ArtifactFactory

@Component
lateinit var artifactMetadataSource: ArtifactMetadataSource

Expand All @@ -37,37 +44,62 @@ class UpdateMojo : AbstractMojo() {

override fun execute() {
withGit { git ->
UpdateResolver(
mavenProject = mavenProject,
artifactMetadataSource = artifactMetadataSource,
localRepository = localRepository,
artifactFactory = artifactFactory
val updateCandidates = UpdateResolver(
mavenProject = mavenProject,
artifactMetadataSource = artifactMetadataSource,
localRepository = localRepository,
artifactFactory = artifactFactory
)
.updates
.onEach { println("execute im mojo: latestVersion: '${it.latestVersion}' / version:'${it.version}'") }
.filter(VersionUpdate::canSkip)
.onEach { println("execute im mojo canSkip: ${it.latestVersion}") }
.map { it to "dependency-update/${it.groupId}-${it.artifactId}-${it.latestVersion}" }
.onEach { println("execute im mojo canSkip (nach map): ${it.second} ${it.first}") }
.filter { (_, branchName) -> !git.hasRemoteBranch(branchName) }
.onEach { println("execute im mojo nach filter branches: ${it.second} ${it.first}") }
.forEach { (update, branchName) ->
git.checkoutNewBranch(branchName)
val pom = update.updatedPom()
mavenProject.file.writeText(pom.html())
git.add("pom.xml")
git.commit(
"dependency-update-bot","",
"Bump ${update.artifactId} from ${update.version} to ${update.latestVersion}"
)
git.push(branchName)
git.checkoutInitialBranch()
}
.updates
.onEach { log.debug("execute in mojo: latestVersion: '${it.latestVersion}' / version:'${it.version}'") }
.filterNot(VersionUpdate::isAlreadyLatestVersion)
.onEach { log.debug("execute in mojo after latest version check: ${it.latestVersion}") }
.map { it to UpdateBranchName(it.groupId, it.artifactId, it.latestVersion) }
.onEach { log.debug("execute in mojo canSkip (after map): ${it.second} ${it.first}") }

val updateCandidatesWithExistingUpdateBranch = updateCandidates
.filter { (_, branchName) -> git.hasRemoteBranchWithPrefix(branchName.prefix()) }

updateCandidatesWithExistingUpdateBranch
.forEach { (_, branchName) ->
log.warn(
"Dependency '${branchName.prefix()}' already has a branch for a previous version update. " +
"Please merge it first"
)
}

val updatesWithoutBranchForVersion =
updateCandidates.filterNot { (_, branchName) -> git.hasRemoteBranchWithPrefix(branchName.prefix()) }

updatesWithoutBranchForVersion
.onEach { log.debug("execute in mojo after filter branches: ${it.second} ${it.first}") }
.forEach { (update, branchName) ->
git.checkoutNewBranch(branchName.toString())
val pom = update.updatedPom()
mavenProject.file.writeText(pom.html())
git.add("pom.xml")
git.commit(
"dependency-update-bot", "",
"Bump ${update.artifactId} from ${update.version} to ${update.latestVersion}"
)
git.push(branchName.toString())
git.checkoutInitialBranch()
}
}
}

private fun withGit(f: (GitProvider) -> Unit) {
val git = gitProvider.createProvider(mavenProject.basedir.toPath(), settings, connection);
git.use(f)
}
}

data class UpdateBranchName(val groupId: String, val artifactId: String, val version: String) {
fun prefix(): String {
return "dependency-update/${groupId}-${artifactId}"
}

override fun toString(): String {
return "${prefix()}-${version}"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ abstract class VersionUpdate(
) {
abstract fun updatedPom() : Document

fun canSkip() : Boolean {
fun isAlreadyLatestVersion() : Boolean {
// TODO #12: use DI-injected logger
println("canSkip: ${latestVersion} compare to ${version}")
println("latestVersion.equals(version) = ${latestVersion.equals(version)}")
return latestVersion != "null" && latestVersion != version
return latestVersion != "null" && latestVersion == version
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal class DependencyVersionUpdateTest {
val depUpdate = DependencyVersionUpdate("org.junit.jupiter", "junit-jupiter", "5.5.2", "null", pom)

// when
val canSkip = depUpdate.canSkip()
val canSkip = depUpdate.isAlreadyLatestVersion()

// then
assertThat(canSkip)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,18 @@ abstract class GitProviderTest() {
providerUnderTest.checkoutInitialBranch()

assertThat(localGitRepo.repository.branch).isEqualTo(initialBranch)
}

@Test
fun `has remote branch with prefix`() {
remoteGitRepo.branchCreate().setName("branchName-suffix").call()
localGitRepo.fetch().call()

val hasRemoteBranchWithPrefix = providerUnderTest.hasRemoteBranchWithPrefix("branchName");

assertThat(hasRemoteBranchWithPrefix)
.describedAs("remote branch with prefix should exist")
.isTrue()
}

private fun setupLocalGitRepoAsCloneOf(remoteGitDirectory: URI) {
Expand All @@ -117,4 +128,4 @@ abstract class GitProviderTest() {
remoteGitRepo.add().addFilepattern(fileToCommit.name).call()
remoteGitRepo.commit().setAuthor("Georg", "[email protected]").setMessage("init").call()
}
}
}
Loading

0 comments on commit 24da1c9

Please sign in to comment.