Skip to content
26 changes: 25 additions & 1 deletion src/main/groovy/com/rundeck/plugin/GitCloneWorkflowStep.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.rundeck.plugin

import com.dtolabs.rundeck.core.execution.ExecutionListener
import com.dtolabs.rundeck.core.execution.ExecutionContext
import com.dtolabs.rundeck.core.execution.proxy.ProxyRunnerPlugin
import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator
import com.dtolabs.rundeck.core.execution.proxy.SecretBundle
import com.dtolabs.rundeck.core.execution.workflow.steps.StepException
import com.dtolabs.rundeck.core.plugins.Plugin
import com.dtolabs.rundeck.core.plugins.configuration.Describable
Expand All @@ -21,7 +25,7 @@ import groovy.json.JsonOutput

@Plugin(name = GitCloneWorkflowStep.PROVIDER_NAME, service = ServiceNameConstants.WorkflowStep)
@PluginDescription(title = GitCloneWorkflowStep.PROVIDER_TITLE, description = GitCloneWorkflowStep.PROVIDER_DESCRIPTION)
class GitCloneWorkflowStep implements StepPlugin, Describable{
class GitCloneWorkflowStep implements StepPlugin, Describable, ProxyRunnerPlugin, ProxySecretBundleCreator{
public static final String PROVIDER_NAME = "git-clone-step"
public static final String PROVIDER_TITLE = "Git / Clone"
public static final String PROVIDER_DESCRIPTION ="Clone a Git repository on Rundeck server"
Expand All @@ -34,6 +38,7 @@ class GitCloneWorkflowStep implements StepPlugin, Describable{
public final static String GIT_KEY_STORAGE="gitKeyPath"
public final static String GIT_PASSWORD_STORAGE="gitPasswordPath"
public final static String GIT_PROJECT_BASED_SUBDIRECTORY="gitUseProjectBasedSubdirectory"
public final static String GIT_REPO_NAME_SUBDIRECTORY="gitUseRepoNameSubdirectory"


final static Map<String, Object> renderingOptionsAuthentication = GitPluginUtil.getRenderOpt("Authentication", false)
Expand Down Expand Up @@ -74,6 +79,8 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil
null,null,null, renderingOptionsAuthenticationKey))
.property(PropertyUtil.bool(GIT_PROJECT_BASED_SUBDIRECTORY, "Use per-project subdirectories", "Check repositories out in project-based subdirectories of the Rundeck home directory.",
false, "false", PropertyScope.Project, renderingOptionsConfig))
.property(PropertyUtil.bool(GIT_REPO_NAME_SUBDIRECTORY, "Clone into repo name subdirectory", "Clone into a subdirectory named after the Git repository under the base directory.",
false, "false", null, renderingOptionsConfig))
.build()

GitCloneWorkflowStep() {
Expand Down Expand Up @@ -104,6 +111,13 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil
localPath = configuration.get(GIT_BASE_DIRECTORY)
}

if (Boolean.parseBoolean((String) configuration.get(GIT_REPO_NAME_SUBDIRECTORY))) {
String repoName = GitPluginUtil.extractRepoName((String) configuration.get(GIT_URL))
if (repoName) {
localPath = new File(localPath, repoName).getPath()
}
}

if(configuration.get(GIT_PASSWORD_STORAGE)){
def password = GitPluginUtil.getFromKeyStorage(configuration.get(GIT_PASSWORD_STORAGE), context)
gitManager.setGitPassword(password)
Expand Down Expand Up @@ -146,4 +160,14 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil


}

@Override
List<String> listSecretsPathWorkflowStep(ExecutionContext context, Map<String, Object> configuration) {
return GitPluginUtil.listSecretsPathForStep(context, configuration, GIT_PASSWORD_STORAGE, GIT_KEY_STORAGE)
}

@Override
SecretBundle prepareSecretBundleWorkflowStep(ExecutionContext context, Map<String, Object> configuration) {
return GitPluginUtil.prepareSecretBundleForStep(context, configuration, GIT_PASSWORD_STORAGE, GIT_KEY_STORAGE)
}
}
26 changes: 25 additions & 1 deletion src/main/groovy/com/rundeck/plugin/GitCommitWorkflowStep.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.rundeck.plugin

