Skip to content

Commit dc7568d

Browse files
authored
Modify release to deploy to Central Publishing Portal (#1124)
* Add upload script to upload to central publishing portal * Migration to central publishing portal * Fix copyright
1 parent fea7450 commit dc7568d

File tree

4 files changed

+316
-94
lines changed

4 files changed

+316
-94
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ jobs:
6969
with:
7070
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
7171
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
72-
MAVEN_SETTINGS: ${{ secrets.MAVEN_SETTINGS }}
72+
CENTRAL_USER: ${{ secrets.CENTRAL_USER }}
73+
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
7374
build-cache: read-only
7475
artifact-name: io-helidon-build-tools-artifacts-${{ github.ref_name }}
7576
artifact-path: target/nexus-staging/

etc/scripts/release.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,19 +191,19 @@ release_build(){
191191
/usr/lib/gnupg/gpg-preset-passphrase --preset "${GPG_KEYGRIP}" <<< "${GPG_PASSPHRASE}"
192192
fi
193193

194-
# Perform local deployment
194+
# Perform local deployment to filesystem
195195
# shellcheck disable=SC2086
196196
mvn ${MVN_ARGS} "${ARGS[@]}" \
197197
deploy \
198198
-Prelease \
199199
-DskipTests \
200-
-DskipRemoteStaging=true
200+
-DaltDeploymentRepository=":::file://${PWD}/staging"
201201

202-
# Upload all artifacts to nexus
202+
# Upload artifacts to Sonatype Central Publishing Portal
203203
version=$(release_version)
204-
# shellcheck disable=SC2086
205-
mvn ${MVN_ARGS} -N nexus-staging:deploy-staged \
206-
-DstagingDescription="Helidon Build Tools v${version}"
204+
"${WS_DIR}/etc/scripts/upload.sh" upload_release \
205+
--dir="staging" \
206+
--description="Helidon Build Tools v${version}"
207207
}
208208

209209
# Invoke command

etc/scripts/upload.sh

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
#!/bin/bash
2+
#
3+
# Copyright (c) 2025 Oracle and/or its affiliates.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
set -o pipefail || true # trace ERR through pipes
19+
set -o errtrace || true # trace ERR through commands and functions
20+
set -o errexit || true # exit the script if any statement returns a non-true return value
21+
22+
on_error(){
23+
CODE="${?}" && \
24+
set +x && \
25+
printf "[ERROR] Error(code=%s) occurred at %s:%s command: %s\n" \
26+
"${CODE}" "${BASH_SOURCE[0]}" "${LINENO}" "${BASH_COMMAND}" >&2
27+
}
28+
trap on_error ERR
29+
30+
usage(){
31+
cat <<EOF
32+
33+
DESCRIPTION: Upload staged artifacts to the Central Portal
34+
35+
USAGE:
36+
37+
$(basename "${0}") [OPTIONS] --directory=DIR CMD
38+
39+
--dir=DIR
40+
Set the staging directory to use.
41+
42+
--description=DESCRIPTION
43+
Set the staging repository description to use.
44+
%{version} can be used to subsitute the release version.
45+
46+
--help
47+
Prints the usage and exits.
48+
49+
CMD:
50+
51+
upload_release
52+
Upload staging directory to a release repository
53+
54+
upload_snapshot
55+
Uploading staging directory to a snapshots repository
56+
EOF
57+
}
58+
59+
# parse command line args
60+
ARGS=( )
61+
while (( ${#} > 0 )); do
62+
case ${1} in
63+
"--dir="*)
64+
STAGING_DIR=${1#*=}
65+
shift
66+
;;
67+
"--description="*)
68+
DESCRIPTION=${1#*=}
69+
shift
70+
;;
71+
"--help")
72+
usage
73+
exit 0
74+
;;
75+
"upload_release"|"upload_snapshot")
76+
COMMAND="${1}"
77+
shift
78+
;;
79+
*)
80+
ARGS+=( "${1}" )
81+
shift
82+
;;
83+
esac
84+
done
85+
readonly ARGS
86+
readonly COMMAND
87+
88+
# copy stdout as fd 6 and redirect stdout to stderr
89+
# this allows us to use fd 6 for returning data
90+
exec 6>&1 1>&2
91+
92+
case ${COMMAND} in
93+
"upload_release")
94+
if [ -z "${DESCRIPTION}" ] ; then
95+
echo "ERROR: description required" >&2
96+
usage
97+
exit 1
98+
fi
99+
;;
100+
"upload_snapshot")
101+
# no-op
102+
;;
103+
"")
104+
echo "ERROR: no command provided" >&2
105+
usage
106+
exit 1
107+
;;
108+
*)
109+
echo "ERROR: unknown command ${COMMAND}" >&2
110+
usage
111+
exit 1
112+
;;
113+
esac
114+
115+
if [ -z "${STAGING_DIR}" ] ; then
116+
echo "ERROR: --staging-dir is required" >&2
117+
usage
118+
exit 1
119+
fi
120+
121+
if [ -z "${CENTRAL_USER}" ] ; then
122+
echo "ERROR: CENTRAL_USER environment is not set" >&2
123+
usage
124+
exit 1
125+
fi
126+
if [ -z "${CENTRAL_PASSWORD}" ] ; then
127+
echo "ERROR: CENTRAL_PASSWORD environment is not set" >&2
128+
usage
129+
exit 1
130+
fi
131+
132+
if [ ! -d "${STAGING_DIR}" ] ; then
133+
echo "ERROR: Invalid staging directory: ${STAGING_DIR}" >&2
134+
exit 1
135+
fi
136+
137+
# Central Portal URL for releases
138+
readonly CENTRAL_URL="https://central.sonatype.com/api/v1"
139+
# Central SNAPSHOT URL
140+
readonly SNAPSHOT_URL="https://central.sonatype.com/repository/maven-snapshots"
141+
142+
BEARER=$(printf "%s:%s" "${CENTRAL_USER}" "${CENTRAL_PASSWORD}" | base64)
143+
144+
find_version() {
145+
local versions version
146+
147+
# List the "version" directories
148+
versions=$(while read -r v ; do
149+
dirname "${v}" | xargs basename
150+
done < <(find "${STAGING_DIR}" -type f -name "*.pom" -print) | sort | uniq)
151+
152+
# Enforce one version per staging directory
153+
for v in ${versions} ; do
154+
if [ -n "${version}" ] ; then
155+
echo "ERROR: staging directory contains more than one version: ${versions}" >&2
156+
return 1
157+
fi
158+
version="${v}"
159+
done
160+
161+
if [ -z "${version}" ] ; then
162+
echo "ERROR: version not found" >&2
163+
return 1
164+
fi
165+
echo "${version}"
166+
}
167+
168+
upload_snapshot() {
169+
echo "Uploading SNAPSHOT..." >&2
170+
local version
171+
version=$(find_version)
172+
173+
# Make sure version ends in -SNAPSHOT
174+
if [[ "${version}" != *-SNAPSHOT ]]; then
175+
echo "ERROR: Version ${version} is NOT a SNAPSHOT version" >&2
176+
exit 1
177+
fi
178+
179+
nexus_upload "${SNAPSHOT_URL}" "${STAGING_DIR}"
180+
}
181+
182+
upload_release() {
183+
echo "Uploading release..." >&2
184+
local version
185+
version=$(find_version)
186+
187+
# Make sure version does NOT end in -SNAPSHOT
188+
if [[ "${v}" = *-SNAPSHOT ]]; then
189+
echo "ERROR: Version ${version} is a SNAPSHOT version" >&2
190+
exit 1
191+
fi
192+
193+
deployment_id="$(central_upload "${CENTRAL_URL}" "${STAGING_DIR}")"
194+
central_finish "${deployment_id}"
195+
}
196+
197+
# Upload contents of the staging directory to central portal
198+
# arg1: base URL of upload portal
199+
# arg2: staging directory
200+
# prints deployment ID
201+
central_upload() {
202+
local version
203+
version=$(find_version)
204+
205+
printf "Uploading artifacts...\n" >&2
206+
readonly UPLOAD_BUNDLE=io-helidon-build-tools-artifacts-${version}.zip
207+
rm -f "${UPLOAD_BUNDLE}"
208+
printf "Creating artifact bundle %s...\n" "${UPLOAD_BUNDLE}" >&2
209+
(cd "${2}"; zip -ryq "../${UPLOAD_BUNDLE}" .)
210+
211+
local responseFile statusFile
212+
responseFile=$(mktemp)
213+
statusFile=$(mktemp)
214+
215+
printf "Uploading %s to %s...\n" "${UPLOAD_BUNDLE}" "${1}" >&2
216+
# Upload bundle in one shot
217+
# publishingType of USER_MANAGED acts like "staging". Artifacts are uploaded and verified but not published.
218+
curl --request POST \
219+
--retry 2 \
220+
--header "Authorization: Bearer ${BEARER}" \
221+
--write-out "%{stderr}%{http_code} %{url_effective}\n%{stdout}%{http_code}" \
222+
--form bundle=@"${UPLOAD_BUNDLE}" \
223+
-o "${responseFile}" \
224+
"${1}/publisher/upload?name=io-helidon-build-tools-${version}&publishingType=USER_MANAGED" > "${statusFile}"
225+
226+
# handle errors
227+
if [ "$(cat "${statusFile}")" != "201" ] ; then
228+
printf "[ERROR] %s\n" "$(cat "${responseFile}")" >&2
229+
exit 1
230+
fi
231+
232+
# If success then output deployment ID
233+
cat "${responseFile}"
234+
}
235+
236+
# Poll deployment status until operation is complete.
237+
# arg1: deployment ID
238+
central_finish() {
239+
printf "\n\nVerifying upload status of %s...\n\n" "$1" >&2
240+
241+
while true; do
242+
local deploymentState
243+
deploymentState=$(central_get_deployment_state "$1")
244+
printf "%s...\n" "${deploymentState}" >&2
245+
case ${deploymentState} in
246+
"PENDING")
247+
;;
248+
"VALIDATING")
249+
;;
250+
"VALIDATED")
251+
printf "Done. Bits are uploaded." >&2
252+
exit
253+
;;
254+
"PUBLISHING")
255+
;;
256+
"PUBLISHED")
257+
printf "!!!! Oh No! Artifacts have been published!!!! That should not have happened." >&2
258+
exit
259+
;;
260+
"FAILED")
261+
exit
262+
;;
263+
esac
264+
sleep 10
265+
done
266+
}
267+
268+
# Gets the status of a deployment from central portal
269+
# arg1: deployment ID
270+
# Prints deployment state for the given ID:
271+
# PENDING, VALIDATING, VALIDATED, PUBLISHING, PUBLISHED, FAILED
272+
# Should never be PUBLISHING or PUBLISHED since our publishingType is USER_MANAGED
273+
central_get_deployment_state() {
274+
curl --request POST \
275+
-s \
276+
--retry 3 \
277+
--header "Authorization: Bearer ${BEARER}" \
278+
"${CENTRAL_URL}/publisher/status?id=${1}" \
279+
| jq -r ".deploymentState"
280+
}
281+
282+
# Upload to a nexus repository. This is used to support SNAPSHOT releases
283+
# arg1: base URL
284+
# arg2: staging directory
285+
nexus_upload() {
286+
printf "\nUploading artifacts...\n" >&2
287+
288+
local tmpfile
289+
tmpfile=$(mktemp)
290+
291+
# Generate a curl config file for all files to deploy
292+
# Use -T <file> --url <url> for each file
293+
while read -r i ; do
294+
echo "-T ${2}/${i}" >> "${tmpfile}"
295+
echo "--url ${1}/${i}" >> "${tmpfile}"
296+
done < <(find "${2}" -type f | cut -c $((${#2} + 2))-)
297+
298+
# Upload
299+
curl -s \
300+
--user "${CENTRAL_USER}:${CENTRAL_PASSWORD}" \
301+
--write-out "%{stderr}%{http_code} %{url_effective} %{speed_upload}B/s\n" \
302+
--config "${tmpfile}" \
303+
--parallel \
304+
--parallel-max 10 \
305+
--retry 3
306+
}
307+
308+
${COMMAND}

0 commit comments

Comments
 (0)