Skip to content

Commit c7c2399

Browse files
committed
Merge remote-tracking branch 'origin/main' into cve-fixes
2 parents 09617be + 26216a7 commit c7c2399

10 files changed

Lines changed: 343 additions & 32 deletions

File tree

.github/copilot-instructions.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!-- AUTO-GENERATED from rundeck-plugins/.github/templates/copilot-instructions.shared.md -- DO NOT EDIT. Run scripts/sync-copilot-instructions.sh to update. -->
2+
3+
# GitHub Copilot Instructions
4+
5+
This repository is part of the `rundeck-plugins` organization and follows the org-wide engineering guide.
6+
7+
- Canonical guide: https://github.com/rundeck-plugins/.github/blob/main/CLAUDE.md
8+
- Plugin overview: https://github.com/rundeck-plugins/.github/blob/main/PLUGINS_OVERVIEW.md
9+
10+
Read the canonical guide for the full context. The essentials below apply to work in this repo.
11+
12+
## Working agreements
13+
- Optimize for customer impact first; connect technical choices to customer/business impact.
14+
- Strive for backwards compatibility. Announce deprecations; don't remove until the next major version.
15+
- Do not push to GitHub or create commits unless explicitly asked.
16+
- Never add `Co-authored-by` / agent / Cursor trailers or email author flags to commits. Plain messages describing the change only.
17+
- Commit messages and PR descriptions explain the "why" and the customer impact, not just the "what."
18+
- Avoid emojis unless explicitly requested.
19+
- Write full build logs to a `temp/` dir at the repo root using `YYYYMMDD-HHMMSS-<desc>.log` names; re-read logs instead of re-running builds repeatedly.
20+
21+
## Build and release conventions
22+
- Baseline: Rundeck 6.0 (Grails 7 / Spring Boot 3). JAR plugins build on Java 17.
23+
- Maven coordinates: group `com.rundeck.plugins`, published to PackageCloud (`PKGCLD_READ_TOKEN`).
24+
- Versioning: Axion from git tags with `prefix = ''` (tags like `1.2.3`, no `v`).
25+
- ZIP (script) plugins: package with Gradle `type: Jar` (archiveExtension `zip`), publish with `extension = 'jar'` and `pom.packaging = 'jar'`, and process `plugin.yaml` with `ReplaceTokens` (never `expand`).
26+
- Groovy-based JAR plugins need the `groovy` Gradle plugin and `groovy-all` dependency or the jar ships with no compiled classes.
27+
28+
## Branch and CI conventions
29+
- Default branch is `main`. Target CI triggers at `main`.
30+
- Security scanning is centralized: `.github/workflows/snyk-scan.yml` calls the org reusable Snyk workflow (push/PR to `main`, weekly Monday 06:00 UTC, and manual dispatch).

.github/workflows/gradle.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ jobs:
88
runs-on: ubuntu-latest
99

1010
steps:
11-
- uses: actions/checkout@v4
11+
- uses: actions/checkout@v5
1212
with:
1313
fetch-depth: 0
1414
- name: Get Fetch Tags
1515
run: git -c protocol.version=2 fetch --tags --progress --no-recurse-submodules origin
1616
if: "!contains(github.ref, 'refs/tags')"
1717
- name: Set up JDK 17
18-
uses: actions/setup-java@v4
18+
uses: actions/setup-java@v5
1919
with:
2020
java-version: 17
2121
distribution: 'zulu'
@@ -33,7 +33,7 @@ jobs:
3333
id: get_version
3434
run: VERSION=$(./gradlew currentVersion -q -Prelease.quiet) && echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
3535
- name: Upload plugin jar
36-
uses: actions/upload-artifact@v4
36+
uses: actions/upload-artifact@v5
3737
with:
3838
# Artifact name
3939
name: Grails-Plugin-${{ steps.get_version.outputs.VERSION }}

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Checkout code
14-
uses: actions/checkout@v4
14+
uses: actions/checkout@v5
1515
with:
1616
fetch-depth: 0
1717
- name: Set up JDK 17
18-
uses: actions/setup-java@v4
18+
uses: actions/setup-java@v5
1919
with:
2020
java-version: '17'
2121
distribution: 'zulu'

.github/workflows/snyk-scan.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ on:
55
branches: [ main, master ]
66
pull_request:
77
branches: [ main, master ]
8+
schedule:
9+
# Weekly on Monday 06:00 UTC (GitHub Actions cron is UTC-only)
10+
- cron: '0 6 * * 1'
811
workflow_dispatch:
912

1013
jobs:

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
plugins {
2-
id 'pl.allegro.tech.build.axion-release' version '1.17.2'
2+
id 'pl.allegro.tech.build.axion-release' version '1.21.2'
33
id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
44
}
55