import com.dtolabs.rundeck.core.execution.ExecutionListener
import com.dtolabs.rundeck.core.execution.ExecutionContext
import com.dtolabs.rundeck.core.execution.proxy.ProxyRunnerPlugin
import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator
import com.dtolabs.rundeck.core.execution.proxy.SecretBundle
import com.dtolabs.rundeck.core.execution.workflow.steps.StepException
import com.dtolabs.rundeck.core.plugins.Plugin
import com.dtolabs.rundeck.core.plugins.configuration.Describable
Expand All @@ -21,7 +25,7 @@ import groovy.json.JsonOutput

@Plugin(name = GitCommitWorkflowStep.PROVIDER_NAME, service = ServiceNameConstants.WorkflowStep)
@PluginDescription(title = GitCommitWorkflowStep.PROVIDER_TITLE, description = GitCommitWorkflowStep.PROVIDER_DESCRIPTION)
class GitCommitWorkflowStep implements StepPlugin, Describable{
class GitCommitWorkflowStep implements StepPlugin, Describable, ProxyRunnerPlugin, ProxySecretBundleCreator{
public static final String PROVIDER_NAME = "git-commit-step"
public static final String PROVIDER_TITLE = "Git / Commit"
public static final String PROVIDER_DESCRIPTION ="Commit a Git repository on Rundeck server"
Expand All @@ -36,6 +40,7 @@ class GitCommitWorkflowStep implements StepPlugin, Describable{
public final static String GIT_KEY_STORAGE="gitKeyPath"
public final static String GIT_PASSWORD_STORAGE="gitPasswordPath"
public final static String GIT_PROJECT_BASED_SUBDIRECTORY="gitUseProjectBasedSubdirectory"
public final static String GIT_REPO_NAME_SUBDIRECTORY="gitUseRepoNameSubdirectory"


final static Map<String, Object> renderingOptionsAuthentication = GitPluginUtil.getRenderOpt("Authentication", false)
Expand Down Expand Up @@ -80,6 +85,8 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil
null,null,null, renderingOptionsAuthenticationKey))
.property(PropertyUtil.bool(GIT_PROJECT_BASED_SUBDIRECTORY, "Use per-project subdirectories", "Check repositories out in project-based subdirectories of the Rundeck home directory.",
false, "false", PropertyScope.Project, renderingOptionsConfig))
.property(PropertyUtil.bool(GIT_REPO_NAME_SUBDIRECTORY, "Clone into repo name subdirectory", "Use a subdirectory named after the Git repository under the base directory.",
false, "false", null, renderingOptionsConfig))
.build()

GitCommitWorkflowStep() {
Expand Down Expand Up @@ -110,6 +117,13 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil
localPath = configuration.get(GIT_BASE_DIRECTORY)
}

if (Boolean.parseBoolean((String) configuration.get(GIT_REPO_NAME_SUBDIRECTORY))) {
String repoName = GitPluginUtil.extractRepoName((String) configuration.get(GIT_URL))
if (repoName) {
localPath = new File(localPath, repoName).getPath()
}
}

if(configuration.get(GIT_PASSWORD_STORAGE)){
def password = GitPluginUtil.getFromKeyStorage(configuration.get(GIT_PASSWORD_STORAGE), context)
gitManager.setGitPassword(password)
Expand Down Expand Up @@ -156,4 +170,14 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil


}

@Override
List<String> listSecretsPathWorkflowStep(ExecutionContext context, Map<String, Object> configuration) {
return GitPluginUtil.listSecretsPathForStep(context, configuration, GIT_PASSWORD_STORAGE, GIT_KEY_STORAGE)
}

@Override
SecretBundle prepareSecretBundleWorkflowStep(ExecutionContext context, Map<String, Object> configuration) {
return GitPluginUtil.prepareSecretBundleForStep(context, configuration, GIT_PASSWORD_STORAGE, GIT_KEY_STORAGE)
}
}
45 changes: 35 additions & 10 deletions src/main/groovy/com/rundeck/plugin/GitManager.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.rundeck.plugin
import com.rundeck.plugin.util.PluginSshSessionFactory
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.PullResult
import org.eclipse.jgit.api.ResetCommand
import org.eclipse.jgit.transport.PushResult
import org.eclipse.jgit.api.TransportCommand
import org.eclipse.jgit.lib.Repository
Expand Down Expand Up @@ -115,6 +116,8 @@ class GitManager {
logger.debug("branch differs, re-cloning")
needsClone = true
} else {
agit.reset().setMode(ResetCommand.ResetType.HARD).call()
agit.clean().setCleanDirectories(true).setForce(true).call()
performPull(agit)
}

