diff --git a/.github/scripts/github_actions_utils.py b/.github/scripts/github_actions_utils.py
deleted file mode 100644
index 8b90c6c4..00000000
--- a/.github/scripts/github_actions_utils.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2026 Apollo Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Shared helpers for GitHub Actions scripts."""
-
-from __future__ import annotations
-
-import os
-
-
-def write_output(key: str, value: str) -> None:
- output_path = os.environ.get("GITHUB_OUTPUT", "").strip()
- if not output_path:
- return
- with open(output_path, "a", encoding="utf-8") as output:
- output.write(f"{key}={value}\n")
diff --git a/.github/scripts/release_extract_upload_context.py b/.github/scripts/release_extract_upload_context.py
deleted file mode 100644
index 52981515..00000000
--- a/.github/scripts/release_extract_upload_context.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2026 Apollo Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Extract uploaded artifact URLs from Maven deploy logs."""
-
-from __future__ import annotations
-
-import json
-import os
-import re
-from pathlib import Path
-
-from github_actions_utils import write_output
-
-
-def main() -> int:
- repository_name = os.environ.get("TARGET_REPOSITORY", "").strip()
- log_file = Path(os.environ.get("DEPLOY_LOG", "maven-deploy.log"))
- context_file = Path(os.environ.get("DEPLOY_ARTIFACTS_FILE", "deploy-artifacts.json"))
-
- log_text = log_file.read_text(encoding="utf-8")
- pattern = re.compile(r"Uploaded to (\S+):\s+(\S+)")
-
- uploaded_urls: list[str] = []
- for target_repo, url in pattern.findall(log_text):
- if target_repo == repository_name:
- uploaded_urls.append(url)
-
- deduped_urls = sorted(set(uploaded_urls))
- jar_urls = [url for url in deduped_urls if url.endswith(".jar")]
- pom_urls = [url for url in deduped_urls if url.endswith(".pom")]
-
- payload = {
- "target_repository": repository_name,
- "uploaded_urls": deduped_urls,
- "jar_urls": jar_urls,
- "pom_urls": pom_urls,
- }
- context_file.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
-
- write_output("uploaded_urls_count", str(len(deduped_urls)))
- write_output("jar_urls_count", str(len(jar_urls)))
- write_output("pom_urls_count", str(len(pom_urls)))
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/.github/scripts/release_resolve_repository_context.py b/.github/scripts/release_resolve_repository_context.py
deleted file mode 100644
index 226bb99a..00000000
--- a/.github/scripts/release_resolve_repository_context.py
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2026 Apollo Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Resolve Sonatype repository context for release deployments."""
-
-from __future__ import annotations
-
-import base64
-import json
-import os
-import urllib.error
-import urllib.parse
-import urllib.request
-from pathlib import Path
-from typing import Any
-
-from github_actions_utils import write_output
-
-OSSRH_BASE = "https://ossrh-staging-api.central.sonatype.com"
-
-
-def request_json(url: str, headers: dict[str, str]) -> tuple[int | None, dict[str, Any]]:
- request = urllib.request.Request(url=url, method="GET", headers=headers)
- try:
- with urllib.request.urlopen(request, timeout=30) as response:
- body = response.read().decode("utf-8")
- if not body:
- return response.status, {}
- try:
- return response.status, json.loads(body)
- except json.JSONDecodeError:
- return response.status, {"raw": body}
- except urllib.error.HTTPError as error:
- body = error.read().decode("utf-8")
- try:
- payload = json.loads(body) if body else {}
- except json.JSONDecodeError:
- payload = {"raw": body}
- payload.setdefault("error", f"HTTP {error.code}")
- return error.code, payload
- except Exception as error: # noqa: BLE001
- return None, {"error": str(error)}
-
-
-def main() -> int:
- target_repository = os.environ.get("TARGET_REPOSITORY", "").strip()
- namespace = os.environ.get("TARGET_NAMESPACE", "").strip()
- username = os.environ.get("MAVEN_USERNAME", "")
- password = os.environ.get("MAVEN_CENTRAL_TOKEN", "")
- context_path = Path(
- os.environ.get("REPOSITORY_CONTEXT_FILE", "repository-context.json")
- )
-
- context: dict[str, Any] = {
- "target_repository": target_repository,
- "namespace": namespace,
- "status": "not_applicable",
- "reason": "repository input is not releases",
- "repository_key": "",
- "portal_deployment_id": "",
- "search_candidates": [],
- }
-
- if target_repository == "releases":
- if not username or not password:
- context["status"] = "manual_required"
- context["reason"] = "Missing MAVEN_USERNAME/MAVEN_CENTRAL_TOKEN"
- else:
- token = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode("utf-8")
- headers = {
- "Authorization": f"Bearer {token}",
- "Accept": "application/json",
- }
-
- searches = [
- ("open", "client"),
- ("closed", "client"),
- ("open", "any"),
- ("closed", "any"),
- ]
- selected: dict[str, Any] | None = None
- last_error = ""
-
- for state, ip in searches:
- url = (
- f"{OSSRH_BASE}/manual/search/repositories?"
- f"profile_id={urllib.parse.quote(namespace)}"
- f"&state={urllib.parse.quote(state)}"
- f"&ip={urllib.parse.quote(ip)}"
- )
- status, payload = request_json(url, headers)
- if status is None:
- last_error = payload.get("error", "unknown error")
- context["search_candidates"].append(
- {
- "state": state,
- "ip": ip,
- "status": None,
- "count": 0,
- "error": last_error,
- }
- )
- continue
-
- if status < 200 or status >= 300:
- http_error = payload.get("error", f"HTTP {status}")
- last_error = http_error
- context["search_candidates"].append(
- {
- "state": state,
- "ip": ip,
- "status": status,
- "count": 0,
- "error": http_error,
- }
- )
- continue
-
- repositories = (
- payload.get("repositories", []) if isinstance(payload, dict) else []
- )
- context["search_candidates"].append(
- {"state": state, "ip": ip, "status": status, "count": len(repositories)}
- )
- if repositories:
- selected = repositories[0]
- break
-
- if selected:
- context["status"] = "resolved"
- context["reason"] = ""
- context["repository_key"] = selected.get("key", "") or ""
- context["portal_deployment_id"] = (
- selected.get("portal_deployment_id", "") or ""
- )
- else:
- context["status"] = "manual_required"
- context["reason"] = last_error or "No staging repository key found"
-
- context_path.write_text(json.dumps(context, indent=2) + "\n", encoding="utf-8")
- write_output("repository_key", context.get("repository_key", ""))
- write_output("portal_deployment_id", context.get("portal_deployment_id", ""))
- write_output("status", context.get("status", ""))
- write_output("reason", context.get("reason", ""))
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/.github/scripts/release_write_summary.py b/.github/scripts/release_write_summary.py
deleted file mode 100644
index 4558e5db..00000000
--- a/.github/scripts/release_write_summary.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2026 Apollo Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Write release publish context summary for GitHub Actions."""
-
-from __future__ import annotations
-
-import json
-import os
-from pathlib import Path
-from typing import Any
-
-
-def read_json(path: Path) -> dict[str, Any]:
- if not path.exists():
- return {}
- try:
- return json.loads(path.read_text(encoding="utf-8"))
- except json.JSONDecodeError:
- return {}
-
-
-def main() -> int:
- summary_path = os.environ.get("GITHUB_STEP_SUMMARY", "").strip()
- if not summary_path:
- return 0
-
- deploy_path = Path(os.environ.get("DEPLOY_ARTIFACTS_FILE", "deploy-artifacts.json"))
- repository_path = Path(
- os.environ.get("REPOSITORY_CONTEXT_FILE", "repository-context.json")
- )
-
- deploy = read_json(deploy_path)
- repository = read_json(repository_path)
-
- lines = [
- "## Publish Context",
- "",
- f"- target repository: {deploy.get('target_repository', '')}",
- f"- uploaded URLs: {len(deploy.get('uploaded_urls', []))}",
- f"- jar URLs: {len(deploy.get('jar_urls', []))}",
- f"- pom URLs: {len(deploy.get('pom_urls', []))}",
- f"- staging key status: {repository.get('status', '')}",
- f"- repository_key: {repository.get('repository_key', '')}",
- f"- portal_deployment_id: {repository.get('portal_deployment_id', '')}",
- ]
- reason = repository.get("reason", "")
- if reason:
- lines.append(f"- reason: {reason}")
-
- with open(summary_path, "a", encoding="utf-8") as output:
- output.write("\n".join(lines) + "\n")
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/.github/scripts/sonatype_publish.py b/.github/scripts/sonatype_publish.py
deleted file mode 100644
index 559c57d5..00000000
--- a/.github/scripts/sonatype_publish.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2026 Apollo Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Trigger and monitor Sonatype portal publish flow."""
-
-from __future__ import annotations
-
-import base64
-import json
-import os
-import time
-import urllib.error
-import urllib.parse
-import urllib.request
-from typing import Any
-
-from github_actions_utils import write_output
-
-OSSRH_BASE = "https://ossrh-staging-api.central.sonatype.com"
-PORTAL_BASE = "https://central.sonatype.com"
-
-
-def request_json(
- method: str,
- url: str,
- headers: dict[str, str],
-) -> tuple[int | None, dict[str, Any]]:
- request = urllib.request.Request(url=url, method=method, headers=headers)
- try:
- with urllib.request.urlopen(request, timeout=30) as response:
- body = response.read().decode("utf-8")
- if not body:
- return response.status, {}
- try:
- return response.status, json.loads(body)
- except json.JSONDecodeError:
- return response.status, {"raw": body}
- except urllib.error.HTTPError as error:
- body = error.read().decode("utf-8")
- try:
- payload = json.loads(body) if body else {}
- except json.JSONDecodeError:
- payload = {"raw": body}
- payload.setdefault("error", f"HTTP {error.code}")
- return error.code, payload
- except Exception as error: # noqa: BLE001
- return None, {"error": str(error)}
-
-
-def extract_deployment_state(payload: dict[str, Any]) -> str:
- for key in ("deploymentState", "deployment_state", "state"):
- value = payload.get(key)
- if isinstance(value, str):
- return value
- return "unknown"
-
-
-def to_int(value: str, default: int) -> int:
- try:
- return int(value)
- except ValueError:
- return default
-
-
-def main() -> int:
- namespace = os.environ.get("INPUT_NAMESPACE", "com.ctrip.framework.apollo").strip()
- repository_key = os.environ.get("INPUT_REPOSITORY_KEY", "").strip()
- timeout_minutes = to_int(os.environ.get("INPUT_TIMEOUT_MINUTES", "60"), 60)
- mode = os.environ.get("INPUT_MODE", "portal_api").strip().lower()
-
- username = os.environ.get("MAVEN_USERNAME", "")
- password = os.environ.get("MAVEN_CENTRAL_TOKEN", "")
-
- result = "manual_required"
- final_state = "unknown"
- deployment_id = ""
- deployment_url = ""
- reason = ""
-
- if not username or not password:
- reason = "Missing MAVEN_USERNAME/MAVEN_CENTRAL_TOKEN secrets"
- else:
- token = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode("utf-8")
- headers = {
- "Authorization": f"Bearer {token}",
- "Accept": "application/json",
- }
-
- if not repository_key:
- searches = ("open", "closed")
- search_errors: list[str] = []
- successful_search = False
- for state in searches:
- search_url = (
- f"{OSSRH_BASE}/manual/search/repositories?"
- f"ip=any&profile_id={urllib.parse.quote(namespace)}"
- f"&state={urllib.parse.quote(state)}"
- )
- search_status, payload = request_json("GET", search_url, headers)
- if search_status is None or search_status < 200 or search_status >= 300:
- search_error = payload.get("error") if isinstance(payload, dict) else ""
- if not search_error:
- search_error = (
- f"HTTP {search_status}"
- if search_status is not None
- else "HTTP unknown"
- )
- search_errors.append(
- f"Repository search failed ({state}): {search_error}"
- )
- continue
-
- successful_search = True
- repositories = payload.get("repositories", []) if isinstance(payload, dict) else []
- if repositories:
- repository_key = repositories[0].get("key", "") or ""
- break
-
- if not repository_key:
- if search_errors and not successful_search:
- reason = "; ".join(search_errors)
- else:
- reason = "No staging repository key found"
- else:
- upload_url = (
- f"{OSSRH_BASE}/manual/upload/repository/{urllib.parse.quote(repository_key)}"
- f"?publishing_type={urllib.parse.quote(mode)}"
- )
- upload_status, upload_payload = request_json("POST", upload_url, headers)
- if upload_status is None:
- reason = f"Upload API failed: {upload_payload.get('error', 'unknown error')}"
- elif upload_status >= 400:
- upload_error = upload_payload.get("error")
- if not upload_error:
- upload_error = f"HTTP {upload_status}"
- reason = (
- f"Upload API failed: {upload_error}"
- )
-
- if not reason:
- list_url = (
- f"{OSSRH_BASE}/manual/search/repositories?"
- f"ip=any&profile_id={urllib.parse.quote(namespace)}"
- )
- list_status, list_payload = request_json("GET", list_url, headers)
- if list_status is None or list_status < 200 or list_status >= 300:
- list_error = list_payload.get("error") if isinstance(list_payload, dict) else ""
- if not list_error:
- list_error = (
- f"HTTP {list_status}"
- if list_status is not None
- else "HTTP unknown"
- )
- reason = f"Repository list API failed after upload: {list_error}"
- else:
- repositories = (
- list_payload.get("repositories", [])
- if isinstance(list_payload, dict)
- else []
- )
- for item in repositories:
- if item.get("key") == repository_key and item.get("portal_deployment_id"):
- deployment_id = item.get("portal_deployment_id")
- break
-
- if deployment_id:
- deployment_url = f"{PORTAL_BASE}/publishing/deployments/{deployment_id}"
- publish_triggered = False
- deadline = time.time() + timeout_minutes * 60
-
- while time.time() <= deadline:
- status_url = (
- f"{PORTAL_BASE}/api/v1/publisher/status?"
- f"id={urllib.parse.quote(deployment_id)}"
- )
- poll_status, status_payload = request_json(
- "POST",
- status_url,
- headers,
- )
- if poll_status is None or poll_status < 200 or poll_status >= 300:
- poll_error = (
- status_payload.get("error")
- if isinstance(status_payload, dict)
- else ""
- )
- if not poll_error:
- poll_error = (
- f"HTTP {poll_status}"
- if poll_status is not None
- else "HTTP unknown"
- )
- reason = f"Status polling API failed: {poll_error}"
- break
-
- final_state = extract_deployment_state(status_payload)
-
- if final_state == "PUBLISHED":
- result = "published"
- reason = ""
- break
-
- if final_state in {"FAILED", "BROKEN", "ERROR"}:
- reason = f"Deployment entered terminal state: {final_state}"
- break
-
- if (
- mode == "portal_api"
- and final_state == "VALIDATED"
- and not publish_triggered
- ):
- publish_url = (
- f"{PORTAL_BASE}/api/v1/publisher/deployment/"
- f"{urllib.parse.quote(deployment_id)}"
- )
- publish_status, publish_payload = request_json(
- "POST",
- publish_url,
- headers,
- )
- if publish_status is None or publish_status >= 400:
- publish_error = publish_payload.get("error")
- if not publish_error:
- publish_error = (
- f"HTTP {publish_status}"
- if publish_status is not None
- else "HTTP unknown"
- )
- reason = f"Publish API failed: {publish_error}"
- break
- publish_triggered = True
-
- if mode == "user_managed" and final_state == "VALIDATED":
- reason = "Mode user_managed requires manual publish in portal"
- break
-
- time.sleep(10)
-
- if result != "published" and not reason:
- reason = (
- "Timed out waiting for deployment status. "
- f"Latest state={final_state}"
- )
- else:
- reason = "No portal deployment id found for repository"
-
- if result != "published" and not reason:
- reason = "Automatic publish did not complete"
-
- write_output("result", result)
- write_output("repository_key", repository_key)
- write_output("deployment_id", deployment_id)
- write_output("deployment_url", deployment_url)
- write_output("final_state", final_state)
- write_output("reason", reason)
-
- display_key = repository_key or "-"
- display_deployment = deployment_id or "-"
- display_url = deployment_url or "-"
- print(
- "SONATYPE_RESULT "
- f"result={result} "
- f"repository_key={display_key} "
- f"deployment_id={display_deployment} "
- f"final_state={final_state} "
- f"deployment_url={display_url}"
- )
- if reason:
- print(f"SONATYPE_REASON {reason}")
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5e466865..e750b8ca 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,86 +19,29 @@ name: publish sdks
on:
workflow_dispatch:
- inputs:
- repository:
- description: 'Maven Repository(snapshots or releases)'
- required: true
- default: 'snapshots'
- namespace:
- description: 'Sonatype namespace used to search staging repositories'
- required: true
- default: 'com.ctrip.framework.apollo'
jobs:
publish:
runs-on: ubuntu-latest
- outputs:
- repository_key: ${{ steps.repository_context.outputs.repository_key }}
- portal_deployment_id: ${{ steps.repository_context.outputs.portal_deployment_id }}
- uploaded_urls_count: ${{ steps.upload_context.outputs.uploaded_urls_count }}
- jar_urls_count: ${{ steps.upload_context.outputs.jar_urls_count }}
- pom_urls_count: ${{ steps.upload_context.outputs.pom_urls_count }}
steps:
- uses: actions/checkout@v4
- - name: Set up Maven Central Repository
+ - name: Set up Maven Central publishing credentials
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
- server-id: ${{ github.event.inputs.repository }}
+ server-id: central
server-username: MAVEN_USERNAME
server-password: MAVEN_CENTRAL_TOKEN
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
- - name: Publish to Apache Maven Central
+ - name: Publish to Sonatype Central
run: |
set -eo pipefail
- mvn clean deploy -DskipTests=true -Prelease \
- "-Dreleases.repo=https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/" \
- "-Dsnapshots.repo=https://central.sonatype.com/repository/maven-snapshots/" \
- 2>&1 | tee maven-deploy.log
+ mvn clean deploy -DskipTests=true -Prelease
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
-
- - name: Extract uploaded artifact URLs
- id: upload_context
- env:
- TARGET_REPOSITORY: ${{ github.event.inputs.repository }}
- DEPLOY_LOG: maven-deploy.log
- DEPLOY_ARTIFACTS_FILE: deploy-artifacts.json
- run: |
- python3 .github/scripts/release_extract_upload_context.py
-
- - name: Resolve staging repository key
- id: repository_context
- env:
- TARGET_REPOSITORY: ${{ github.event.inputs.repository }}
- TARGET_NAMESPACE: ${{ github.event.inputs.namespace }}
- MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
- MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
- REPOSITORY_CONTEXT_FILE: repository-context.json
- run: |
- python3 .github/scripts/release_resolve_repository_context.py
-
- - name: Upload release publish context
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: release-deploy-context
- if-no-files-found: warn
- path: |
- maven-deploy.log
- deploy-artifacts.json
- repository-context.json
-
- - name: Publish summary
- if: always()
- env:
- DEPLOY_ARTIFACTS_FILE: deploy-artifacts.json
- REPOSITORY_CONTEXT_FILE: repository-context.json
- run: |
- python3 .github/scripts/release_write_summary.py
diff --git a/.github/workflows/sonatype-publish.yml b/.github/workflows/sonatype-publish.yml
deleted file mode 100644
index a96cd600..00000000
--- a/.github/workflows/sonatype-publish.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Copyright 2026 Apollo Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-name: sonatype publish
-
-on:
- workflow_dispatch:
- inputs:
- namespace:
- description: 'Sonatype namespace (e.g. com.ctrip.framework.apollo)'
- required: true
- default: 'com.ctrip.framework.apollo'
- repository_key:
- description: 'Optional staging repository key; leave blank to auto-detect'
- required: false
- default: ''
- timeout_minutes:
- description: 'Max minutes to wait for deployment status polling'
- required: true
- default: '60'
- mode:
- description: 'manual upload mode: portal_api|automatic|user_managed'
- required: true
- default: 'portal_api'
- type: choice
- options:
- - portal_api
- - automatic
- - user_managed
-
-jobs:
- publish:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Publish via Sonatype API
- id: publish
- env:
- INPUT_NAMESPACE: ${{ github.event.inputs.namespace }}
- INPUT_REPOSITORY_KEY: ${{ github.event.inputs.repository_key }}
- INPUT_TIMEOUT_MINUTES: ${{ github.event.inputs.timeout_minutes }}
- INPUT_MODE: ${{ github.event.inputs.mode }}
- MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
- MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
- run: |
- python3 .github/scripts/sonatype_publish.py
-
- - name: Sonatype publish summary
- if: always()
- env:
- PUBLISH_RESULT: ${{ steps.publish.outputs.result }}
- PUBLISH_REPOSITORY_KEY: ${{ steps.publish.outputs.repository_key }}
- PUBLISH_DEPLOYMENT_ID: ${{ steps.publish.outputs.deployment_id }}
- PUBLISH_FINAL_STATE: ${{ steps.publish.outputs.final_state }}
- PUBLISH_DEPLOYMENT_URL: ${{ steps.publish.outputs.deployment_url }}
- PUBLISH_REASON: ${{ steps.publish.outputs.reason }}
- run: |
- {
- echo "## Sonatype Publish Result"
- echo ""
- echo "- result: ${PUBLISH_RESULT}"
- echo "- repository_key: ${PUBLISH_REPOSITORY_KEY}"
- echo "- deployment_id: ${PUBLISH_DEPLOYMENT_ID}"
- echo "- final_state: ${PUBLISH_FINAL_STATE}"
- echo "- deployment_url: ${PUBLISH_DEPLOYMENT_URL}"
- if [ -n "${PUBLISH_REASON}" ]; then
- echo "- reason: ${PUBLISH_REASON}"
- echo ""
- echo "Manual fallback: open https://central.sonatype.com/publishing/deployments and complete publish by deployment id."
- fi
- } >> "$GITHUB_STEP_SUMMARY"
diff --git a/pom.xml b/pom.xml
index 70ff7174..0c35c8a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,7 @@
3.2.2
2.5.2
2.8.2
+ 0.10.0
3.4.0
3.0.1
@@ -457,19 +458,18 @@
org.apache.maven.plugins
maven-gpg-plugin
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central-publishing-maven-plugin.version}
+ true
+
+ true
+ PUBLISHED
+
+
-
-
-
- releases
- ${releases.repo}
-
-
- snapshots
- ${snapshots.repo}
-
-