@@ -48,6 +48,7 @@ scmVersion {
4848
prefix = '' // NO "v" prefix - see PLUGIN_TAGGING_ARCHITECTURE.md
4949
versionSeparator = ''
5050
}
51+
versionCreator 'simple' // Use simple version creator (just tag name); tolerates legacy v* tags
5152
}
5253

5354
version = scmVersion.version // Dynamic version from git tag

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ kotlin-stdlib = "2.1.0"
66

77
# Current dependency versions (maintained for compatibility)
88
gson = "2.10.1"
9-
jackson = "2.21.2"
9+
jackson = "2.22.0"
1010
snakeyaml = "2.2"
1111
groovy = "4.0.29"
1212
lombok = "1.18.30"
1313
spock = "2.4-groovy-4.0"
1414
testcontainers = "1.19.0"
15-
rd-api-client = "2.0.10"
15+
rd-api-client = "2.1.2"
1616
jsch = "0.1.55"
1717
bytebuddy = "1.14.11"
1818
objenesis = "3.4"
1919

2020
# Rundeck version
21-
rundeck-core = "6.0.0-alpha1-20260407"
21+
rundeck-core = "6.1.0-SNAPSHOT"
2222

2323
[libraries]
2424
# Security overrides

src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerContextBuilder.java

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ public class AnsibleRunnerContextBuilder {
4242
private final Map<String, Object> jobConf;
4343
private final Collection<INodeEntry> nodes;
4444
private final Collection<File> tempFiles;
45+
/**
46+
* Per-builder working directory (e.g. {@code ansible-exec-<executionId>-builder-<rand>/})
47+
* created atomically with
48+
* {@link Files#createTempDirectory(java.nio.file.Path, String, java.nio.file.attribute.FileAttribute[])}.
49+
* EXCLUSIVELY owned by this builder; safe to delete recursively.
50+
*/
4551
private File executionSpecificDir;
4652

4753
private AnsiblePluginGroup pluginGroup;
@@ -827,20 +833,25 @@ public void cleanupTempFiles() {
827833
}
828834
tempFiles.clear();
829835

830-
// Clean up execution-specific directory (including group_vars when inventory was generated/inline)
831-
// Note: group_vars created alongside user-provided inventory files is cleaned up by AnsibleRunner
836+
// Clean up the builder-specific working directory (including group_vars when inventory
837+
// was generated/inline). Note: group_vars created alongside user-provided inventory files
838+
// is cleaned up by AnsibleRunner.
839+
// executionSpecificDir is an EXCLUSIVE per-builder directory (ansible-exec-<execId>-builder-<rand>/),
840+
// so recursive deletion is always safe — no sibling builder shares files with it.
832841
if (executionSpecificDir != null && executionSpecificDir.exists()) {
833842
if (!getDebug()) {
834-
log.debug("Cleaning up execution-specific directory: {}", executionSpecificDir.getAbsolutePath());
843+
log.debug("Cleaning up builder-specific directory: {}", executionSpecificDir.getAbsolutePath());
835844
if (!deleteDirectoryRecursively(executionSpecificDir)) {
836-
log.warn("Failed to completely delete execution-specific directory: {}", executionSpecificDir.getAbsolutePath());
845+
log.warn("Failed to completely delete builder-specific directory: {}", executionSpecificDir.getAbsolutePath());
837846
} else {
838-
log.debug("Successfully deleted execution-specific directory: {}", executionSpecificDir.getAbsolutePath());
847+
log.debug("Successfully deleted builder-specific directory: {}", executionSpecificDir.getAbsolutePath());
839848
}
840849
} else {
841-
// In debug mode, the execution-specific directory is intentionally preserved for troubleshooting.
842-
// Note: These directories will accumulate over time and may require periodic manual cleanup.
843-
log.debug("Debug mode enabled; not cleaning up execution-specific directory: {}", executionSpecificDir.getAbsolutePath());
850+
// In debug mode the per-builder directory is intentionally preserved for troubleshooting.
851+
// Sibling directories of the same Rundeck execution share the ansible-exec-<execId>-* prefix
852+
// for easy filtering. These directories will accumulate over time and may require periodic
853+
// manual cleanup.
854+
log.debug("Debug mode enabled; not cleaning up builder-specific directory: {}", executionSpecificDir.getAbsolutePath());
844855
}
845856
}
846857
}
@@ -1145,16 +1156,25 @@ public Boolean generateInventoryNodesAuth() {
11451156
}
11461157

