feat: add Rundeck Runner support and repository-named subdirectories#44
feat: add Rundeck Runner support and repository-named subdirectories#44elioe wants to merge 5 commits into
Conversation
elioe
commented
May 20, 2026
- Implement ProxyRunnerPlugin and ProxySecretBundleCreator for secure distributed execution.
- Add gitUseRepoNameSubdirectory option to clone under base directory using the git repo name.
- Clean up temporary SSH keys on factory close and fix hard reset behavior in GitManager.
- Add Spock unit tests for secret bundling and repo name extraction.
- Implement ProxyRunnerPlugin and ProxySecretBundleCreator for secure distributed execution. - Add gitUseRepoNameSubdirectory option to clone under base directory using the git repo name. - Clean up temporary SSH keys on factory close and fix hard reset behavior in GitManager. - Add Spock unit tests for secret bundling and repo name extraction.
There was a problem hiding this comment.
Pull request overview
This PR adds Rundeck Runner compatibility to the Git workflow steps by implementing runner/secret-bundling interfaces, introduces an option to clone into a repository-named subdirectory under the configured base directory, and improves SSH key lifecycle management by explicitly closing/deleting temporary key material after JGit transport operations.
Changes:
- Implement
ProxyRunnerPlugin+ProxySecretBundleCreatorfor Git Clone/Commit/Push steps, backed by new secret path/bundle helpers inGitPluginUtil. - Add
gitUseRepoNameSubdirectoryoption and repo-name extraction utility (extractRepoName) to support repository-named clone directories. - Improve JGit SSH transport cleanup by making
PluginSshSessionFactoryCloseableand ensuring factories are closed after clone/pull/push; add Spock tests for secret bundling, repo name extraction, and SSH temp key cleanup.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/groovy/com/rundeck/plugin/util/GitPluginUtil.groovy | Adds repo name extraction and shared secret-path/secret-bundle helpers for Runner support. |
| src/main/groovy/com/rundeck/plugin/GitCloneWorkflowStep.groovy | Adds Runner interfaces, repo-name subdir option handling, and secret bundle/path delegation. |
| src/main/groovy/com/rundeck/plugin/GitCommitWorkflowStep.groovy | Adds Runner interfaces, repo-name subdir option handling, and secret bundle/path delegation. |
| src/main/groovy/com/rundeck/plugin/GitPushWorkflowStep.groovy | Adds Runner interfaces, repo-name subdir option handling, and secret bundle/path delegation. |
| src/main/groovy/com/rundeck/plugin/util/PluginSshSessionFactory.groovy | Makes SSH session factory reusable/closeable and deletes temp key files on close. |
| src/main/groovy/com/rundeck/plugin/GitManager.groovy | Adds hard reset/clean before pull and ensures SSH factories are closed after operations. |
| src/test/groovy/com/rundeck/plugin/WorkflowStepSecretBundleSpec.groovy | Tests workflow steps implement runner/secret-bundle interfaces and return/bundle secrets correctly. |
| src/test/groovy/com/rundeck/plugin/util/GitPluginUtilSecretBundleSpec.groovy | Unit tests for secret-path resolution and secret bundle preparation behavior. |
| src/test/groovy/com/rundeck/plugin/util/GitPluginUtilExtractRepoNameSpec.groovy | Parameterized tests for extracting repo names from various Git URL formats. |
| src/test/groovy/com/rundeck/plugin/util/PluginSshSessionFactorySpec.groovy | Updates behavior expectation (factory reuse) and adds tests for idempotent close + temp key deletion. |
Comments suppressed due to low confidence (1)
src/main/groovy/com/rundeck/plugin/GitManager.groovy:209
- The success/failure logging here is incorrect: the
elsebranch logs the same "not successful" message as the failure branch. This makes logs misleading during normal successful pulls.
if (!result.isSuccessful()) {
logger.info("Pull is not successful.")
} else {
logger.debug("Pull is not successful.")
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| static String extractRepoName(String gitUrl) { | ||
| if (!gitUrl) return null | ||
| String cleaned = gitUrl.replaceAll('/+$', '') | ||
| int lastSlash = cleaned.lastIndexOf('/') | ||
| int lastColon = cleaned.lastIndexOf(':') | ||
| int lastSep = Math.max(lastSlash, lastColon) | ||
| String name = lastSep >= 0 ? cleaned.substring(lastSep + 1) : cleaned | ||
| if (name.endsWith('.git')) { | ||
| name = name.substring(0, name.length() - 4) | ||
| } | ||
| return name ?: null | ||
| } |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/main/groovy/com/rundeck/plugin/GitManager.groovy:209
- The pull result logging is incorrect: the
elsebranch logs "Pull is not successful." even whenresult.isSuccessful()is true, which will mislead users and make troubleshooting harder.
if (!result.isSuccessful()) {
logger.info("Pull is not successful.")
} else {
logger.debug("Pull 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 != RemoteRefUpdate.Status.OK } |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/main/groovy/com/rundeck/plugin/GitManager.groovy:209
- The success branch logs the same "Pull is not successful" message as the failure branch, which will mislead troubleshooting/log parsing. Update the else branch to indicate success (and optionally tweak wording on the failure branch).
if (!result.isSuccessful()) {
logger.info("Pull is not successful.")
} else {
logger.debug("Pull is not successful.")
}
| } catch (Exception e) { | ||
| logger.error("Failed to prepare secret bundle for key path '{}': {}", path, e.message) | ||
| } |
| try { | ||
| sessionFactory.close() | ||
| } catch (Exception ignored) { | ||
| } | ||
| sessionFactory.deleteTempKey() | ||
| sessionFactory = null |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/main/groovy/com/rundeck/plugin/GitManager.groovy:209
- The success branch logs the same "Pull is not successful." message as the failure branch, which makes pull outcomes misleading in logs. Update the else log message to indicate success.
if (!result.isSuccessful()) {
logger.info("Pull is not successful.")
} else {
logger.debug("Pull is not successful.")
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>