Skip to content

Commit a398bc1

Browse files
ci: fix jreleaser release process (#24)
1 parent ece9a3d commit a398bc1

4 files changed

Lines changed: 251 additions & 10 deletions

File tree

.claude/settings.local.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
"Bash(done)",
3535
"Bash(grep:*)",
3636
"Bash(ls:*)",
37-
"Bash(xargs:*)"
37+
"Bash(xargs:*)",
38+
"Bash(./gradlew tasks:*)",
39+
"Bash(./gradlew jreleaserConfig 2>&1 | tail -30)",
40+
"Bash(find:*)",
41+
"Bash(chmod:*)"
3842
]
3943
}
4044
}

.github/workflows/release.yml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,33 @@ jobs:
7373
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_USERNAME }}
7474
JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_PASSWORD }}
7575

76-
- name: Release to Maven Central portal
77-
run: ./gradlew jreleaserFullRelease
76+
- name: Deploy to Maven Central portal
77+
run: ./infra/release-with-retry.sh 900 100 20
78+
env:
79+
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_USERNAME }}
80+
JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_PASSWORD }}
81+
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PUBLIC_KEY }}
82+
JRELEASER_GPG_SECRET_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_SECRET_KEY }}
83+
JRELEASER_GPG_PASSPHRASE: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PASSPHRASE }}
84+
85+
- name: Generate changelog
86+
run: ./gradlew jreleaserChangelog --no-daemon
7887
env:
7988
JRELEASER_GITHUB_TOKEN: ${{ secrets.FLAMINGOCK_JRELEASER_GITHUB_TOKEN }}
8089
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_USERNAME }}
8190
JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_PASSWORD }}
8291
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PUBLIC_KEY }}
8392
JRELEASER_GPG_SECRET_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_SECRET_KEY }}
8493
JRELEASER_GPG_PASSPHRASE: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PASSPHRASE }}
94+
95+
- name: Create GitHub release
96+
run: |
97+
VERSION=$(grep '^version' build.gradle.kts | head -1 | sed 's/.*"\(.*\)".*/\1/')
98+
PRERELEASE_FLAG=$(echo "$VERSION" | grep -qiE '(beta|alpha|rc|snapshot)' && echo "--prerelease" || echo "")
99+
gh release create "v${VERSION}" \
100+
--title "Release v${VERSION}" \
101+
--notes-file build/jreleaser/release/CHANGELOG.md \
102+
$PRERELEASE_FLAG \
103+
--repo "${{ github.repository }}"
104+
env:
105+
GH_TOKEN: ${{ secrets.FLAMINGOCK_JRELEASER_GITHUB_TOKEN }}

build.gradle.kts

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ plugins {
22
`java-library`
33
`maven-publish`
44
id("com.diffplug.spotless") version "6.25.0"
5+
id("org.jreleaser") version "1.15.0"
56
}
67

78

89
group = "io.flamingock"
9-
version = "1.0.0-SNAPSHOT"
10+
version = "1.0.0-beta.7"
1011

1112
val flamingockVersion = "1.2.0-beta.1"
1213

@@ -37,7 +38,8 @@ java {
3738
toolchain {
3839
languageVersion.set(JavaLanguageVersion.of(8))
3940
}
40-
41+
withSourcesJar()
42+
withJavadocJar()
4143
}
4244

4345
tasks.withType<JavaCompile>().configureEach {
@@ -67,19 +69,43 @@ publishing {
6769
description.set("MongoDB change templates for document database operations using Flamingock")
6870
url.set("https://flamingock.io")
6971

72+
organization {
73+
name.set("Flamingock")
74+
url.set("https://flamingock.io")
75+
}
76+
77+
issueManagement {
78+
system.set("GitHub")
79+
url.set("https://github.com/flamingock/flamingock-java-template-mongodb/issues")
80+
}
81+
7082
licenses {
7183
license {
72-
name.set("Apache License, Version 2.0")
84+
name.set("Apache-2.0")
7385
url.set("https://www.apache.org/licenses/LICENSE-2.0")
7486
}
7587
}
7688

7789
developers {
7890
developer {
79-
id.set("flamingock")
80-
name.set("Flamingock Team")
81-
organization.set("Flamingock")
82-
organizationUrl.set("https://flamingock.io")
91+
id.set("dieppa")
92+
name.set("Antonio Perez Dieppa")
93+
email.set("dieppa@flamingock.io")
94+
}
95+
developer {
96+
id.set("osantana")
97+
name.set("Oscar Santana")
98+
email.set("osantana@flamingock.io")
99+
}
100+
developer {
101+
id.set("bercianor")
102+
name.set("Berciano Ramiro")
103+
email.set("bercianor@flamingock.io")
104+
}
105+
developer {
106+
id.set("dfrigolet")
107+
name.set("Daniel Frigolet")
108+
email.set("dfrigolet@flamingock.io")
83109
}
84110
}
85111

@@ -91,6 +117,11 @@ publishing {
91117
}
92118
}
93119
}
120+
repositories {
121+
maven {
122+
url = uri(layout.buildDirectory.dir("staging-deploy"))
123+
}
124+
}
94125
}
95126

