Skip to content

Commit bd0375f

Browse files
committed
ci: fix release workflow review findings
1 parent a7e7925 commit bd0375f

File tree

6 files changed

+162
-76
lines changed

6 files changed

+162
-76
lines changed

.github/scripts/release_extract_upload_context.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
#!/usr/bin/env python3
2+
# Copyright 2026 Apollo Authors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
215
"""Extract uploaded artifact URLs from Maven deploy logs."""
316

417
from __future__ import annotations
@@ -32,14 +45,8 @@ def main() -> int:
3245
uploaded_urls.append(url)
3346

3447
deduped_urls = sorted(set(uploaded_urls))
35-
jar_urls = [
36-
url for url in deduped_urls
37-
if url.endswith(".jar") and not url.endswith(".jar.asc")
38-
]
39-
pom_urls = [
40-
url for url in deduped_urls
41-
if url.endswith(".pom") and not url.endswith(".pom.asc")
42-
]
48+
jar_urls = [url for url in deduped_urls if url.endswith(".jar")]
49+
pom_urls = [url for url in deduped_urls if url.endswith(".pom")]
4350

4451
payload = {
4552
"target_repository": repository_name,

.github/scripts/release_resolve_repository_context.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
#!/usr/bin/env python3
2+
# Copyright 2026 Apollo Authors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
215
"""Resolve Sonatype repository context for release deployments."""
316

417
from __future__ import annotations
@@ -22,7 +35,10 @@ def request_json(url: str, headers: dict[str, str]) -> tuple[int | None, dict[st
2235
body = response.read().decode("utf-8")
2336
if not body:
2437
return response.status, {}
25-
return response.status, json.loads(body)
38+
try:
39+
return response.status, json.loads(body)
40+
except json.JSONDecodeError:
41+
return response.status, {"raw": body}
2642
except urllib.error.HTTPError as error:
2743
try:
2844
payload = json.loads(error.read().decode("utf-8"))
@@ -91,6 +107,15 @@ def main() -> int:
91107
status, payload = request_json(url, headers)
92108
if status is None:
93109
last_error = payload.get("error", "unknown error")
110+
context["search_candidates"].append(
111+
{
112+
"state": state,
113+
"ip": ip,
114+
"status": None,
115+
"count": 0,
116+
"error": last_error,
117+
}
118+
)
94119
continue
95120

96121
repositories = (

.github/scripts/release_write_summary.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
#!/usr/bin/env python3
2+
# Copyright 2026 Apollo Authors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
215
"""Write release publish context summary for GitHub Actions."""
316

417
from __future__ import annotations

.github/scripts/sonatype_publish.py

Lines changed: 86 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
#!/usr/bin/env python3
2+
# Copyright 2026 Apollo Authors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
215
"""Trigger and monitor Sonatype portal publish flow."""
316

417
from __future__ import annotations
@@ -115,75 +128,90 @@ def main() -> int:
115128
if upload_status is None:
116129
reason = f"Upload API failed: {upload_payload.get('error', 'unknown error')}"
117130
elif upload_status >= 400:
131+
upload_error = upload_payload.get("error")
132+
if not upload_error:
133+
upload_error = f"HTTP {upload_status}"
118134
reason = (
119-
f"Upload API returned HTTP {upload_status}: "
120-
f"{upload_payload.get('error', 'unknown error')}"
135+
f"Upload API failed: {upload_error}"
121136
)
122137

123-
list_url = (
124-
f"{OSSRH_BASE}/manual/search/repositories?"
125-
f"ip=any&profile_id={urllib.parse.quote(namespace)}"
126-
)
127-
_, list_payload = request_json("GET", list_url, headers)
128-
repositories = list_payload.get("repositories", []) if isinstance(list_payload, dict) else []
129-
for item in repositories:
130-
if item.get("key") == repository_key and item.get("portal_deployment_id"):
131-
deployment_id = item.get("portal_deployment_id")
132-
break
133-
134-
if deployment_id:
135-
deployment_url = f"{PORTAL_BASE}/publishing/deployments/{deployment_id}"
136-
publish_triggered = False
137-
deadline = time.time() + timeout_minutes * 60
138-
139-
while time.time() <= deadline:
140-
status_url = (
141-
f"{PORTAL_BASE}/api/v1/publisher/status?"
142-
f"id={urllib.parse.quote(deployment_id)}"
143-
)
144-
_, status_payload = request_json("POST", status_url, headers)
145-
final_state = extract_deployment_state(status_payload)
146-
147-
if final_state == "PUBLISHED":
148-
result = "published"
149-
reason = ""
138+
if not reason:
139+
list_url = (
140+
f"{OSSRH_BASE}/manual/search/repositories?"
141+
f"ip=any&profile_id={urllib.parse.quote(namespace)}"
142+
)
143+
_, list_payload = request_json("GET", list_url, headers)
144+
repositories = (
145+
list_payload.get("repositories", [])
146+
if isinstance(list_payload, dict)
147+
else []
148+
)
149+
for item in repositories:
150+
if item.get("key") == repository_key and item.get("portal_deployment_id"):
151+
deployment_id = item.get("portal_deployment_id")
150152
break
151153

152-
if final_state in {"FAILED", "BROKEN", "ERROR"}:
153-
reason = f"Deployment entered terminal state: {final_state}"
154-
break
154+
if deployment_id:
155+
deployment_url = f"{PORTAL_BASE}/publishing/deployments/{deployment_id}"
156+
publish_triggered = False
157+
deadline = time.time() + timeout_minutes * 60
155158

156-
if mode == "portal_api" and final_state == "VALIDATED" and not publish_triggered:
157-
publish_url = (
158-
f"{PORTAL_BASE}/api/v1/publisher/deployment/"
159-
f"{urllib.parse.quote(deployment_id)}"
160-
)
161-
publish_status, publish_payload = request_json(
162-
"POST",
163-
publish_url,
164-
headers,
159+
while time.time() <= deadline:
160+
status_url = (
161+
f"{PORTAL_BASE}/api/v1/publisher/status?"
162+
f"id={urllib.parse.quote(deployment_id)}"
165163
)
166-
if publish_status is None or publish_status >= 400:
167-
reason = (
168-
"Publish API failed: "
169-
f"{publish_payload.get('error', 'HTTP ' + str(publish_status))}"
170-
)
164+
_, status_payload = request_json("POST", status_url, headers)
165+
final_state = extract_deployment_state(status_payload)
166+
167+
if final_state == "PUBLISHED":
168+
result = "published"
169+
reason = ""
171170
break
172-
publish_triggered = True
173171

174-
if mode == "user_managed" and final_state == "VALIDATED":
175-
reason = "Mode user_managed requires manual publish in portal"
176-
break
172+
if final_state in {"FAILED", "BROKEN", "ERROR"}:
173+
reason = f"Deployment entered terminal state: {final_state}"
174+
break
177175

178-
time.sleep(10)
176+
if (
177+
mode == "portal_api"
178+
and final_state == "VALIDATED"
179+
and not publish_triggered
180+
):
181+
publish_url = (
182+
f"{PORTAL_BASE}/api/v1/publisher/deployment/"
183+
f"{urllib.parse.quote(deployment_id)}"
184+
)
185+
publish_status, publish_payload = request_json(
186+
"POST",
187+
publish_url,
188+
headers,
189+
)
190+
if publish_status is None or publish_status >= 400:
191+
publish_error = publish_payload.get("error")
192+
if not publish_error:
193+
publish_error = (
194+
f"HTTP {publish_status}"
195+
if publish_status is not None
196+
else "HTTP unknown"
197+
)
198+
reason = f"Publish API failed: {publish_error}"
199+
break
200+
publish_triggered = True
201+
202+
if mode == "user_managed" and final_state == "VALIDATED":
203+
reason = "Mode user_managed requires manual publish in portal"
204+
break
179205

180-
if result != "published" and not reason and final_state != "unknown":
181-
reason = (
182-
"Timed out waiting for deployment status. "
183-
f"Latest state={final_state}"
184-
)
185-
else:
186-
reason = "No portal deployment id found for repository"
206+
time.sleep(10)
207+
208+
if result != "published" and not reason and final_state != "unknown":
209+
reason = (
210+
"Timed out waiting for deployment status. "
211+
f"Latest state={final_state}"
212+
)
213+
else:
214+
reason = "No portal deployment id found for repository"
187215

188216
if result != "published" and not reason:
189217
reason = "Automatic publish did not complete"

.github/workflows/release.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ jobs:
3939
jar_urls_count: ${{ steps.upload_context.outputs.jar_urls_count }}
4040
pom_urls_count: ${{ steps.upload_context.outputs.pom_urls_count }}
4141
steps:
42-
- uses: actions/checkout@v2
42+
- uses: actions/checkout@v4
4343

4444
- name: Set up Maven Central Repository
45-
uses: actions/setup-java@v1
45+
uses: actions/setup-java@v4
4646
with:
47+
distribution: temurin
4748
java-version: 8
4849
server-id: ${{ github.event.inputs.repository }}
4950
server-username: MAVEN_USERNAME

.github/workflows/sonatype-publish.yml

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ on:
3535
description: 'manual upload mode: portal_api|automatic|user_managed'
3636
required: true
3737
default: 'portal_api'
38+
type: choice
39+
options:
40+
- portal_api
41+
- automatic
42+
- user_managed
3843

3944
jobs:
4045
publish:
@@ -55,17 +60,24 @@ jobs:
5560
python3 .github/scripts/sonatype_publish.py
5661
5762
- name: Sonatype publish summary
63+
env:
64+
PUBLISH_RESULT: ${{ steps.publish.outputs.result }}
65+
PUBLISH_REPOSITORY_KEY: ${{ steps.publish.outputs.repository_key }}
66+
PUBLISH_DEPLOYMENT_ID: ${{ steps.publish.outputs.deployment_id }}
67+
PUBLISH_FINAL_STATE: ${{ steps.publish.outputs.final_state }}
68+
PUBLISH_DEPLOYMENT_URL: ${{ steps.publish.outputs.deployment_url }}
69+
PUBLISH_REASON: ${{ steps.publish.outputs.reason }}
5870
run: |
5971
{
6072
echo "## Sonatype Publish Result"
6173
echo ""
62-
echo "- result: ${{ steps.publish.outputs.result }}"
63-
echo "- repository_key: ${{ steps.publish.outputs.repository_key }}"
64-
echo "- deployment_id: ${{ steps.publish.outputs.deployment_id }}"
65-
echo "- final_state: ${{ steps.publish.outputs.final_state }}"
66-
echo "- deployment_url: ${{ steps.publish.outputs.deployment_url }}"
67-
if [ -n "${{ steps.publish.outputs.reason }}" ]; then
68-
echo "- reason: ${{ steps.publish.outputs.reason }}"
74+
echo "- result: ${PUBLISH_RESULT}"
75+
echo "- repository_key: ${PUBLISH_REPOSITORY_KEY}"
76+
echo "- deployment_id: ${PUBLISH_DEPLOYMENT_ID}"
77+
echo "- final_state: ${PUBLISH_FINAL_STATE}"
78+
echo "- deployment_url: ${PUBLISH_DEPLOYMENT_URL}"
79+
if [ -n "${PUBLISH_REASON}" ]; then
80+
echo "- reason: ${PUBLISH_REASON}"
6981
echo ""
7082
echo "Manual fallback: open https://central.sonatype.com/publishing/deployments and complete publish by deployment id."
7183
fi

0 commit comments

Comments
 (0)