testing some rpcserver calls #682
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: .NET Core Test and Publish | |
| on: | |
| push: | |
| branches: [master-n3] | |
| pull_request: | |
| env: | |
| DOTNET_VERSION: 10.0.x | |
| jobs: | |
| Test: | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Setup .NET Core | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Check format | |
| if: runner.os == 'Linux' | |
| run: dotnet format --no-restore --verify-no-changes --verbosity diagnostic | |
| - name: Build CLI | |
| if: runner.os == 'Linux' | |
| run: | | |
| dotnet publish -o ./out -c Release src/Neo.CLI | |
| find ./out -name 'config.json' | xargs perl -pi -e 's|LevelDBStore|MemoryStore|g' | |
| - name: Install dependencies | |
| if: runner.os == 'Linux' | |
| run: sudo apt-get install libleveldb-dev expect | |
| - name: Run tests with expect | |
| if: runner.os == 'Linux' | |
| run: expect ./.github/workflows/test-neo-cli.expect | |
| - name: Run Unit Tests | |
| if: runner.os == 'Windows' | |
| run: | | |
| forfiles /p tests /m *.csproj /s /c "cmd /c dotnet add @PATH package coverlet.msbuild" | |
| dotnet test /p:CollectCoverage=true /p:CoverletOutput='${{ github.workspace }}/TestResults/coverage/' /p:MergeWith='${{ github.workspace }}/TestResults/coverage/coverage.json' /p:CoverletOutputFormat=lcov%2cjson -m:1 | |
| - name: Codecov | |
| if: runner.os == 'Windows' | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ${{ github.workspace }}/TestResults/coverage/coverage.info | |
| fail_ci_if_error: false | |
| Test-with-plugins: | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Setup .NET Core | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Build CLI | |
| run: | | |
| dotnet build -c Release | |
| dotnet publish -o ./out -c Release src/Neo.CLI | |
| - name: Create Plugins folder and copy plugin DLLs | |
| run: | | |
| mkdir -p ./out/Plugins | |
| for plugin_dir in plugins/*/; do | |
| plugin_name=$(basename "$plugin_dir") | |
| build_output="$plugin_dir/bin/Release/net10.0" | |
| if [ -d "$build_output" ]; then | |
| mkdir -p "./out/Plugins/$plugin_name" | |
| cp -r "$build_output"/* "./out/Plugins/$plugin_name/" | |
| fi | |
| done | |
| # Remove duplicated RpcServer.dll from TokensTracker and StorageDumper (they can't load twice) | |
| rm -f ./out/Plugins/TokensTracker/RpcServer.dll | |
| rm -f ./out/Plugins/StateService/RpcServer.dll | |
| - name: Install dependencies | |
| run: sudo apt-get install -y libleveldb-dev expect jq | |
| - name: Run tests with expect | |
| run: expect ./.github/workflows/test-neo-cli-plugins.expect | |
| - name: Start neo-cli with plugins | |
| run: | | |
| dotnet out/neo-cli.dll & | |
| NEO_PID=$! | |
| echo "NEO_PID=$NEO_PID" >> $GITHUB_ENV | |
| sleep 10 | |
| - name: Test RPC calls | |
| run: | | |
| chmod +x ./.github/workflows/test-rpc-calls.sh | |
| ./.github/workflows/test-rpc-calls.sh | |
| - name: Stop neo-cli | |
| run: | | |
| if [ -n "$NEO_PID" ]; then | |
| kill $NEO_PID || true | |
| fi | |
| Release: | |
| if: github.ref == 'refs/heads/master-n3' && github.repository == 'neo-project/neo-node' | |
| needs: Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Get version | |
| id: get_version | |
| run: | | |
| sudo apt install xmlstarlet -qq | |
| # Only release when <Version> is explicitly set (dev builds keep <VersionPrefix> and should not release). | |
| VERSION=$(xmlstarlet sel -N i=http://schemas.microsoft.com/developer/msbuild/2003 -t -v "//i:Version/text()" src/Directory.Build.props 2>/dev/null || echo "") | |
| [ -z "$VERSION" ] && VERSION=$(find plugins -name Directory.Build.props | xargs xmlstarlet sel -N i=http://schemas.microsoft.com/developer/msbuild/2003 -t -v "//i:Version/text()" 2>/dev/null || echo "") | |
| [ -n "$VERSION" ] && echo "version=v$VERSION" >> $GITHUB_OUTPUT || echo "version=" >> $GITHUB_OUTPUT | |
| - name: Check tag | |
| if: steps.get_version.outputs.version != '' && startsWith(steps.get_version.outputs.version, 'v') | |
| id: check_tag | |
| run: curl -s -I ${{ format('https://github.com/{0}/releases/tag/{1}', github.repository, steps.get_version.outputs.version) }} | head -n 1 | cut -d$' ' -f2 | xargs printf "statusCode=%s" | xargs echo >> $GITHUB_OUTPUT | |
| - name: Create release | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if ! gh release view ${{ steps.get_version.outputs.version }} >/dev/null 2>&1; then | |
| if [[ "${{ steps.get_version.outputs.version }}" == *"-"* ]]; then | |
| gh release create ${{ steps.get_version.outputs.version }} \ | |
| --title "${{ steps.get_version.outputs.version }}" \ | |
| --notes "Release ${{ steps.get_version.outputs.version }}" \ | |
| --draft \ | |
| --prerelease | |
| else | |
| gh release create ${{ steps.get_version.outputs.version }} \ | |
| --title "${{ steps.get_version.outputs.version }}" \ | |
| --notes "Release ${{ steps.get_version.outputs.version }}" \ | |
| --draft | |
| fi | |
| fi | |
| - name: Setup .NET Core | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Install zip tool | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| run: sudo apt-get update && sudo apt-get install -y zip | |
| - name: Build LevelDBStore plugin for all platforms | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| run: | | |
| dotnet build plugins/LevelDBStore/LevelDBStore.csproj -c Release | |
| - name: Build and package neo-cli for all platforms | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| run: | | |
| # Define platforms | |
| platforms=("portable" "linux-x64" "linux-arm64" "win-x64" "osx-x64" "osx-arm64") | |
| # LevelDBStore output directory (where native libs are copied after build) | |
| LEVELDB_OUTPUT="plugins/LevelDBStore/bin/Release/net10.0" | |
| # LevelDBStore intermediate directory (where native libs are downloaded) | |
| LEVELDB_OBJ="plugins/LevelDBStore/bin/Release/net10.0/obj/LevelDBStore" | |
| copy_native_lib() { | |
| local rid=$1 | |
| local lib_name=$2 | |
| local target_dir=$3 | |
| local output_path="$LEVELDB_OUTPUT/runtimes/$rid/native/$lib_name" | |
| local obj_path="$LEVELDB_OBJ/libleveldb-$rid/runtimes/$rid/native/$lib_name" | |
| local zip_dir="$LEVELDB_OBJ/libleveldb-$rid" | |
| if [ -f "$output_path" ]; then | |
| cp "$output_path" "$target_dir/" | |
| elif [ -f "$obj_path" ]; then | |
| cp "$obj_path" "$target_dir/" | |
| else | |
| return | |
| fi | |
| # For macOS, also copy any extra .dylib files from the LevelDB package (best-effort) | |
| if [[ "$rid" == osx-* ]] && [ -d "$zip_dir" ]; then | |
| find "$zip_dir" -name "*.dylib" -type f -exec cp {} "$target_dir/" \; 2>/dev/null || true | |
| fi | |
| } | |
| for platform in "${platforms[@]}"; do | |
| if [ "$platform" == "portable" ]; then | |
| dotnet publish src/Neo.CLI/Neo.CLI.csproj -c Release -o "./publish/$platform" --self-contained false | |
| else | |
| dotnet publish src/Neo.CLI/Neo.CLI.csproj -c Release -o "./publish/$platform" -r "$platform" --self-contained true | |
| fi | |
| mkdir -p "./publish/$platform/Plugins/LevelDBStore" | |
| cp "$LEVELDB_OUTPUT/LevelDBStore.dll" "./publish/$platform/Plugins/LevelDBStore/" 2>/dev/null || true | |
| if [ "$platform" == "portable" ]; then | |
| copy_native_lib "linux-x64" "libleveldb.so" "./publish/$platform" | |
| copy_native_lib "win-x64" "libleveldb.dll" "./publish/$platform" | |
| copy_native_lib "osx-x64" "libleveldb.dylib" "./publish/$platform" | |
| else | |
| case "$platform" in | |
| "linux-x64"|"linux-arm64") copy_native_lib "$platform" "libleveldb.so" "./publish/$platform" ;; | |
| "win-x64") copy_native_lib "$platform" "libleveldb.dll" "./publish/$platform" ;; | |
| "osx-x64"|"osx-arm64") copy_native_lib "$platform" "libleveldb.dylib" "./publish/$platform" ;; | |
| esac | |
| fi | |
| cd "./publish/$platform" && zip -r "../../neo-cli-$platform.zip" . && cd ../.. | |
| done | |
| - name: Build and package plugins (zip per plugin) | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| run: | | |
| set -euo pipefail | |
| # Use neo-cli portable publish output as the baseline set of assemblies | |
| if [ ! -d "./publish/portable" ]; then | |
| echo "Error: ./publish/portable not found. neo-cli portable must be published first." | |
| exit 1 | |
| fi | |
| mkdir -p "./plugin-publish" "./plugin-zip" | |
| # Collect baseline DLL filenames shipped with neo-cli portable | |
| mapfile -t cli_dlls < <(find "./publish/portable" -maxdepth 1 -type f -name "*.dll" -printf "%f\n" | sort -u) | |
| declare -A cli_dll_set | |
| for f in "${cli_dlls[@]}"; do cli_dll_set["$f"]=1; done | |
| # Package every plugin project under plugins/*/*.csproj (stage only, zip after all are staged) | |
| for csproj in plugins/*/*.csproj; do | |
| plugin_dir="$(dirname "$csproj")" | |
| plugin_name="$(basename "$plugin_dir")" | |
| # Publish plugin (framework-dependent) to get a clean dependency set | |
| plugin_out="./plugin-publish/$plugin_name" | |
| rm -rf "$plugin_out" | |
| dotnet publish "$csproj" -c Release -o "$plugin_out" --self-contained false | |
| # MPTTrie is only used as a dependency (by StateService). We publish it so that MPTTrie.dll | |
| # exists under plugin-publish/, but we don't stage or zip it as a standalone plugin. | |
| if [ "$plugin_name" == "MPTTrie" ]; then | |
| continue | |
| fi | |
| plugin_json="$plugin_dir/$plugin_name.json" | |
| # Staging layout required by release consumers: | |
| # Plugins/<PluginName>/{PluginName}.dll (+ optional {PluginName}.json) + only required extra dependency dlls | |
| stage_root="./plugin-zip/$plugin_name" | |
| stage_dir="$stage_root/Plugins/$plugin_name" | |
| rm -rf "$stage_root" | |
| mkdir -p "$stage_dir" | |
| # Always include the plugin's own assembly | |
| if [ ! -f "$plugin_out/$plugin_name.dll" ]; then | |
| echo "Error: Missing published plugin dll: $plugin_out/$plugin_name.dll" | |
| exit 1 | |
| fi | |
| cp "$plugin_out/$plugin_name.dll" "$stage_dir/" | |
| # Include plugin json if present | |
| if [ -f "$plugin_json" ]; then | |
| cp "$plugin_json" "$stage_dir/" | |
| fi | |
| # Only include extra dependency DLLs for plugins that explicitly reference NuGet packages. | |
| # This keeps "pure" project-reference plugins (e.g. DBFTPlugin) minimal: {Plugin}.dll (+ optional json). | |
| if grep -q "<PackageReference" "$csproj"; then | |
| # Include only dlls that neo-cli portable does NOT already ship | |
| shopt -s nullglob | |
| for dep in "$plugin_out"/*.dll; do | |
| dep_name="$(basename "$dep")" | |
| if [ "$dep_name" == "$plugin_name.dll" ]; then | |
| continue | |
| fi | |
| if [ -z "${cli_dll_set["$dep_name"]+x}" ]; then | |
| cp "$dep" "$stage_dir/" | |
| fi | |
| done | |
| shopt -u nullglob | |
| fi | |
| done | |
| # After all plugins are published, ensure StateService staging includes its MPTTrie dependency. | |
| mpt_trie_dll="./plugin-publish/MPTTrie/MPTTrie.dll" | |
| state_stage="./plugin-zip/StateService/Plugins/StateService" | |
| if [ -f "$mpt_trie_dll" ] && [ -d "$state_stage" ]; then | |
| cp "$mpt_trie_dll" "$state_stage/" | |
| fi | |
| # Finally, create zip per plugin from staged directories (excluding MPTTrie by design) | |
| for csproj in plugins/*/*.csproj; do | |
| plugin_dir="$(dirname "$csproj")" | |
| plugin_name="$(basename "$plugin_dir")" | |
| [ "$plugin_name" == "MPTTrie" ] && continue | |
| stage_root="./plugin-zip/$plugin_name" | |
| if [ -d "$stage_root" ]; then | |
| (cd "$stage_root" && zip -r "../../$plugin_name.zip" "Plugins") | |
| fi | |
| done | |
| - name: Upload CLI packages to release | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| RELEASE_TAG="${{ steps.get_version.outputs.version }}" | |
| # Verify release exists | |
| if ! gh release view "$RELEASE_TAG" >/dev/null 2>&1; then | |
| echo "Error: Release $RELEASE_TAG does not exist" | |
| exit 1 | |
| fi | |
| # Verify all required zip files exist | |
| missing_files=() | |
| for file in neo-cli-portable.zip neo-cli-linux-x64.zip neo-cli-linux-arm64.zip neo-cli-win-x64.zip neo-cli-osx-x64.zip neo-cli-osx-arm64.zip; do | |
| [ ! -f "$file" ] && missing_files+=("$file") | |
| done | |
| # Verify plugin zip files exist (one per plugins/*/*.csproj, except MPTTrie which is only a dependency) | |
| for csproj in plugins/*/*.csproj; do | |
| plugin_dir="$(dirname "$csproj")" | |
| plugin_name="$(basename "$plugin_dir")" | |
| [ "$plugin_name" == "MPTTrie" ] && continue | |
| [ ! -f "$plugin_name.zip" ] && missing_files+=("$plugin_name.zip") | |
| done | |
| if [ ${#missing_files[@]} -gt 0 ]; then | |
| echo "Error: Missing zip files: ${missing_files[*]}" | |
| exit 1 | |
| fi | |
| # Upload all zip files to release | |
| failed_files=() | |
| for file in neo-cli-portable.zip neo-cli-linux-x64.zip neo-cli-linux-arm64.zip neo-cli-win-x64.zip neo-cli-osx-x64.zip neo-cli-osx-arm64.zip; do | |
| if ! gh release upload "$RELEASE_TAG" "$file" --clobber; then | |
| failed_files+=("$file") | |
| fi | |
| done | |
| # Upload plugin zip files to release (excluding MPTTrie) | |
| for csproj in plugins/*/*.csproj; do | |
| plugin_dir="$(dirname "$csproj")" | |
| plugin_name="$(basename "$plugin_dir")" | |
| [ "$plugin_name" == "MPTTrie" ] && continue | |
| if ! gh release upload "$RELEASE_TAG" "$plugin_name.zip" --clobber; then | |
| failed_files+=("$plugin_name.zip") | |
| fi | |
| done | |
| if [ ${#failed_files[@]} -gt 0 ]; then | |
| echo "Error: Failed to upload: ${failed_files[*]}" | |
| exit 1 | |
| fi | |
| - name: Publish core packages to NuGet | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| run: | | |
| dotnet pack -o out -c Release | |
| # Push only non-plugin packages here (plugins handled in a separate step) | |
| for pkg in out/*.nupkg; do | |
| name="$(basename "$pkg")" | |
| case "$name" in | |
| Neo.Plugins.*) continue ;; | |
| esac | |
| dotnet nuget push "$pkg" -s https://api.nuget.org/v3/index.json -k "${NUGET_TOKEN}" --skip-duplicate | |
| done | |
| env: | |
| NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} | |
| - name: Publish plugin packages to NuGet | |
| if: steps.check_tag.outputs.statusCode == '404' | |
| run: | | |
| # Ensure plugin packages are packed (reuse existing 'out' folder to avoid rebuilding everything) | |
| for csproj in plugins/*/*.csproj; do | |
| dotnet pack "$csproj" -c Release -o out | |
| done | |
| # Push all Neo.Plugins.* packages to NuGet.org | |
| for pkg in out/Neo.Plugins.*.nupkg; do | |
| [ -f "$pkg" ] || continue | |
| dotnet nuget push "$pkg" -s https://api.nuget.org/v3/index.json -k "${NUGET_TOKEN}" --skip-duplicate | |
| done | |
| env: | |
| NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} |