Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 67 additions & 157 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: publish

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
dry_run:
Expand All @@ -23,172 +26,79 @@ permissions:
contents: read

jobs:
publish:
release-preflight:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-wasip2
- 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 }}
- name: validate tag matches workspace version
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
tag_version="${GITHUB_REF_NAME#v}"
pkgid="$(cargo pkgid -p aelf-sdk)"
workspace_version="${pkgid##*@}"
if [[ "$workspace_version" == "$pkgid" ]]; then
workspace_version="${pkgid##*#}"
fi

package_selected() {
local pkg="$1"
if [[ "${#requested_packages[@]}" -eq 0 ]]; then
return 0
fi
[[ -n "${requested_packages[$pkg]:-}" ]]
}

crate_version() {
local pkgid
local version

pkgid="$(cargo pkgid -p "$1")"
version="${pkgid##*@}"
if [[ "$version" == "$pkgid" ]]; then
version="${pkgid##*#}"
fi
echo "$version"
}

crate_registry_state() {
local pkg="$1"
local version="$2"
local output
local cmd_status

set +e
output="$(cargo info "${pkg}@${version}" --registry crates-io 2>&1)"
cmd_status=$?
set -e

if [[ "$cmd_status" -eq 0 ]]; then
echo "present"
return 0
fi

if grep -qi "could not find" <<< "$output"; then
echo "missing"
return 0
fi

echo "::error::Failed to query crates.io index for ${pkg} ${version}: ${output}" >&2
return 1
}

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
state="$(crate_registry_state "$pkg" "$version")"
case "$state" in
present)
echo "${pkg} ${version} is visible on crates.io."
return 0
;;
missing)
echo "Waiting for ${pkg} ${version} to become visible on crates.io (${attempt}/${max_attempts})..."
sleep "$sleep_seconds"
;;
*)
echo "::error::Unexpected crates.io registry state '${state}' 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" && -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then
echo "::error::CARGO_REGISTRY_TOKEN is required when dry_run=false."
if [[ "$tag_version" != "$workspace_version" ]]; then
echo "::error::Tag version ${tag_version} does not match workspace version ${workspace_version}."
exit 1
fi
- name: install cargo-audit
run: cargo install cargo-audit --locked
- name: fmt
run: cargo fmt --all -- --check
- name: msrv
run: cargo +1.85.0 check --workspace --all-targets --all-features --locked
- name: clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- name: audit
run: cargo audit
- name: examples
run: cargo check --workspace --examples
- name: wasm aelf-client
run: cargo check -p aelf-client --target wasm32-wasip2 --no-default-features
- name: wasm aelf-contract
run: cargo check -p aelf-contract --target wasm32-wasip2 --no-default-features
- name: wasm aelf-sdk
run: cargo check -p aelf-sdk --target wasm32-wasip2 --no-default-features
- name: test
run: cargo test --workspace
- name: public readonly smoke
run: cargo test -p aelf-sdk --test public_readonly_smoke -- --ignored --test-threads=1 --nocapture

tag-publish:
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: release-preflight
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: publish crates
env:
DRY_RUN: "false"
SELECTED_PACKAGES: ""
SKIP_PUBLISHED: "true"
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: ./scripts/publish-crates.sh

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

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

state="$(crate_registry_state "$pkg" "$version")"
case "$state" in
present)
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
;;
missing)
;;
*)
echo "::error::Unexpected crates.io registry state '${state}' while checking ${pkg} ${version}."
exit 1
;;
esac

cargo publish -p "$pkg" --locked --token "$CARGO_REGISTRY_TOKEN"
ensure_crate_visible "$pkg" "$version"
done
manual-publish:
if: github.event_name == 'workflow_dispatch'
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: ./scripts/publish-crates.sh
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
- The facade crate now exposes the transport abstraction needed by native-wasm skill runtimes
- `send_transaction` no longer treats arbitrary non-empty text payloads as success
- typed contract wrappers now lazily reuse the first descriptor per handle, while direct `contract_at(...)` calls still fetch a fresh descriptor for each new handle
- `ChainStatusDto` now accepts both public-node branch map shapes, preventing readonly smoke failures behind mixed main-chain gateway backends

## [0.1.0-alpha.0] - 2026-03-10

Expand Down
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ cargo test -p aelf-sdk --test public_readonly_smoke -- --ignored --test-threads=
- Keep the documented MSRV at Rust `1.85` and preserve the hard `cargo +1.85.0 check --workspace --all-targets --all-features --locked` CI gate.
- Preserve the `wasm32-wasip2` compile gates for `aelf-client`, `aelf-contract`, and `aelf-sdk` when changing transport or feature-flag behavior.

## Release Flow

- Push a tag such as `v0.1.0-alpha.1` to trigger the automated release flow.
- The tag workflow runs release preflight checks first, then publishes the workspace crates in dependency order.
- If a publish run partially succeeds, rerun the `publish` workflow manually with `packages="aelf-contract,aelf-sdk"`-style inputs and keep `skip_published=true`.

## Commit Style

Use Conventional Commit / `git-cz` style messages in English and keep the matching emoji prefix used by the repository workflow.
Loading
Loading