|
1 | 1 | #!/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. |
2 | 15 | """Trigger and monitor Sonatype portal publish flow.""" |
3 | 16 |
|
4 | 17 | from __future__ import annotations |
@@ -115,75 +128,90 @@ def main() -> int: |
115 | 128 | if upload_status is None: |
116 | 129 | reason = f"Upload API failed: {upload_payload.get('error', 'unknown error')}" |
117 | 130 | elif upload_status >= 400: |
| 131 | + upload_error = upload_payload.get("error") |
| 132 | + if not upload_error: |
| 133 | + upload_error = f"HTTP {upload_status}" |
118 | 134 | reason = ( |
119 | | - f"Upload API returned HTTP {upload_status}: " |
120 | | - f"{upload_payload.get('error', 'unknown error')}" |
| 135 | + f"Upload API failed: {upload_error}" |
121 | 136 | ) |
122 | 137 |
|
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") |
150 | 152 | break |
151 | 153 |
|
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 |
155 | 158 |
|
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)}" |
165 | 163 | ) |
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 = "" |
171 | 170 | break |
172 | | - publish_triggered = True |
173 | 171 |
|
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 |
177 | 175 |
|
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 |
179 | 205 |
|
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" |
187 | 215 |
|
188 | 216 | if result != "published" and not reason: |
189 | 217 | reason = "Automatic publish did not complete" |
|
0 commit comments