publish #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: publish | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "Run cargo publish in dry-run mode" | |
| required: true | |
| default: true | |
| type: boolean | |
| packages: | |
| description: "Comma-separated package list; empty publishes the full workspace" | |
| required: false | |
| default: "" | |
| type: string | |
| skip_published: | |
| description: "Skip versions that already exist on crates.io during a real publish" | |
| required: true | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: read | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: publish crates | |
| env: | |
| DRY_RUN: ${{ inputs.dry_run }} | |
| SELECTED_PACKAGES: ${{ inputs.packages }} | |
| SKIP_PUBLISHED: ${{ inputs.skip_published }} | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| ordered_packages=( | |
| aelf-proto | |
| aelf-crypto | |
| aelf-client | |
| aelf-keystore | |
| aelf-contract | |
| aelf-sdk | |
| ) | |
| declare -A allowed_packages=() | |
| declare -A requested_packages=() | |
| for pkg in "${ordered_packages[@]}"; do | |
| allowed_packages["$pkg"]=1 | |
| done | |
| if [[ -n "${SELECTED_PACKAGES//[[:space:]]/}" ]]; then | |
| IFS=',' read -r -a raw_packages <<< "$SELECTED_PACKAGES" | |
| for raw_pkg in "${raw_packages[@]}"; do | |
| pkg="${raw_pkg//[[:space:]]/}" | |
| if [[ -z "$pkg" ]]; then | |
| continue | |
| fi | |
| if [[ -z "${allowed_packages[$pkg]:-}" ]]; then | |
| echo "::error::Unsupported package '$pkg'. Allowed packages: ${ordered_packages[*]}" | |
| exit 1 | |
| fi | |
| requested_packages["$pkg"]=1 | |
| done | |
| if [[ "${#requested_packages[@]}" -eq 0 ]]; then | |
| echo "::error::No valid packages were provided." | |
| exit 1 | |
| fi | |
| fi | |
| package_selected() { | |
| local pkg="$1" | |
| if [[ "${#requested_packages[@]}" -eq 0 ]]; then | |
| return 0 | |
| fi | |
| [[ -n "${requested_packages[$pkg]:-}" ]] | |
| } | |
| crate_version() { | |
| cargo pkgid -p "$1" | sed 's/.*@//' | |
| } | |
| crate_status_code() { | |
| local pkg="$1" | |
| local version="$2" | |
| curl -sS -o /dev/null -w '%{http_code}' "https://crates.io/api/v1/crates/${pkg}/${version}" | |
| } | |
| ensure_crate_visible() { | |
| local pkg="$1" | |
| local version="$2" | |
| local max_attempts=30 | |
| local sleep_seconds=10 | |
| for ((attempt = 1; attempt <= max_attempts; attempt++)); do | |
| http_code="$(crate_status_code "$pkg" "$version")" | |
| case "$http_code" in | |
| 200) | |
| echo "${pkg} ${version} is visible on crates.io." | |
| return 0 | |
| ;; | |
| 404) | |
| echo "Waiting for ${pkg} ${version} to become visible on crates.io (${attempt}/${max_attempts})..." | |
| sleep "$sleep_seconds" | |
| ;; | |
| 5??) | |
| echo "::error::crates.io returned ${http_code} while checking ${pkg} ${version}." | |
| return 1 | |
| ;; | |
| *) | |
| echo "::error::Unexpected crates.io status ${http_code} while checking ${pkg} ${version}." | |
| return 1 | |
| ;; | |
| esac | |
| done | |
| echo "::error::Timed out waiting for ${pkg} ${version} to become visible on crates.io." | |
| return 1 | |
| } | |
| if [[ "$DRY_RUN" == "true" && "${#requested_packages[@]}" -eq 0 ]]; then | |
| echo "Running workspace dry-run for the full publish set." | |
| cargo publish --workspace --dry-run --locked | |
| exit 0 | |
| fi | |
| if [[ "$DRY_RUN" != "true" && -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then | |
| echo "::error::CARGO_REGISTRY_TOKEN is required when dry_run=false." | |
| exit 1 | |
| fi | |
| for pkg in "${ordered_packages[@]}"; do | |
| if ! package_selected "$pkg"; then | |
| continue | |
| fi | |
| version="$(crate_version "$pkg")" | |
| echo "Processing ${pkg} ${version}" | |
| if [[ "$DRY_RUN" == "true" ]]; then | |
| cargo publish -p "$pkg" --dry-run --locked | |
| continue | |
| fi | |
| http_code="$(crate_status_code "$pkg" "$version")" | |
| case "$http_code" in | |
| 200) | |
| if [[ "$SKIP_PUBLISHED" == "true" ]]; then | |
| echo "Skipping ${pkg} ${version}; version already exists on crates.io." | |
| continue | |
| fi | |
| echo "::error::${pkg} ${version} already exists on crates.io." | |
| exit 1 | |
| ;; | |
| 404) | |
| ;; | |
| 5??) | |
| echo "::error::crates.io returned ${http_code} while checking ${pkg} ${version}." | |
| exit 1 | |
| ;; | |
| *) | |
| echo "::error::Unexpected crates.io status ${http_code} while checking ${pkg} ${version}." | |
| exit 1 | |
| ;; | |
| esac | |
| cargo publish -p "$pkg" --locked --token "$CARGO_REGISTRY_TOKEN" | |
| ensure_crate_visible "$pkg" "$version" | |
| done |