|
@@ -11,224 +11,229 @@ on:
|
|
|
branches:
|
|
|
- main
|
|
|
|
|
|
+env:
|
|
|
+ MAX_JOBS: 64
|
|
|
+ MIN_PROJECTS_PER_JOB: 4
|
|
|
+ MIN_PROJECTS_FOR_MATRIX: 4
|
|
|
+
|
|
|
jobs:
|
|
|
- build:
|
|
|
+ changes:
|
|
|
runs-on: ubuntu-latest
|
|
|
permissions:
|
|
|
- pull-requests: read
|
|
|
- strategy:
|
|
|
- matrix:
|
|
|
- node-version: [20.x]
|
|
|
- solana-version: [1.18.17, stable]
|
|
|
- anchor-version: [0.30.1]
|
|
|
+ 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
|
|
|
- if: github.event_name == 'pull_request'
|
|
|
id: changes
|
|
|
+ if: github.event_name == 'pull_request'
|
|
|
with:
|
|
|
list-files: shell
|
|
|
filters: |
|
|
|
anchor:
|
|
|
- added|modified: '**/anchor/**'
|
|
|
- anchor_action:
|
|
|
- - added|modified: '**/workflows/anchor.yml'
|
|
|
-
|
|
|
- - name: Setup Anchor
|
|
|
- if: steps.changes.outputs.anchor == 'true' || steps.changes.outputs.anchor_action == 'true' || github.event_name == 'schedule' || github.event_name == 'push'
|
|
|
- uses: heyAyushh/setup-anchor@v4.0
|
|
|
- with:
|
|
|
- anchor-version: ${{ matrix.anchor-version }}
|
|
|
- solana-cli-version: ${{ matrix.solana-version }}
|
|
|
- node-version: ${{ matrix.node-version }}
|
|
|
- - name: Display versions and Install pnpm
|
|
|
- if: steps.changes.outputs.anchor == 'true' || steps.changes.outputs.anchor_action == 'true' || github.event_name == 'schedule'
|
|
|
- run: |
|
|
|
- solana -V
|
|
|
- solana-keygen new --no-bip39-passphrase
|
|
|
- rustc -V
|
|
|
- anchor -V
|
|
|
- npm i -g pnpm
|
|
|
- # Run only if it's triggered by PR to main,
|
|
|
- # Build the changed programs
|
|
|
- - name: Build Changed Anchor programs
|
|
|
- if: steps.changes.outputs.anchor == 'true' && steps.changes.outputs.anchor_action != 'true'
|
|
|
+ workflow:
|
|
|
+ - added|modified: '.github/workflows/anchor.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"
|
|
|
+
|
|
|
+ function get_projects() {
|
|
|
+ find . -type d -name "anchor" | grep -vE "$ignore_pattern" | sort
|
|
|
+ }
|
|
|
+
|
|
|
+ # 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.anchor }}" == "true" ]]; then
|
|
|
changed_files=(${{ steps.changes.outputs.anchor_files }})
|
|
|
+ projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep anchor | sed 's#/anchor/.*#/anchor#g'; done | grep -vE "$ignore_pattern" | sort -u)
|
|
|
+ else
|
|
|
+ projects=""
|
|
|
+ fi
|
|
|
|
|
|
- # Read ignored projects from .github/.ghaignore, ignoring empty lines and comments
|
|
|
- ignored_projects=($(grep . .github/.ghaignore | grep -v '^$'))
|
|
|
-
|
|
|
- # Find anchor projects and remove ignored projects
|
|
|
- ProjectDirs=($(for file in "${changed_files[@]}"; do
|
|
|
- anchor_path=$(dirname "${file}" | grep anchor | sed 's#/anchor/.*#/anchor#g')
|
|
|
- if [[ ! " ${ignored_projects[*]} " =~ " ${anchor_path} " ]]; then
|
|
|
- echo "$anchor_path"
|
|
|
- fi
|
|
|
- done | sort -u))
|
|
|
-
|
|
|
- # Build anchor projects
|
|
|
- echo "Projects to Build:"
|
|
|
- printf "%s\n" "${ProjectDirs[@]}"
|
|
|
- for projectDir in "${ProjectDirs[@]}"; do
|
|
|
- echo "
|
|
|
- ********
|
|
|
- Building $projectDir
|
|
|
- ********"
|
|
|
- cd $projectDir
|
|
|
- if anchor build; then
|
|
|
- echo "Build succeeded for $projectDir."
|
|
|
- rm -rf target
|
|
|
- else
|
|
|
- failed=true
|
|
|
- failed_builds+=($projectDir)
|
|
|
- echo "Build failed for $projectDir. Continuing with the next program."
|
|
|
- fi
|
|
|
- cd - > /dev/null
|
|
|
- done
|
|
|
- if [ "$failed" = true ]; then
|
|
|
- echo "Programs that failed building:"
|
|
|
- printf "%s\n" "${failed_builds[@]}"
|
|
|
- exit 1
|
|
|
- else
|
|
|
- echo "All programs built successfully."
|
|
|
- fi
|
|
|
- shell: bash
|
|
|
- # Skip Building all Programs if it's a PR to main branch
|
|
|
- - name: Build All Anchor programs
|
|
|
- if: github.event_name == 'schedule' || github.event_name == 'push' || steps.changes.outputs.anchor_action == 'true'
|
|
|
+ # 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: |
|
|
|
- # Find all anchor projects and remove ignored projects
|
|
|
- declare -a ProjectDirs=($(find . -type d -name 'anchor' | sed 's|^\./||'| grep -v -f <(grep . .github/.ghaignore | grep -v '^$')))
|
|
|
-
|
|
|
- echo "Projects to Build:"
|
|
|
- printf "%s\n" "${ProjectDirs[@]}"
|
|
|
-
|
|
|
- # Build anchor projects
|
|
|
- for projectDir in "${ProjectDirs[@]}"; do
|
|
|
- echo "
|
|
|
- ********
|
|
|
- Building $projectDir
|
|
|
- ********"
|
|
|
- cd $projectDir
|
|
|
- if anchor build; then
|
|
|
- echo "Build succeeded for $projectDir."
|
|
|
- rm -rf target
|
|
|
- else
|
|
|
- failed=true
|
|
|
- failed_builds+=($projectDir)
|
|
|
- echo "Build failed for $projectDir. Continuing with the next program."
|
|
|
- fi
|
|
|
- cd - > /dev/null
|
|
|
- done
|
|
|
- if [ "$failed" = true ]; then
|
|
|
- echo "Programs that failed building:"
|
|
|
- printf "%s\n" "${failed_builds[@]}"
|
|
|
- exit 1
|
|
|
+ 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 }}
|
|
|
+
|
|
|
+ # Generate matrix based on number of projects
|
|
|
+ if [ "$total_projects" -lt "$min_projects_for_matrix" ]; then
|
|
|
+ echo "matrix=[0]" >> $GITHUB_OUTPUT
|
|
|
else
|
|
|
- echo "All programs built successfully."
|
|
|
+ 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
|
|
|
- shell: bash
|
|
|
|
|
|
- test:
|
|
|
+ build-and-test:
|
|
|
+ needs: changes
|
|
|
+ if: needs.changes.outputs.total_projects != '0'
|
|
|
runs-on: ubuntu-latest
|
|
|
- permissions:
|
|
|
- pull-requests: read
|
|
|
strategy:
|
|
|
+ fail-fast: false
|
|
|
matrix:
|
|
|
- node-version: [20.x]
|
|
|
- solana-version: [1.18.17, stable]
|
|
|
- anchor-version: [0.30.1]
|
|
|
+ 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
|
|
|
- - uses: dorny/paths-filter@v3
|
|
|
- if: github.event_name == 'pull_request'
|
|
|
- id: changes
|
|
|
- with:
|
|
|
- list-files: shell
|
|
|
- filters: |
|
|
|
- anchor:
|
|
|
- - added|modified: '**/anchor/**'
|
|
|
- anchor_action:
|
|
|
- - added|modified: '**/workflows/anchor.yml'
|
|
|
- # Skip Installing and Displaying versions if theres no change in anchor programs or anchor action workflow file or isn't a schedule event
|
|
|
- - name: Setup Anchor
|
|
|
- if: steps.changes.outputs.anchor == 'true' || steps.changes.outputs.anchor_action == 'true' || github.event_name == 'schedule' || github.event_name == 'push'
|
|
|
- uses: heyAyushh/setup-anchor@v4.0
|
|
|
+ - uses: heyAyushh/setup-anchor@v4.2
|
|
|
with:
|
|
|
- anchor-version: ${{ matrix.anchor-version }}
|
|
|
- solana-cli-version: ${{ matrix.solana-version }}
|
|
|
- node-version: ${{ matrix.node-version }}
|
|
|
- - name: Display versions and Install pnpm
|
|
|
- if: steps.changes.outputs.anchor == 'true' || steps.changes.outputs.anchor_action == 'true' || github.event_name == 'schedule' || github.event_name == 'push'
|
|
|
+ anchor-version: 0.30.1
|
|
|
+ solana-cli-version: 1.18.17
|
|
|
+ node-version: 20.x
|
|
|
+ use-avm: false
|
|
|
+ - name: Display Versions and Install pnpm
|
|
|
run: |
|
|
|
solana -V
|
|
|
solana-keygen new --no-bip39-passphrase
|
|
|
rustc -V
|
|
|
anchor -V
|
|
|
npm i -g pnpm
|
|
|
- - name: Test Changed Anchor Programs
|
|
|
- if: steps.changes.outputs.anchor == 'true' && steps.changes.outputs.anchor_action != 'true'
|
|
|
+ - name: Build and Test
|
|
|
+ env:
|
|
|
+ TOTAL_PROJECTS: ${{ needs.changes.outputs.total_projects }}
|
|
|
+ PROJECTS_PER_JOB: ${{ env.MIN_PROJECTS_PER_JOB }}
|
|
|
run: |
|
|
|
- changed_files=(${{ steps.changes.outputs.anchor_files }})
|
|
|
- ProjectDirs=($(for file in "${changed_files[@]}"; do dirname "${file}" | grep anchor | sed 's#/anchor/.*#/anchor#g'; done | grep -v -f <(grep . .github/.ghaignore | grep -v '^$') | sort -u))
|
|
|
- echo "Projects to Test:"
|
|
|
- printf "%s\n" "${ProjectDirs[@]}"
|
|
|
- for projectDir in "${ProjectDirs[@]}"; do
|
|
|
- echo "
|
|
|
- ********
|
|
|
- Testing $projectDir
|
|
|
- ********"
|
|
|
- cd $projectDir
|
|
|
- if pnpm install --frozen-lockfile && anchor test; then
|
|
|
- echo "Tests succeeded for $projectDir."
|
|
|
- rm -rf target node_modules
|
|
|
- else
|
|
|
+ function build_and_test() {
|
|
|
+ local project=$1
|
|
|
+ echo "Building and Testing $project"
|
|
|
+ cd "$project"
|
|
|
+
|
|
|
+ # Run anchor build
|
|
|
+ if ! anchor build; then
|
|
|
+ echo "::error::anchor build failed for $project"
|
|
|
+ echo "$project: anchor build failed" >> $GITHUB_WORKSPACE/failed_projects.txt
|
|
|
+ return 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Install dependencies
|
|
|
+ if ! pnpm install --frozen-lockfile; then
|
|
|
+ echo "::error::pnpm install failed for $project"
|
|
|
+ echo "$project: pnpm install failed" >> $GITHUB_WORKSPACE/failed_projects.txt
|
|
|
+ return 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Run anchor test
|
|
|
+ if ! anchor test; then
|
|
|
+ echo "::error::anchor test failed for $project"
|
|
|
+ echo "$project: anchor test failed" >> $GITHUB_WORKSPACE/failed_projects.txt
|
|
|
+ return 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo "Build and tests succeeded for $project."
|
|
|
+ rm -rf target node_modules
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+
|
|
|
+ # Determine which projects to build in this job
|
|
|
+ readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
|
|
|
+ start_index=$(( ${{ matrix.index }} * PROJECTS_PER_JOB ))
|
|
|
+ end_index=$(( start_index + PROJECTS_PER_JOB ))
|
|
|
+ end_index=$(( end_index > TOTAL_PROJECTS ? 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
|
|
|
+
|
|
|
+ # Build and test projects
|
|
|
+ failed=false
|
|
|
+ failed_projects=()
|
|
|
+ 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]}"; then
|
|
|
failed=true
|
|
|
- failed_tests+=($projectDir)
|
|
|
- echo "Tests failed for $val. Continuing with the next program."
|
|
|
+ failed_projects+=("${all_projects[$i]}")
|
|
|
fi
|
|
|
- cd - > /dev/null
|
|
|
+ echo "::endgroup::"
|
|
|
done
|
|
|
- if [ "$failed" = true ]; then
|
|
|
- echo "*****************************"
|
|
|
- echo "Programs that failed testing:"
|
|
|
- printf "%s\n" "${failed_tests[@]}"
|
|
|
+
|
|
|
+ if [[ "$failed" == "true" ]]; then
|
|
|
+ echo "::group::Failed projects"
|
|
|
+ cat $GITHUB_WORKSPACE/failed_projects.txt
|
|
|
+ echo "::endgroup::"
|
|
|
+ echo "failed_projects=${failed_projects[@]}" >> $GITHUB_OUTPUT
|
|
|
exit 1
|
|
|
else
|
|
|
- echo "All tests passed."
|
|
|
+ echo "failed_projects=" >> $GITHUB_OUTPUT
|
|
|
fi
|
|
|
- shell: bash
|
|
|
- # Skip Testing all Programs if it's a CRON job or a change in the workflow file
|
|
|
- - name: Test All Anchor Programs
|
|
|
- if: github.event_name == 'schedule' || github.event_name == 'push' || steps.changes.outputs.anchor_action == 'true'
|
|
|
+
|
|
|
+ - name: Set failed projects output
|
|
|
+ id: set-failed
|
|
|
+ if: failure()
|
|
|
run: |
|
|
|
- declare -a ProjectDirs=($(find . -type d -name "anchor"| grep -v -f <(grep . .github/.ghaignore | grep -v '^$')))
|
|
|
- echo "Projects to Test:"
|
|
|
- printf "%s\n" "${ProjectDirs[@]}"
|
|
|
- for projectDir in "${ProjectDirs[@]}"; do
|
|
|
- echo "
|
|
|
- ********
|
|
|
- Testing $projectDir
|
|
|
- ********"
|
|
|
- cd $projectDir
|
|
|
- pnpm install --frozen-lockfile
|
|
|
- if anchor test; then
|
|
|
- echo "Tests succeeded for $projectDir."
|
|
|
- rm -rf target node_modules
|
|
|
+ # Prepare failed projects list for output
|
|
|
+ failed_projects=$(cat $GITHUB_WORKSPACE/failed_projects.txt | jq -R -s -c 'split("\n")[:-1]')
|
|
|
+ echo "failed_projects=$failed_projects" >> $GITHUB_OUTPUT
|
|
|
+
|
|
|
+ summary:
|
|
|
+ needs: [changes, build-and-test]
|
|
|
+ if: always()
|
|
|
+ runs-on: ubuntu-latest
|
|
|
+ steps:
|
|
|
+ - uses: actions/checkout@v4
|
|
|
+ - name: Create job summary
|
|
|
+ run: |
|
|
|
+ echo "## Anchor 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
|
|
|
- failed=true
|
|
|
- failed_tests+=($projectDir)
|
|
|
- echo "Tests failed for $val. Continuing with the next program."
|
|
|
+ echo "No failed projects reported. This might indicate an unexpected error in the workflow." >> $GITHUB_STEP_SUMMARY
|
|
|
fi
|
|
|
- cd - > /dev/null
|
|
|
- done
|
|
|
- if [ "$failed" = true ]; then
|
|
|
- echo "*****************************"
|
|
|
- echo "Programs that failed testing:"
|
|
|
- printf "%s\n" "${failed_tests[@]}"
|
|
|
- exit 1
|
|
|
+ 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 "All tests passed."
|
|
|
+ echo "## :warning: Build and test job was skipped or canceled" >> $GITHUB_STEP_SUMMARY
|
|
|
fi
|
|
|
- shell: bash
|