Expand Down Expand Up @@ -175,14 +178,16 @@ class GitManager {
setURI(this.gitURL).
setCloneSubmodules(true)


PluginSshSessionFactory sshFactory = null
try {
setupTransportAuthentication(sshConfig, cloneCommand, this.gitURL)
sshFactory = setupTransportAuthentication(sshConfig, cloneCommand, this.gitURL)
git = withPluginClassLoader { cloneCommand.call() }
} catch (Exception e) {
e.printStackTrace()
logger.debug("Failed cloning the repository from ${this.gitURL}: ${e.message}", e)
throw new Exception("Failed cloning the repository from ${this.gitURL}: ${e.message}", e)
} finally {
sshFactory?.close()
}
repo = git.getRepository()
}
Expand All @@ -193,8 +198,9 @@ class GitManager {
def pullCommand = git.pull()
.setRebase(true)

PluginSshSessionFactory sshFactory = null
try {
setupTransportAuthentication(sshConfig, pullCommand, this.gitURL)
sshFactory = setupTransportAuthentication(sshConfig, pullCommand, this.gitURL)
PullResult result = withPluginClassLoader { pullCommand.call() }
if (!result.isSuccessful()) {
logger.info("Pull is not successful.")
Expand All @@ -205,6 +211,8 @@ class GitManager {
e.printStackTrace()
logger.debug("Failed pulling the repository from ${this.gitURL}: ${e.message}", e)
throw new Exception("Failed pulling the repository from ${this.gitURL}: ${e.message}", e)
} finally {
sshFactory?.close()
}
repo = git.getRepository()
}
Expand All @@ -213,14 +221,23 @@ class GitManager {
def pushCommand = git.push()
.setPushAll()

PluginSshSessionFactory sshFactory = null
try {
setupTransportAuthentication(sshConfig, pushCommand, this.gitURL)
withPluginClassLoader { pushCommand.call() }
logger.info("Push is not successful.")
sshFactory = setupTransportAuthentication(sshConfig, pushCommand, this.gitURL)
def results = withPluginClassLoader { pushCommand.call() }
def updates = results.collectMany { it.remoteUpdates ?: [] }
def failed = updates.findAll { !(it.status in [RemoteRefUpdate.Status.OK, RemoteRefUpdate.Status.UP_TO_DATE]) }
if (failed) {
throw new Exception("Push had failed updates: ${failed}")
} else {
logger.debug("Push successful.")
}
} catch (Exception e) {
e.printStackTrace()
logger.debug("Failed pushing the repository to ${this.gitURL}: ${e.message}", e)
throw new Exception("Failed pushing the repository to ${this.gitURL}: ${e.message}", e)
} finally {
sshFactory?.close()
}
}

Expand Down Expand Up @@ -254,7 +271,7 @@ class GitManager {
}
}

void setupTransportAuthentication(
PluginSshSessionFactory setupTransportAuthentication(
Map<String, String> sshConfig,
TransportCommand command,
String url = null) throws Exception {
Expand Down Expand Up @@ -283,19 +300,25 @@ class GitManager {
def factory = new PluginSshSessionFactory(keyData)
factory.sshConfig = sshConfig
command.setTransportConfigCallback(factory)
return factory
} else if (u.user && gitPassword) {
logger.debug("using password")

if (null != gitPassword && gitPassword.length() > 0) {
command.setCredentialsProvider(new UsernamePasswordCredentialsProvider(u.user, gitPassword))
}
}
return null
}

PullResult gitPull(Git git1 = null) {
def pullCommand = (git1 ?: git).pull().setRemote(REMOTE_NAME).setRemoteBranchName(branch)
setupTransportAuthentication(sshConfig, pullCommand)
withPluginClassLoader { pullCommand.call() }
PluginSshSessionFactory sshFactory = setupTransportAuthentication(sshConfig, pullCommand)
try {
return withPluginClassLoader { pullCommand.call() }
} finally {
sshFactory?.close()
}
}

def gitCommitAndPush() {
Expand All @@ -317,14 +340,16 @@ class GitManager {
def pushb = git.push()
pushb.setRemote(REMOTE_NAME)
pushb.add(branch)
setupTransportAuthentication(sshConfig, pushb)
PluginSshSessionFactory sshFactory = setupTransportAuthentication(sshConfig, pushb)

def push
try {
push = withPluginClassLoader { pushb.call() }
} catch (Exception e) {
logger.debug("Failed push to remote: ${e.message}", e)
throw new Exception("Failed push to remote: ${e.message}", e)
} finally {
sshFactory?.close()
}
def sb = new StringBuilder()
def updates = (push*.remoteUpdates).flatten()
Expand Down
26 changes: 25 additions & 1 deletion src/main/groovy/com/rundeck/plugin/GitPushWorkflowStep.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.rundeck.plugin

import com.dtolabs.rundeck.core.execution.ExecutionListener
import com.dtolabs.rundeck.core.execution.ExecutionContext
import com.dtolabs.rundeck.core.execution.proxy.ProxyRunnerPlugin
import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator
import com.dtolabs.rundeck.core.execution.proxy.SecretBundle
import com.dtolabs.rundeck.core.execution.workflow.steps.StepException
import com.dtolabs.rundeck.core.plugins.Plugin
import com.dtolabs.rundeck.core.plugins.configuration.Describable
Expand All @@ -21,7 +25,7 @@ import groovy.json.JsonOutput

@Plugin(name = GitPushWorkflowStep.PROVIDER_NAME, service = ServiceNameConstants.WorkflowStep)
@PluginDescription(title = GitPushWorkflowStep.PROVIDER_TITLE, description = GitPushWorkflowStep.PROVIDER_DESCRIPTION)
class GitPushWorkflowStep implements StepPlugin, Describable{
class GitPushWorkflowStep implements StepPlugin, Describable, ProxyRunnerPlugin, ProxySecretBundleCreator{
public static final String PROVIDER_NAME = "git-push-step"
public static final String PROVIDER_TITLE = "Git / Push"
public static final String PROVIDER_DESCRIPTION ="Push a Git repository on Rundeck server"
Expand All @@ -33,6 +37,7 @@ class GitPushWorkflowStep implements StepPlugin, Describable{
public final static String GIT_KEY_STORAGE="gitKeyPath"
public final static String GIT_PASSWORD_STORAGE="gitPasswordPath"
public final static String GIT_PROJECT_BASED_SUBDIRECTORY="gitUseProjectBasedSubdirectory"
public final static String GIT_REPO_NAME_SUBDIRECTORY="gitUseRepoNameSubdirectory"


final static Map<String, Object> renderingOptionsAuthentication = GitPluginUtil.getRenderOpt("Authentication", false)
Expand Down Expand Up @@ -71,6 +76,8 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil
null,null,null, renderingOptionsAuthenticationKey))
.property(PropertyUtil.bool(GIT_PROJECT_BASED_SUBDIRECTORY, "Use per-project subdirectories", "Check repositories out in project-based subdirectories of the Rundeck home directory.",
false, "false", PropertyScope.Project, renderingOptionsConfig))
.property(PropertyUtil.bool(GIT_REPO_NAME_SUBDIRECTORY, "Clone into repo name subdirectory", "Use a subdirectory named after the Git repository under the base directory.",
false, "false", null, renderingOptionsConfig))
.build()

GitPushWorkflowStep() {
Expand Down Expand Up @@ -101,6 +108,13 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil
localPath = configuration.get(GIT_BASE_DIRECTORY)
}

if (Boolean.parseBoolean((String) configuration.get(GIT_REPO_NAME_SUBDIRECTORY))) {
String repoName = GitPluginUtil.extractRepoName((String) configuration.get(GIT_URL))
if (repoName) {
localPath = new File(localPath, repoName).getPath()
}
}

if(configuration.get(GIT_PASSWORD_STORAGE)){
def password = GitPluginUtil.getFromKeyStorage(configuration.get(GIT_PASSWORD_STORAGE), context)
gitManager.setGitPassword(password)
Expand Down Expand Up @@ -139,4 +153,14 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil


}

@Override
List<String> listSecretsPathWorkflowStep(ExecutionContext context, Map<String, Object> configuration) {
return GitPluginUtil.listSecretsPathForStep(context, configuration, GIT_PASSWORD_STORAGE, GIT_KEY_STORAGE)
}

@Override
SecretBundle prepareSecretBundleWorkflowStep(ExecutionContext context, Map<String, Object> configuration) {
return GitPluginUtil.prepareSecretBundleForStep(context, configuration, GIT_PASSWORD_STORAGE, GIT_KEY_STORAGE)
}
}
Loading