Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,18 @@ jobs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./waitfor/build/libs/waitfor-v${{ steps.get_version.outputs.VERSION }}.zip
asset_name: waitfor-${{ steps.get_version.outputs.VERSION }}.zip
asset_content_type: application/zip
asset_content_type: application/zip

- name: Publish to Sonatype Maven Central
if: ${{ secrets.SONATYPE_USERNAME != '' && secrets.SIGNING_KEY != '' }}

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The if condition syntax is incorrect. In GitHub Actions, the secrets context cannot be accessed directly within the if expression like this. The condition secrets.SONATYPE_USERNAME != '' will always evaluate to false because secrets are not available in the if context.

To conditionally run this step based on whether secrets are set, you should either:

  1. Remove the if condition and let the step fail gracefully if secrets are missing
  2. Use a separate job with environment-level conditions
  3. Check for the secrets in a previous step and set an output variable

The recommended approach is to simply remove the if condition, as the Gradle task will fail with a clear error if the required properties are missing.

Suggested change
if: ${{ secrets.SONATYPE_USERNAME != '' && secrets.SIGNING_KEY != '' }}

Copilot uses AI. Check for mistakes.
env:
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
run: |
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository \
-PsigningKey="$SIGNING_KEY" \
-PsigningPassword="$SIGNING_PASSWORD" \
-PsonatypeUsername="$SONATYPE_USERNAME" \
-PsonatypePassword="$SONATYPE_PASSWORD"
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,24 @@ After install you should see these as Node Steps in the job editor.

![steps](images/nixy-steps.png)

## Publishing to Maven Central

Artifacts are published to Sonatype and Maven Central (group `org.rundeck.plugins`). The release workflow can publish when the required secrets are configured.

**Required project properties (for local publish or CI):**

- `signingKey` – base64-encoded GPG private key for artifact signing
- `signingPassword` – passphrase for the GPG key
- `sonatypeUsername` – Sonatype Nexus username (or token user)
- `sonatypePassword` – Sonatype Nexus password (or token)

**Publish command:**

```bash
./gradlew -PsigningKey="<base64 key>" -PsigningPassword="..." \
-PsonatypeUsername="..." -PsonatypePassword="..." \
publishToSonatype closeAndReleaseSonatypeStagingRepository
```

In CI, configure the repository secrets `SONATYPE_USERNAME`, `SONATYPE_PASSWORD`, `SIGNING_KEY`, and `SIGNING_PASSWORD`; the release workflow will publish to Maven Central when these are set.

10 changes: 10 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
plugins {
id 'pl.allegro.tech.build.axion-release' version '1.18.17'
id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
}

project.version = scmVersion.version