96127
val licenseHeaderText = """/*
@@ -130,3 +161,54 @@ afterEvaluate {
130161
description = "Check license headers (manual task - not part of build)"
131162
}
132163
}
164+
165+
jreleaser {
166+
project {
167+
inceptionYear = "2024"
168+
author("dieppa")
169+
author("osantana")
170+
author("bercianor")
171+
author("dfrigolet")
172+
description = "MongoDB change templates for document database operations using Flamingock"
173+
}
174+
signing {
175+
active = org.jreleaser.model.Active.ALWAYS
176+
armored = true
177+
}
178+
deploy {
179+
maven {
180+
mavenCentral {
181+
create("sonatype") {
182+
active = org.jreleaser.model.Active.ALWAYS
183+
url = "https://central.sonatype.com/api/v1/publisher"
184+
stagingRepository("build/staging-deploy")
185+
applyMavenCentralRules = true
186+
maxRetries = 90
187+
retryDelay = 20
188+
}
189+
}
190+
}
191+
}
192+
release {
193+
github {
194+
overwrite = true
195+
changelog {
196+
formatted = org.jreleaser.model.Active.ALWAYS
197+
preset = "conventional-commits"
198+
}
199+
prerelease {
200+
pattern = ".*(?:beta|alpha|rc|snapshot).*"
201+
}
202+
}
203+
}
204+
}
205+
206+
tasks.register("createStagingDeployFolder") {
207+
doLast {
208+
mkdir(layout.buildDirectory.dir("staging-deploy"))
209+
}
210+
}
211+
212+
tasks.matching { it.name == "publish" }.configureEach {
213+
finalizedBy("createStagingDeployFolder")
214+
}

infra/release-with-retry.sh

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# -------------------------------------------------------------------
5+
# Maven Central release script with curl-based upload fallback.
6+
#
7+
# JReleaser's Feign HTTP client has a hard read-timeout (~6 min) that
8+
# cannot be reliably overridden via Gradle DSL for large bundles.
9+
# This script first attempts jreleaserDeploy; if that fails, it falls
10+
# back to uploading the bundle via curl with a generous timeout,
11+
# then polls the Central Portal for deployment status.
12+
# -------------------------------------------------------------------
13+
14+
UPLOAD_TIMEOUT=${1:-900} # curl upload timeout in seconds (default 15 min)
15+
MAX_STATUS_CHECKS=${2:-100} # max status poll attempts
16+
STATUS_CHECK_INTERVAL=${3:-20} # seconds between status polls
17+
18+
CENTRAL_API="https://central.sonatype.com/api/v1/publisher"
19+
20+
# Credentials from environment (set by GitHub Actions)
21+
USERNAME="${JRELEASER_MAVENCENTRAL_USERNAME:?JRELEASER_MAVENCENTRAL_USERNAME not set}"
22+
PASSWORD="${JRELEASER_MAVENCENTRAL_PASSWORD:?JRELEASER_MAVENCENTRAL_PASSWORD not set}"
23+
AUTH_TOKEN=$(echo -n "${USERNAME}:${PASSWORD}" | base64)
24+
25+
# --- Step 1: Try jreleaserDeploy (signs artifacts and creates bundle) ---
26+
echo "==> Attempting jreleaserDeploy (signs + bundles + uploads)..."
27+
if ./gradlew jreleaserDeploy --no-daemon --stacktrace; then
28+
echo "==> jreleaserDeploy succeeded"
29+
exit 0
30+
fi
31+
32+
echo "==> jreleaserDeploy failed (likely upload timeout). Falling back to curl upload."
33+
34+
# --- Step 2: Locate the bundle created by JReleaser ---
35+
BUNDLE_DIR="build/jreleaser/deploy/mavenCentral/sonatype"
36+
BUNDLE=$(find "$BUNDLE_DIR" -name "*.zip" -type f 2>/dev/null | head -1)
37+
38+
if [ -z "$BUNDLE" ]; then
39+
echo "ERROR: No bundle zip found in $BUNDLE_DIR"
40+
echo "Contents of build/jreleaser/deploy/:"
41+
find build/jreleaser/deploy/ -type f 2>/dev/null || true
42+
exit 1
43+
fi
44+
45+
echo "==> Found bundle: $BUNDLE"
46+
47+
# --- Step 3: Upload bundle via curl with retries ---
48+
upload_bundle() {
49+
local attempt=$1
50+
echo "==> Upload attempt $attempt — timeout ${UPLOAD_TIMEOUT}s"
51+
52+
HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" \
53+
--max-time "$UPLOAD_TIMEOUT" \
54+
--connect-timeout 60 \
55+
-X POST "$CENTRAL_API/upload" \
56+
-H "Authorization: Bearer $AUTH_TOKEN" \
57+
-F "bundle=@$BUNDLE;type=application/octet-stream" \
58+
-F "publishingType=AUTOMATIC" \
59+
2>&1) || true
60+
61+
HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
62+
RESPONSE_BODY=$(echo "$HTTP_RESPONSE" | sed '$d')
63+
64+
echo "==> Upload response: HTTP $HTTP_CODE"
65+
66+
if [ "$HTTP_CODE" = "201" ]; then
67+
DEPLOYMENT_ID="$RESPONSE_BODY"
68+
echo "==> Upload successful. Deployment ID: $DEPLOYMENT_ID"
69+
return 0
70+
else
71+
echo "==> Upload failed: $RESPONSE_BODY"
72+
return 1
73+
fi
74+
}
75+
76+
DEPLOYMENT_ID=""
77+
for attempt in 1 2 3 4 5; do
78+
if upload_bundle "$attempt"; then
79+
break
80+
fi
81+
if [ "$attempt" -eq 5 ]; then
82+
echo "ERROR: All upload attempts failed"
83+
exit 1
84+
fi
85+
echo "==> Retrying upload in 30 seconds..."
86+
sleep 30
87+
done
88+
89+
# --- Step 4: Poll for deployment status ---
90+
if [ -z "$DEPLOYMENT_ID" ]; then
91+
echo "ERROR: No deployment ID obtained"
92+
exit 1
93+
fi
94+
95+
echo "==> Polling deployment status (max $MAX_STATUS_CHECKS checks, every ${STATUS_CHECK_INTERVAL}s)..."
96+
97+
for (( i=1; i<=MAX_STATUS_CHECKS; i++ )); do
98+
sleep "$STATUS_CHECK_INTERVAL"
99+
100+
STATUS_RESPONSE=$(curl -s \
101+
--max-time 60 \
102+
-X POST "$CENTRAL_API/status?id=$DEPLOYMENT_ID" \
103+
-H "Authorization: Bearer $AUTH_TOKEN" \
104+
-H "Accept: application/json" \
105+
2>&1) || true
106+
107+
DEPLOYMENT_STATE=$(echo "$STATUS_RESPONSE" | grep -o '"deploymentState":"[^"]*"' | head -1 | cut -d'"' -f4)
108+
109+
echo " [$i/$MAX_STATUS_CHECKS] Deployment state: ${DEPLOYMENT_STATE:-UNKNOWN}"
110+
111+
case "$DEPLOYMENT_STATE" in
112+
PUBLISHED)
113+
echo "==> Deployment published successfully!"
114+
exit 0
115+
;;
116+
FAILED)
117+
echo "ERROR: Deployment failed on Maven Central"
118+
echo "Response: $STATUS_RESPONSE"
119+
exit 1
120+
;;
121+
VALIDATED|PUBLISHING)
122+
echo " Deployment is progressing..."
123+
;;
124+
PENDING|VALIDATING)
125+
echo " Still processing..."
126+
;;
127+
*)
128+
echo " Unexpected state, continuing to poll..."
129+
;;
130+
esac
131+
done
132+
133+
echo "ERROR: Timed out waiting for deployment status after $MAX_STATUS_CHECKS checks"
134+
exit 1

0 commit comments

Comments
 (0)