Skip to content

Commit e908059

Browse files
[RUN-4638] Migrate publishing from Maven Central to PackageCloud (#60)
* Migrate publishing from Maven Central to PackageCloud Part of RUN-4570: Maven Central publishing limits resolution, applying the same design validated on rundeck-plugins/sshj-plugin (RUN-4638). - Remove nexusPublish plugin/nexusPublishing block (Sonatype-specific, dead once release.yml stops calling publishToSonatype). - Point the existing PackageCloud maven repo at the configurable PKGCLD_REPO_URL (trimmed, guarded so plain builds/CI without the var don't fail at configuration time) instead of the hardcoded rundeckpro-test URL. - Drop GPG signing: PackageCloud's Maven endpoint rejects the checksum Gradle auto-generates for .asc signature files (422 Unprocessable Entity), and neither the OSS rundeck WAR nor rundeckpro-enterprise carry .asc signatures on PackageCloud either - only RPM/DEB packages are signed there natively. - release.yml: replace the Sonatype publish step with PackageCloud. * Fail fast in release.yml if PackageCloud env vars are missing Addresses GitHub Copilot review feedback on the PackageCloud migration PRs: without this, a missing PKGCLD_REPO_URL makes the publish task not exist at all (confusing "task not found"), and a missing PKGCLD_WRITE_TOKEN with the URL present produces a "Bearer null" Authorization header that PackageCloud rejects with a non-obvious 401/422. Validate both upfront in the one step that actually needs them, instead of leaving the Gradle-side guard (which exists to keep unrelated CI like gradle.yml working without these vars) to surface the failure indirectly. * Fall back to rundeck-plugins PackageCloud URL, simplify token check Addresses the remaining GitHub Copilot review feedback: default pkgcldRepoUrl to the real, canonical rundeck-plugins PackageCloud URL instead of relying on PKGCLD_REPO_URL always being set. This removes the need for the "only register if URL present" guard entirely, since uri() never receives null - registering a Maven repository doesn't make any network call until something actually resolves/publishes through it, so there's no cost to always registering it. With the URL guaranteed non-null, release.yml's fail-fast check only needs to validate PKGCLD_WRITE_TOKEN (which still has no safe public default, since it's a secret). * Sign and upload GPG signatures to PackageCloud manually Validated end-to-end on rundeck-plugins/sshj-plugin (merged): signing via sign(publishing.publications) makes Gradle auto-generate a checksum for every publication artifact including the .asc files themselves, and PackageCloud's Maven endpoint 422s on the checksum-of-a-signature-file. Sidestep this entirely by signing the already-built artifacts with the gpg CLI directly and uploading just the .asc files via curl, bypassing Gradle's publish/checksum machinery for this part. Reuses the existing SIGNING_KEY_B64/SIGNING_PASSWORD secrets (now pointing at a renewed, non-expired key).
1 parent cb13e8c commit e908059

3 files changed

Lines changed: 45 additions & 49 deletions

File tree

.github/workflows/release.yml

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ jobs:
99
build:
1010
name: Publish Release
1111
runs-on: ubuntu-latest
12+
env:
13+
PKGCLD_REPO_URL: ${{ vars.PKGCLD_REPO_URL }}
1214
steps:
1315
- name: Checkout code
1416
uses: actions/checkout@v7
@@ -33,10 +35,38 @@ jobs:
3335
build/libs/http-step-${{ steps.get_version.outputs.VERSION }}.jar
3436
env:
3537
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36-
- name: Publish to Maven Central
37-
run: ./gradlew -PsigningKey=${SIGNING_KEY_B64} -PsigningPassword=${SIGNING_PASSWORD} -PsonatypeUsername=${SONATYPE_USERNAME} -PsonatypePassword=${SONATYPE_PASSWORD} publishToSonatype closeAndReleaseSonatypeStagingRepository
38+
- name: Publish to PackageCloud
39+
run: |
40+
if [ -z "$PKGCLD_WRITE_TOKEN" ]; then
41+
echo "::error::PKGCLD_WRITE_TOKEN must be set to publish to PackageCloud"
42+
exit 1
43+
fi
44+
./gradlew publishAllPublicationsToPackageCloudRepository
45+
env:
46+
PKGCLD_WRITE_TOKEN: ${{ secrets.PKGCLD_WRITE_TOKEN }}
47+
- name: Sign and upload GPG signatures to PackageCloud
48+
run: |
49+
set -e
50+
VERSION="${{ steps.get_version.outputs.VERSION }}"
51+
BASE_URL="${PKGCLD_REPO_URL:-https://packagecloud.io/pagerduty/rundeck-plugins/maven2}/org/rundeck/plugins/http-step/${VERSION}"
52+
53+
echo "$SIGNING_KEY_B64" | base64 -d | gpg --batch --yes --import
54+
KEY_ID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/ {print $5; exit}')
55+
56+
sign_and_upload() {
57+
local FILE="$1"
58+
local REMOTE_NAME="$2"
59+
gpg --batch --yes --pinentry-mode loopback --passphrase "$SIGNING_PASSWORD" --default-key "$KEY_ID" --detach-sign --armor "$FILE"
60+
curl -sf -H "Authorization: Bearer ${PKGCLD_WRITE_TOKEN}" \
61+
-X PUT --data-binary "@${FILE}.asc" \
62+
"${BASE_URL}/${REMOTE_NAME}.asc"
63+
}
64+
65+
sign_and_upload "build/libs/http-step-${VERSION}.jar" "http-step-${VERSION}.jar"
66+
sign_and_upload "build/libs/http-step-${VERSION}-sources.jar" "http-step-${VERSION}-sources.jar"
67+
sign_and_upload "build/libs/http-step-${VERSION}-javadoc.jar" "http-step-${VERSION}-javadoc.jar"
68+
sign_and_upload "build/publications/http-step/pom-default.xml" "http-step-${VERSION}.pom"
3869
env:
39-
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
40-
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
4170
SIGNING_KEY_B64: ${{ secrets.SIGNING_KEY_B64 }}
4271
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
72+
PKGCLD_WRITE_TOKEN: ${{ secrets.PKGCLD_WRITE_TOKEN }}

build.gradle

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
plugins {
22
id 'pl.allegro.tech.build.axion-release' version '1.21.2'
33
id 'java'
4-
id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
54
}
65

76
java {
@@ -142,14 +141,4 @@ jar {
142141
}
143142
}
144143

145-
nexusPublishing {
146-
packageGroup = 'org.rundeck.plugins'
147-
repositories {
148-
sonatype {
149-
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
150-
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
151-
}
152-
}
153-
}
154-
155144
apply from: "${rootDir}/gradle/publishing.gradle"

gradle/publishing.gradle

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,10 @@
55
* publishDescription = 'description' (optional)
66
* githubSlug = Github slug e.g. 'rundeck/rundeck-cli'
77
* developers = [ [id:'id', name:'name', email: 'email' ] ] list of developers
8-
*
9-
* Define project properties to sign and publish when invoking publish task:
10-
*
11-
* ./gradlew \
12-
* -PsigningKey="base64 encoded gpg key" \
13-
* -PsigningPassword="password for key" \
14-
* -PsonatypeUsername="sonatype token user" \
15-
* -PsonatypePassword="sonatype token password" \
16-
* publishToSonatype closeAndReleaseSonatypeStagingRepository
178
*/
189
apply plugin: 'maven-publish'
19-
apply plugin: 'signing'
10+
11+
def pkgcldRepoUrl = (System.getenv("PKGCLD_REPO_URL") ?: "https://packagecloud.io/pagerduty/rundeck-plugins/maven2").trim()
2012

2113
publishing {
2214
publications {
@@ -56,31 +48,16 @@ publishing {
5648
}
5749
}
5850
repositories {
59-
def pkgcldWriteToken = System.getenv("PKGCLD_WRITE_TOKEN") ?: project.findProperty("pkgcldWriteToken")
60-
if (pkgcldWriteToken) {
61-
maven {
62-
name = "PackageCloudTest"
63-
url = uri("https://packagecloud.io/pagerduty/rundeckpro-test/maven2")
64-
authentication {
65-
header(HttpHeaderAuthentication)
66-
}
67-
credentials(HttpHeaderCredentials) {
68-
name = "Authorization"
69-
value = "Bearer " + pkgcldWriteToken
70-
}
51+
maven {
52+
name = "PackageCloud"
53+
url = uri(pkgcldRepoUrl)
54+
authentication {
55+
header(HttpHeaderAuthentication)
56+
}
57+
credentials(HttpHeaderCredentials) {
58+
name = "Authorization"
59+
value = "Bearer " + (System.getenv("PKGCLD_WRITE_TOKEN") ?: project.findProperty("pkgcldWriteToken"))
7160
}
7261
}
7362
}
74-
}
75-
def base64Decode = { String prop ->
76-
project.findProperty(prop) ?
77-
new String(Base64.getDecoder().decode(project.findProperty(prop).toString())).trim() :
78-
null
79-
}
80-
81-
if (project.hasProperty('signingKey') && project.hasProperty('signingPassword')) {
82-
signing {
83-
useInMemoryPgpKeys(base64Decode("signingKey"), project.signingPassword)
84-
sign(publishing.publications)
85-
}
8663
}

0 commit comments

Comments
 (0)