Skip to content

fix(Oracle): skip watch-only accounts when refreshing response signatures #1039

fix(Oracle): skip watch-only accounts when refreshing response signatures

fix(Oracle): skip watch-only accounts when refreshing response signatures #1039

Workflow file for this run

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@v6
- 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@v6
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@v6
- 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
- name: Run tests with expect
run: |
# Count loadable plugins only (exclude MPTTrie dependency and RpcClient library).
expected_count=0
for csproj in plugins/*/*.csproj; do
plugin_name=$(basename "$(dirname "$csproj")")
case "$plugin_name" in
MPTTrie|RpcClient) continue ;;
esac
expected_count=$((expected_count + 1))
done
expect ./.github/workflows/test-neo-cli-plugins.expect "$expected_count"
Release:
if: github.ref == 'refs/heads/master-n3' && github.repository == 'neo-project/neo-node'
needs: Test
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
- 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'
env:
RELEASE_VERSION: ${{ steps.get_version.outputs.version }}
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
}
package_publish_output() {
local platform=$1
local publish_dir="./publish/$platform"
local archive_base="neo-cli.${RELEASE_VERSION}-${platform}"
local package_root="neo-cli-${RELEASE_VERSION}"
local stage_dir="./publish/stage-$platform"
rm -rf "$stage_dir"
mkdir -p "$stage_dir/$package_root"
cp -a "$publish_dir/." "$stage_dir/$package_root/"
case "$platform" in
portable|win-*)
(cd "$stage_dir" && zip -r "../../${archive_base}.zip" "$package_root")
;;
linux-*|osx-*)
tar -czf "${archive_base}.tar.gz" -C "$stage_dir" "$package_root"
;;
*)
echo "Error: Unknown platform: $platform" >&2
exit 1
;;
esac
rm -rf "$stage_dir"
}
# Traditional framework-dependent publish used only for plugin dependency diffing
dotnet publish src/Neo.CLI/Neo.CLI.csproj -c Release -o "./publish/baseline" --self-contained false
for platform in "${platforms[@]}"; do
if [ "$platform" == "portable" ]; then
rm -rf "./publish/$platform"
cp -a "./publish/baseline" "./publish/$platform"
else
dotnet publish src/Neo.CLI/Neo.CLI.csproj -c Release -o "./publish/$platform" -r "$platform" --self-contained true \
-p:PublishSingleFile=true \
-p:IncludeNativeLibrariesForSelfExtract=false \
-p:DebugType=none \
-p:DebugSymbols=false
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
package_publish_output "$platform"
done
- name: Build and package plugins (zip per plugin)
if: steps.check_tag.outputs.statusCode == '404'
run: |
set -euo pipefail
# Use traditional neo-cli publish output as the baseline set of assemblies (not single-file releases)
if [ ! -d "./publish/baseline" ]; then
echo "Error: ./publish/baseline not found. neo-cli baseline must be published first."
exit 1
fi
mkdir -p "./plugin-publish" "./plugin-zip"
# Collect baseline DLL filenames shipped with neo-cli
mapfile -t cli_dlls < <(find "./publish/baseline" -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 (e.g. DeferredRelay), except MPTTrie (StateService dependency only).
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 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 }}
RELEASE_VERSION: ${{ steps.get_version.outputs.version }}
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 archive files exist
missing_files=()
cli_archives=(
"neo-cli.${RELEASE_VERSION}-portable.zip"
"neo-cli.${RELEASE_VERSION}-linux-x64.tar.gz"
"neo-cli.${RELEASE_VERSION}-linux-arm64.tar.gz"
"neo-cli.${RELEASE_VERSION}-win-x64.zip"
"neo-cli.${RELEASE_VERSION}-osx-x64.tar.gz"
"neo-cli.${RELEASE_VERSION}-osx-arm64.tar.gz"
)
for file in "${cli_archives[@]}"; 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 archive files: ${missing_files[*]}"
exit 1
fi
# Upload all CLI archives to release
failed_files=()
for file in "${cli_archives[@]}"; 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 }}
# Docker image publish: only on formal release (explicit <Version> → NuGet path),
# not when only <VersionPrefix> is set (MyGet / dev builds).
- name: Set up Docker Buildx
if: steps.check_tag.outputs.statusCode == '404'
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Log in to GitHub Container Registry
if: steps.check_tag.outputs.statusCode == '404'
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Stage release assets for Docker build
if: steps.check_tag.outputs.statusCode == '404'
env:
RELEASE_VERSION: ${{ steps.get_version.outputs.version }}
run: |
cp "neo-cli.${RELEASE_VERSION}-linux-x64.tar.gz" src/Neo.CLI/Docker/
cp ApplicationLogs.zip RpcServer.zip TokensTracker.zip src/Neo.CLI/Docker/
- name: Extract Docker metadata
if: steps.check_tag.outputs.statusCode == '404'
id: docker_meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=${{ steps.get_version.outputs.version }}
type=raw,value=latest,enable=${{ !contains(steps.get_version.outputs.version, '-') }}
- name: Build and push Docker image
if: steps.check_tag.outputs.statusCode == '404'
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: src/Neo.CLI/Docker
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
build-args: |
CLI_VERSION=${{ steps.get_version.outputs.version }}
GITHUB_REPO=${{ github.repository }}