11471158
/**
1148-
* Creates and returns an execution-specific temporary directory path.
1149-
* This ensures that each execution has its own isolated directory for inventory and group_vars,
1150-
* preventing conflicts when multiple workflow step executions run in parallel.
1159+
* Creates and returns a builder-specific temporary directory path.
1160+
*
1161+
* <p>Each builder receives a unique flat directory directly under the configured base tmp
1162+
* dir, named {@code ansible-exec-<executionId>-builder-<rand>/}. The directory is created
1163+
* atomically with {@link Files#createTempDirectory(java.nio.file.Path, String, java.nio.file.attribute.FileAttribute[])},
1164+
* which guarantees uniqueness without any coordination between sibling threads sharing the
1165+
* same Rundeck executionId.</p>
1166+
*
1167+
* <p>The {@code ansible-exec-<executionId>} prefix preserves the ability to filter all
1168+
* directories belonging to a given Rundeck execution (useful in debug mode), while keeping
1169+
* each directory exclusively owned by a single builder. Recursive cleanup of one builder's
1170+
* directory cannot affect siblings.</p>
11511171
*
1152-
* @return The path to the execution-specific directory
1172+
* @return The absolute path to the builder-specific directory
11531173
*/
11541174
String getExecutionSpecificTmpDir() {
11551175
// Return cached directory if already created
11561176
if (executionSpecificDir != null) {
1157-
log.debug("Using cached execution-specific directory: {}", executionSpecificDir.getAbsolutePath());
1177+
log.debug("Using cached builder-specific directory: {}", executionSpecificDir.getAbsolutePath());
11581178
return executionSpecificDir.getAbsolutePath();
11591179
}
11601180

@@ -1169,18 +1189,17 @@ String getExecutionSpecificTmpDir() {
11691189
// Get base tmp directory
11701190
String baseTmpDir = AnsibleUtil.getCustomTmpPathDir(framework);
11711191

1172-
// Create execution-specific directory
11731192
if (executionId != null && !executionId.isEmpty()) {
1174-
executionSpecificDir = new File(baseTmpDir, "ansible-exec-" + executionId);
1175-
log.debug("Creating execution-specific directory: {}", executionSpecificDir.getAbsolutePath());
1193+
// Atomic + unique by construction. No coordination needed between sibling threads
1194+
// sharing the same Rundeck executionId. The executionId prefix lets operators filter
1195+
// all directories belonging to a given Rundeck execution with a single glob.
11761196
try {
1177-
// Use Files.createDirectories() which is idempotent (safe to call if directory exists)
1178-
// and handles race conditions properly. Unlike mkdirs(), it doesn't return false
1179-
// when the directory already exists - it only throws IOException on actual failure.
1180-
Files.createDirectories(executionSpecificDir.toPath());
1181-
log.debug("Successfully ensured execution-specific directory exists: {}", executionSpecificDir.getAbsolutePath());
1197+
executionSpecificDir = Files
1198+
.createTempDirectory(Paths.get(baseTmpDir), "ansible-exec-" + executionId + "-builder-")
1199+
.toFile();
1200+
log.debug("Created builder-specific directory: {}", executionSpecificDir.getAbsolutePath());
11821201
} catch (IOException e) {
1183-
String errorMsg = "Failed to create execution-specific directory: " + executionSpecificDir.getAbsolutePath();
1202+
String errorMsg = "Failed to create builder-specific directory under: " + baseTmpDir;
11841203
log.error(errorMsg, e);
11851204
throw new IllegalStateException(errorMsg, e);
11861205
}

src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleFileCopier.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public class AnsibleFileCopier implements FileCopier, AnsibleDescribable, ProxyR
5656
builder.property(VAULT_KEY_FILE_PROP);
5757
builder.property(VAULT_KEY_STORAGE_PROP);
5858

59+
builder.mapping(ANSIBLE_BINARIES_DIR_PATH,PROJ_PROP_PREFIX + ANSIBLE_BINARIES_DIR_PATH);
60+
builder.frameworkMapping(ANSIBLE_BINARIES_DIR_PATH,FWK_PROP_PREFIX + ANSIBLE_BINARIES_DIR_PATH);
5961
builder.mapping(ANSIBLE_CONFIG_FILE_PATH,PROJ_PROP_PREFIX + ANSIBLE_CONFIG_FILE_PATH);
6062
builder.frameworkMapping(ANSIBLE_CONFIG_FILE_PATH,FWK_PROP_PREFIX + ANSIBLE_CONFIG_FILE_PATH);
6163
builder.mapping(ANSIBLE_VAULT_PATH,PROJ_PROP_PREFIX + ANSIBLE_VAULT_PATH);

0 commit comments

Comments
 (0)