123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- name: Steel
- 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: |
- steel:
- - added|modified: '**/steel/**'
- workflow:
- - added|modified: '.github/workflows/steel.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 "steel" | 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.steel }}" == "true" ]]; then
- changed_files=(${{ steps.changes.outputs.steel_files }})
- projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep steel | sed 's#/steel/.*#/steel#g'; done | grep -vE "$ignore_pattern" | sort -u)
- 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
- rust-checks:
- needs: changes
- if: ${{ github.event_name == 'pull_request' && needs.changes.outputs.total_projects != '0' }}
- name: Rust Checks
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@stable
- with:
- components: rustfmt, clippy
- - name: Run sccache-cache
- if: github.event_name != 'release'
- uses: mozilla-actions/sccache-action@v0.0.6
- - name: Set Rust cache env vars
- if: github.event_name != 'release'
- run: |
- echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
- echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
- - name: Run fmt and clippy
- run: |
- readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
- for project in "${all_projects[@]}"; do
- echo "::group::Checking ${project}"
- if [ ! -f "${project}/Cargo.toml" ]; then
- echo "::error::No Cargo.toml found in ${project}"
- exit 1
- fi
- cd "${project}"
- cargo fmt --check
- cargo clippy --all-features -- -D warnings
- cd - > /dev/null
- echo "::endgroup::"
- done
- 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
- - uses: dtolnay/rust-toolchain@stable
- - name: Run sccache-cache
- if: github.event_name != 'release'
- uses: mozilla-actions/sccache-action@v0.0.6
- - name: Set Rust cache env vars
- if: github.event_name != 'release'
- run: |
- echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
- echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
- - uses: actions/cache@v3
- with:
- path: ~/.cargo/bin/steel
- key: ${{ runner.os }}-steel-cli
- - name: Use Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20.x
- check-latest: true
- - name: Setup build environment
- id: setup
- run: |
- npm install --global pnpm
- # 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
- # Install dependencies
- if [ -f "package.json" ]; then
- if ! pnpm install --frozen-lockfile; then
- echo "::error::pnpm install failed for $project"
- echo "$project: pnpm install failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
- cd - > /dev/null
- return 1
- fi
- # Build
- if ! pnpm build; then
- echo "::error::build failed for $project"
- echo "$project: build failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
- cd - > /dev/null
- return 1
- fi
- # Test
- if ! pnpm build-and-test; 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
- else
- # Use Steel CLI
- if ! cargo install --quiet steel-cli; then
- echo "::error::steel-cli installation failed for $project"
- echo "$project: steel-cli installation failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
- cd - > /dev/null
- return 1
- fi
- # Build
- if ! steel build; then
- echo "::error::steel build failed for $project"
- echo "$project: steel build failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
- cd - > /dev/null
- return 1
- fi
- # Test
- if ! steel test; then
- echo "::error::steel test failed for $project"
- echo "$project: steel test failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
- cd - > /dev/null
- return 1
- fi
- 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
- - name: Setup Solana stable
- uses: heyAyushh/setup-solana@v5.5
- with:
- solana-cli-version: stable
- - name: Build and Test with Stable
- env:
- SCCACHE_GHA_ENABLED: "true"
- RUSTC_WRAPPER: "sccache"
- run: |
- source build_and_test.sh
- solana -V
- rustc -V
- solana-keygen new --no-bip39-passphrase
- process_projects "stable"
- sccache --show-stats
- - name: Setup Solana 1.18.17
- uses: heyAyushh/setup-solana@v5.5
- with:
- solana-cli-version: 1.18.17
- - name: Build and Test with 1.18.17
- env:
- SCCACHE_GHA_ENABLED: "true"
- RUSTC_WRAPPER: "sccache"
- run: |
- source build_and_test.sh
- solana -V
- rustc -V
- solana-keygen new --no-bip39-passphrase --force
- process_projects "1.18.17"
- sccache --show-stats
- - 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 "## Steel 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
|