scmVersion {
ignoreUncommittedChanges = true
tag {
Expand All @@ -18,6 +21,13 @@ scmVersion {
}
}

nexusPublishing {
packageGroup = 'org.rundeck.plugins'
repositories {
sonatype()
}
}

allprojects {
repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
group=org.rundeck.rundeck-plugins
group=org.rundeck.plugins

# Modern Gradle configuration
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
Expand Down
55 changes: 55 additions & 0 deletions gradle/publishing.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Shared publishing script for Maven Central (Sonatype).
* Applied from plugin-build.gradle after the mavenZip publication is defined.
*
* Expects project ext: publishName, publishDescription, githubSlug, developers
* (defaults set in plugin-build.gradle; subprojects may override in build.gradle).
*
* To publish:
* ./gradlew -PsigningKey="<base64 GPG key>" -PsigningPassword="..." \
* -PsonatypeUsername="..." -PsonatypePassword="..." \
* publishToSonatype closeAndReleaseSonatypeStagingRepository
*/

publishing.publications.mavenZip.pom {
name = publishName
description = project.ext.hasProperty('publishDescription') ? publishDescription : publishName

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback logic is redundant. Line 16 checks project.ext.hasProperty('publishDescription') when publishDescription is already guaranteed to exist from line 62 in plugin-build.gradle. This check will always be true, making the ternary operator unnecessary.

The line can be simplified to just:
description = publishDescription

Suggested change
description = project.ext.hasProperty('publishDescription') ? publishDescription : publishName
description = publishDescription

Copilot uses AI. Check for mistakes.
url = "https://github.com/${githubSlug}"
licenses {
license {
name = 'The Apache Software License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Apache License URL uses HTTP instead of HTTPS. Maven Central and modern best practices recommend using HTTPS URLs for all external references. The URL should be 'https://www.apache.org/licenses/LICENSE-2.0.txt' instead of 'http://www.apache.org/licenses/LICENSE-2.0.txt'.

Suggested change
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'

Copilot uses AI. Check for mistakes.
distribution = 'repo'
}
}
scm {
url = "https://github.com/${githubSlug}"
connection = "scm:git:git@github.com:${githubSlug}.git"
developerConnection = "scm:git:git@github.com:${githubSlug}.git"
}
if (project.ext.developers) {
developers {
project.ext.developers.each { dev ->
developer {
id = dev.id
name = dev.name
email = dev.email
}
}
}
}
}

def base64Decode = { String prop ->
project.findProperty(prop) ?
new String(Base64.getDecoder().decode(project.findProperty(prop).toString())).trim() :
null
}

if (project.hasProperty('signingKey') && project.hasProperty('signingPassword')) {
apply plugin: 'signing'
signing {
useInMemoryPgpKeys(base64Decode("signingKey"), project.signingPassword)
Comment on lines +43 to +52

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signing configuration has a potential security issue. The base64Decode function decodes the signingKey but doesn't validate it. If the signingKey property contains invalid base64 data, this will throw an IllegalArgumentException at runtime, which could expose the partial key data in error logs.

Consider adding try-catch error handling around the base64 decode operation to fail gracefully with a clearer error message that doesn't risk exposing sensitive data.

Copilot uses AI. Check for mistakes.
sign(publishing.publications)
}
}
9 changes: 9 additions & 0 deletions plugin-build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,23 @@ pluginZip.doFirst {

apply plugin: 'maven-publish'

// POM metadata defaults for Maven Central (subprojects may override in build.gradle)
ext.publishName = project.ext.hasProperty('publishName') ? project.ext.publishName : project.ext.archivesBaseName
ext.publishDescription = project.ext.hasProperty('publishDescription') ? project.ext.publishDescription : project.ext.pluginDescription
ext.githubSlug = project.ext.hasProperty('githubSlug') ? project.ext.githubSlug : 'rundeck-plugins/nixy-step-plugins'
ext.developers = project.ext.hasProperty('developers') ? project.ext.developers : [[id: 'gschueler', name: 'Greg Schueler', email: 'greg@rundeck.com']]

publishing {
publications {
mavenZip(MavenPublication) {

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mavenZip publication is missing required Maven coordinates (groupId and artifactId). While the version is set, Maven Central requires all three coordinates. The groupId should be set to the project's group property, and the artifactId should be set to identify each plugin artifact.

Add these properties within the mavenZip publication block:

  • groupId = project.group (or explicitly 'org.rundeck.plugins')
  • artifactId should be set to the plugin's artifact name (e.g., project.ext.archivesBaseName)
Suggested change
mavenZip(MavenPublication) {
mavenZip(MavenPublication) {
groupId = project.group
artifactId = project.ext.archivesBaseName

Copilot uses AI. Check for mistakes.
artifact pluginZip
version = rootProject.version.toString()

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a version conflict between the subproject version and the Maven publication version. The subprojects (e.g., command/build.gradle line 9) set project.version = 'v' + scmVersion.version (with 'v' prefix), but the mavenZip publication uses version = rootProject.version.toString() which comes from the root build.gradle that sets project.version = scmVersion.version (without 'v' prefix).

This creates an inconsistency where the plugin zip file will be named with the 'v' prefix (e.g., command-v1.0.0.zip) but the Maven artifact will be published with version 1.0.0 (no 'v' prefix). While this may be intentional for Maven coordinates (which shouldn't have a 'v' prefix), the inconsistency could cause confusion.

Consider either:

  1. Documenting this intentional difference
  2. Updating the subproject version logic to use rootProject.version directly
  3. Ensuring the archiveVersion in pluginZip task also uses rootProject.version for consistency

Copilot uses AI. Check for mistakes.
}
}
}

apply from: "${rootProject.projectDir}/gradle/publishing.gradle"

defaultTasks 'clean', 'build','pluginZip'

task build(dependsOn: ['pluginZip']) {
Expand Down
Loading