vault-strategy: index-seeded strategies, auto-deploying deposits, and set_weight #414
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: ASM | |
| on: | |
| schedule: | |
| - cron: "0 0 * * *" | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| branches: | |
| - main | |
| env: | |
| MAX_JOBS: 64 | |
| MIN_PROJECTS_PER_JOB: 4 | |
| MIN_PROJECTS_FOR_MATRIX: 4 | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: read | |
| outputs: | |
| changed_projects: ${{ steps.analyze.outputs.changed_projects }} | |
| total_projects: ${{ steps.analyze.outputs.total_projects }} | |
| matrix: ${{ steps.matrix.outputs.matrix }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dorny/paths-filter@v3 | |
| id: changes | |
| if: github.event_name == 'pull_request' | |
| with: | |
| list-files: shell | |
| filters: | | |
| asm: | |
| - added|modified: '**/asm/**' | |
| workflow: | |
| - added|modified: '.github/workflows/solana-asm.yml' | |
| - name: Analyze Changes | |
| id: analyze | |
| run: | | |
| # Generate ignore pattern, excluding comments | |
| ignore_pattern=$(grep -v '^#' .github/.ghaignore | grep -v '^$' | tr '\n' '|' | sed 's/|$//') | |
| echo "Ignore pattern: $ignore_pattern" | |
| # Single source of truth for "what is a framework project": a directory | |
| # whose name is exactly "asm". `find -type d -name asm` gives us that by | |
| # construction — no substring matching, no path-segment trickery, so | |
| # anything containing the substring "asm" (e.g. "wasm/", "plasma/") can | |
| # never enter the build list. | |
| function get_projects() { | |
| # An empty .ghaignore makes ignore_pattern empty, and `grep -vE ""` | |
| # matches everything, silently emptying the project list - only | |
| # filter when there is actually a pattern. | |
| if [[ -n "$ignore_pattern" ]]; then | |
| find . -type d -name "asm" | grep -vE "$ignore_pattern" | sort | |
| else | |
| find . -type d -name "asm" | sort | |
| fi | |
| } | |
| # Filter the full project list down to projects touched by the given | |
| # changed files. A file "touches" a project iff it lives inside that | |
| # project directory (prefix match on "<project>/"). This is an | |
| # intersection against get_projects(), so the result is always a subset | |
| # of the authoritative project list. | |
| function filter_by_changes() { | |
| local all_projects="$1" | |
| shift | |
| local changed_files=("$@") | |
| echo "$all_projects" | while read -r project; do | |
| [ -z "$project" ] && continue | |
| local project_prefix="${project#./}/" | |
| for file in "${changed_files[@]}"; do | |
| if [[ "$file" == "$project_prefix"* ]]; then | |
| echo "$project" | |
| break | |
| fi | |
| done | |
| done | sort -u | |
| } | |
| # Determine which projects to build and test | |
| if [[ "${{ github.event_name }}" == "push" || "${{ github.event_name }}" == "schedule" || "${{ steps.changes.outputs.workflow }}" == "true" ]]; then | |
| projects=$(get_projects) | |
| elif [[ "${{ steps.changes.outputs.asm }}" == "true" ]]; then | |
| changed_files=(${{ steps.changes.outputs.asm_files }}) | |
| projects=$(filter_by_changes "$(get_projects)" "${changed_files[@]}") | |
| else | |
| projects="" | |
| fi | |
| # Output project information | |
| if [[ -n "$projects" ]]; then | |
| echo "Projects to build and test" | |
| echo "$projects" | |
| total_projects=$(echo "$projects" | wc -l) | |
| echo "Total projects: $total_projects" | |
| echo "total_projects=$total_projects" >> $GITHUB_OUTPUT | |
| echo "changed_projects=$(echo "$projects" | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT | |
| else | |
| echo "No projects to build and test." | |
| echo "total_projects=0" >> $GITHUB_OUTPUT | |
| echo "changed_projects=[]" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Generate matrix | |
| id: matrix | |
| run: | | |
| total_projects=${{ steps.analyze.outputs.total_projects }} | |
| max_jobs=${{ env.MAX_JOBS }} | |
| min_projects_per_job=${{ env.MIN_PROJECTS_PER_JOB }} | |
| min_projects_for_matrix=${{ env.MIN_PROJECTS_FOR_MATRIX }} | |
| if [ "$total_projects" -lt "$min_projects_for_matrix" ]; then | |
| echo "matrix=[0]" >> $GITHUB_OUTPUT | |
| else | |
| projects_per_job=$(( (total_projects + max_jobs - 1) / max_jobs )) | |
| projects_per_job=$(( projects_per_job > min_projects_per_job ? projects_per_job : min_projects_per_job )) | |
| num_jobs=$(( (total_projects + projects_per_job - 1) / projects_per_job )) | |
| indices=$(seq 0 $(( num_jobs - 1 ))) | |
| echo "matrix=[$(echo $indices | tr ' ' ',')]" >> $GITHUB_OUTPUT | |
| fi | |
| build-and-test: | |
| needs: changes | |
| if: needs.changes.outputs.total_projects != '0' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| index: ${{ fromJson(needs.changes.outputs.matrix) }} | |
| name: build-and-test-group-${{ matrix.index }} | |
| outputs: | |
| failed_projects: ${{ steps.set-failed.outputs.failed_projects }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup build environment | |
| id: setup | |
| run: | | |
| # Create the build and test function | |
| cat << 'EOF' > build_and_test.sh | |
| function build_and_test() { | |
| local project=$1 | |
| local solana_version=$2 | |
| echo "Building and Testing $project with Solana $solana_version" | |
| cd "$project" || return 1 | |
| # Build the .so into deploy/: the Rust + LiteSVM tests embed it at | |
| # compile time via include_bytes!, so the tests cannot even compile | |
| # without a build, and a stale .so would test old code. | |
| if ! sbpf build; then | |
| echo "::error::sbpf build failed for $project" | |
| echo "$project: sbpf build failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt | |
| cd - > /dev/null | |
| return 1 | |
| fi | |
| # Run the Rust + LiteSVM tests (inline in src/lib.rs) | |
| if ! cargo test --manifest-path=./Cargo.toml; then | |
| echo "::error::tests failed for $project" | |
| echo "$project: tests failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt | |
| cd - > /dev/null | |
| return 1 | |
| fi | |
| echo "Build and tests succeeded for $project with $solana_version version." | |
| cd - > /dev/null | |
| return 0 | |
| } | |
| function process_projects() { | |
| local solana_version=$1 | |
| readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?') | |
| start_index=$(( ${{ matrix.index }} * ${{ env.MIN_PROJECTS_PER_JOB }} )) | |
| end_index=$(( start_index + ${{ env.MIN_PROJECTS_PER_JOB }} )) | |
| end_index=$(( end_index > ${{ needs.changes.outputs.total_projects }} ? ${{ needs.changes.outputs.total_projects }} : end_index )) | |
| echo "Projects to build and test in this job" | |
| for i in $(seq $start_index $(( end_index - 1 ))); do | |
| echo "${all_projects[$i]}" | |
| done | |
| failed=false | |
| for i in $(seq $start_index $(( end_index - 1 ))); do | |
| echo "::group::Building and testing ${all_projects[$i]}" | |
| if ! build_and_test "${all_projects[$i]}" "$solana_version"; then | |
| failed=true | |
| fi | |
| echo "::endgroup::" | |
| done | |
| return $([ "$failed" = true ] && echo 1 || echo 0) | |
| } | |
| EOF | |
| # Make the script executable | |
| chmod +x build_and_test.sh | |
| # Install sbpf assembler | |
| cargo install --git https://github.com/blueshift-gg/sbpf.git | |
| - name: Setup Solana Stable | |
| uses: heyAyushh/setup-solana@v5.9 | |
| with: | |
| # setup-anchor resolves tags like stable by querying GitHub API for latest release which can fail with 429 errors | |
| solana-cli-version: 3.1.14 | |
| - name: Build and Test with Stable | |
| run: | | |
| source build_and_test.sh | |
| solana -V | |
| rustc -V | |
| process_projects "stable" | |
| # continue-on-error because the beta channel may not have a valid release | |
| # (e.g. v4.0 returns 404 from release.anza.xyz). This is an upstream issue | |
| # with heyAyushh/setup-solana — beta setup clears the stable install first. | |
| - name: Setup Solana Beta | |
| continue-on-error: true | |
| uses: heyAyushh/setup-solana@v5.9 | |
| with: | |
| solana-cli-version: beta | |
| - name: Build and Test with Beta | |
| continue-on-error: true | |
| run: | | |
| source build_and_test.sh | |
| solana -V | |
| rustc -V | |
| process_projects "beta" | |
| - name: Set failed projects output | |
| id: set-failed | |
| if: failure() | |
| run: | | |
| if [ -f "$GITHUB_WORKSPACE/failed_projects.txt" ]; then | |
| failed_projects=$(cat $GITHUB_WORKSPACE/failed_projects.txt | jq -R -s -c 'split("\n")[:-1]') | |
| echo "failed_projects=$failed_projects" >> $GITHUB_OUTPUT | |
| else | |
| echo "failed_projects=[]" >> $GITHUB_OUTPUT | |
| fi | |
| summary: | |
| needs: [changes, build-and-test] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Create job summary | |
| run: | | |
| echo "## ASM Workflow Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "- Total projects: ${{ needs.changes.outputs.total_projects }}" >> $GITHUB_STEP_SUMMARY | |
| # List all processed projects | |
| echo "<details>" >> $GITHUB_STEP_SUMMARY | |
| echo "<summary>Projects processed (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]' | while read project; do | |
| echo "- $project" >> $GITHUB_STEP_SUMMARY | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| # Report build and test results | |
| if [[ "${{ needs.build-and-test.result }}" == "failure" ]]; then | |
| echo "## :x: Build or tests failed" >> $GITHUB_STEP_SUMMARY | |
| echo "<details>" >> $GITHUB_STEP_SUMMARY | |
| echo "<summary>Failed projects (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| failed_projects='${{ needs.build-and-test.outputs.failed_projects }}' | |
| if [[ -n "$failed_projects" ]]; then | |
| echo "$failed_projects" | jq -r '.[]' | while IFS=: read -r project failure_reason; do | |
| echo "- **$project**" >> $GITHUB_STEP_SUMMARY | |
| echo " - Failure reason: $failure_reason" >> $GITHUB_STEP_SUMMARY | |
| done | |
| else | |
| echo "No failed projects reported. This might indicate an unexpected error in the workflow." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| elif [[ "${{ needs.build-and-test.result }}" == "success" ]]; then | |
| echo "## :white_check_mark: All builds and tests passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "## :warning: Build and test job was skipped or canceled" >> $GITHUB_STEP_SUMMARY | |
| fi |