steel.yml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. name: Steel
  2. on:
  3. schedule:
  4. - cron: "0 0 * * *"
  5. push:
  6. branches:
  7. - main
  8. pull_request:
  9. types: [opened, synchronize, reopened]
  10. branches:
  11. - main
  12. env:
  13. MAX_JOBS: 64
  14. MIN_PROJECTS_PER_JOB: 4
  15. MIN_PROJECTS_FOR_MATRIX: 4
  16. jobs:
  17. changes:
  18. runs-on: ubuntu-latest
  19. permissions:
  20. pull-requests: read
  21. outputs:
  22. changed_projects: ${{ steps.analyze.outputs.changed_projects }}
  23. total_projects: ${{ steps.analyze.outputs.total_projects }}
  24. matrix: ${{ steps.matrix.outputs.matrix }}
  25. steps:
  26. - uses: actions/checkout@v4
  27. - uses: dtolnay/rust-toolchain@stable
  28. with:
  29. components: rustfmt, clippy
  30. - uses: dorny/paths-filter@v3
  31. id: changes
  32. if: github.event_name == 'pull_request'
  33. with:
  34. list-files: shell
  35. filters: |
  36. steel:
  37. - added|modified: '**/steel/**'
  38. workflow:
  39. - added|modified: '.github/workflows/steel.yml'
  40. - name: Run fmt and clippy
  41. run: |
  42. readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
  43. for project in "${all_projects[@]}"; do
  44. echo "::group::Checking ${project}"
  45. if [ ! -f "${project}/Cargo.toml" ]; then
  46. echo "::error::No Cargo.toml found in ${project}"
  47. exit 1
  48. fi
  49. cd "${project}"
  50. cargo fmt --check
  51. cargo clippy --all-features -- -D warnings
  52. cd - > /dev/null
  53. echo "::endgroup::"
  54. done
  55. - name: Analyze Changes
  56. id: analyze
  57. run: |
  58. # Generate ignore pattern, excluding comments
  59. ignore_pattern=$(grep -v '^#' .github/.ghaignore | grep -v '^$' | tr '\n' '|' | sed 's/|$//')
  60. echo "Ignore pattern: $ignore_pattern"
  61. function get_projects() {
  62. find . -type d -name "steel" | grep -vE "$ignore_pattern" | sort
  63. }
  64. # Determine which projects to build and test
  65. if [[ "${{ github.event_name }}" == "push" || "${{ github.event_name }}" == "schedule" || "${{ steps.changes.outputs.workflow }}" == "true" ]]; then
  66. projects=$(get_projects)
  67. elif [[ "${{ steps.changes.outputs.steel }}" == "true" ]]; then
  68. changed_files=(${{ steps.changes.outputs.steel_files }})
  69. projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep steel | sed 's#/steel/.*#/steel#g'; done | grep -vE "$ignore_pattern" | sort -u)
  70. else
  71. projects=""
  72. fi
  73. # Output project information
  74. if [[ -n "$projects" ]]; then
  75. echo "Projects to build and test"
  76. echo "$projects"
  77. total_projects=$(echo "$projects" | wc -l)
  78. echo "Total projects: $total_projects"
  79. echo "total_projects=$total_projects" >> $GITHUB_OUTPUT
  80. echo "changed_projects=$(echo "$projects" | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
  81. else
  82. echo "No projects to build and test."
  83. echo "total_projects=0" >> $GITHUB_OUTPUT
  84. echo "changed_projects=[]" >> $GITHUB_OUTPUT
  85. fi
  86. - name: Generate matrix
  87. id: matrix
  88. run: |
  89. total_projects=${{ steps.analyze.outputs.total_projects }}
  90. max_jobs=${{ env.MAX_JOBS }}
  91. min_projects_per_job=${{ env.MIN_PROJECTS_PER_JOB }}
  92. min_projects_for_matrix=${{ env.MIN_PROJECTS_FOR_MATRIX }}
  93. if [ "$total_projects" -lt "$min_projects_for_matrix" ]; then
  94. echo "matrix=[0]" >> $GITHUB_OUTPUT
  95. else
  96. projects_per_job=$(( (total_projects + max_jobs - 1) / max_jobs ))
  97. projects_per_job=$(( projects_per_job > min_projects_per_job ? projects_per_job : min_projects_per_job ))
  98. num_jobs=$(( (total_projects + projects_per_job - 1) / projects_per_job ))
  99. indices=$(seq 0 $(( num_jobs - 1 )))
  100. echo "matrix=[$(echo $indices | tr ' ' ',')]" >> $GITHUB_OUTPUT
  101. fi
  102. rust-checks:
  103. needs: changes
  104. if: ${{ github.event_name == 'pull_request' && needs.changes.outputs.total_projects != '0' }}
  105. name: Rust Checks
  106. runs-on: ubuntu-latest
  107. steps:
  108. - uses: actions/checkout@v4
  109. - name: Run fmt and clippy
  110. run: |
  111. readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
  112. for project in "${all_projects[@]}"; do
  113. echo "::group::Checking ${project}"
  114. if [ ! -f "${project}/Cargo.toml" ]; then
  115. echo "::error::No Cargo.toml found in ${project}"
  116. exit 1
  117. fi
  118. cd "${project}"
  119. cargo fmt --check
  120. cargo clippy --all-features -- -D warnings
  121. cd - > /dev/null
  122. echo "::endgroup::"
  123. done
  124. build-and-test:
  125. needs: changes
  126. if: needs.changes.outputs.total_projects != '0'
  127. runs-on: ubuntu-latest
  128. strategy:
  129. fail-fast: false
  130. matrix:
  131. index: ${{ fromJson(needs.changes.outputs.matrix) }}
  132. name: build-and-test-group-${{ matrix.index }}
  133. outputs:
  134. failed_projects: ${{ steps.set-failed.outputs.failed_projects }}
  135. steps:
  136. - uses: actions/checkout@v4
  137. - uses: dorny/paths-filter@v3
  138. id: changes
  139. if: github.event_name == 'pull_request'
  140. with:
  141. list-files: shell
  142. filters: |
  143. native:
  144. - added|modified: '**/native/**'
  145. workflow:
  146. - added|modified: '.github/workflows/solana-native.yml'
  147. - name: Use Node.js
  148. uses: actions/setup-node@v4
  149. with:
  150. node-version: 'lts/*'
  151. check-latest: true
  152. - name: Setup build environment
  153. id: setup
  154. run: |
  155. npm install --global pnpm
  156. # Create the build and test function
  157. cat << 'EOF' > build_and_test.sh
  158. function build_and_test() {
  159. local project=$1
  160. local solana_version=$2
  161. echo "Building and Testing $project with Solana $solana_version"
  162. cd "$project" || return 1
  163. # Check if this is a pnpm project or Steel CLI project
  164. if [ -f "package.json" ]; then
  165. # Use pnpm for projects with package.json
  166. if ! pnpm install --frozen-lockfile; then
  167. echo "::error::pnpm install failed for $project"
  168. echo "$project: pnpm install failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
  169. cd - > /dev/null
  170. return 1
  171. fi
  172. # Build
  173. if ! pnpm build; then
  174. echo "::error::build failed for $project"
  175. echo "$project: build failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
  176. cd - > /dev/null
  177. return 1
  178. fi
  179. # Test
  180. if ! pnpm test; then
  181. echo "::error::tests failed for $project"
  182. echo "$project: tests failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
  183. cd - > /dev/null
  184. return 1
  185. fi
  186. else
  187. # Use Steel CLI for pure Steel projects
  188. # Build
  189. if ! steel build; then
  190. echo "::error::steel build failed for $project"
  191. echo "$project: steel build failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
  192. cd - > /dev/null
  193. return 1
  194. fi
  195. # Test
  196. if ! steel test; then
  197. echo "::error::steel test failed for $project"
  198. echo "$project: steel test failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
  199. cd - > /dev/null
  200. return 1
  201. fi
  202. fi
  203. echo "Build and tests succeeded for $project with $solana_version version."
  204. cd - > /dev/null
  205. return 0
  206. }
  207. function process_projects() {
  208. local solana_version=$1
  209. readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
  210. start_index=$(( ${{ matrix.index }} * ${{ env.MIN_PROJECTS_PER_JOB }} ))
  211. end_index=$(( start_index + ${{ env.MIN_PROJECTS_PER_JOB }} ))
  212. end_index=$(( end_index > ${{ needs.changes.outputs.total_projects }} ? ${{ needs.changes.outputs.total_projects }} : end_index ))
  213. echo "Projects to build and test in this job"
  214. for i in $(seq $start_index $(( end_index - 1 ))); do
  215. echo "${all_projects[$i]}"
  216. done
  217. failed=false
  218. for i in $(seq $start_index $(( end_index - 1 ))); do
  219. echo "::group::Building and testing ${all_projects[$i]}"
  220. if ! build_and_test "${all_projects[$i]}" "$solana_version"; then
  221. failed=true
  222. fi
  223. echo "::endgroup::"
  224. done
  225. return $([ "$failed" = true ] && echo 1 || echo 0)
  226. }
  227. EOF
  228. # Make the script executable
  229. chmod +x build_and_test.sh
  230. - name: Setup Solana Stable
  231. uses: heyAyushh/setup-solana@v2.02
  232. with:
  233. solana-cli-version: stable
  234. - name: Build and Test with Stable
  235. run: |
  236. source build_and_test.sh
  237. solana -V
  238. rustc -V
  239. solana-keygen new --no-bip39-passphrase --force
  240. cargo install --quiet steel-cli
  241. process_projects "stable"
  242. - name: Setup Solana Beta
  243. uses: heyAyushh/setup-solana@v2.02
  244. with:
  245. solana-cli-version: beta
  246. - name: Build and Test with Beta
  247. run: |
  248. source build_and_test.sh
  249. solana -V
  250. rustc -V
  251. solana-keygen new --no-bip39-passphrase --force
  252. cargo install --quiet steel-cli
  253. process_projects "beta"
  254. - name: Set failed projects output
  255. id: set-failed
  256. if: failure()
  257. run: |
  258. if [ -f "$GITHUB_WORKSPACE/failed_projects.txt" ]; then
  259. failed_projects=$(cat $GITHUB_WORKSPACE/failed_projects.txt | jq -R -s -c 'split("\n")[:-1]')
  260. echo "failed_projects=$failed_projects" >> $GITHUB_OUTPUT
  261. else
  262. echo "failed_projects=[]" >> $GITHUB_OUTPUT
  263. fi
  264. summary:
  265. needs: [changes, build-and-test]
  266. if: always()
  267. runs-on: ubuntu-latest
  268. steps:
  269. - uses: actions/checkout@v4
  270. - name: Create job summary
  271. run: |
  272. echo "## Steel Workflow Summary" >> $GITHUB_STEP_SUMMARY
  273. echo "- Total projects: ${{ needs.changes.outputs.total_projects }}" >> $GITHUB_STEP_SUMMARY
  274. # List all processed projects
  275. echo "<details>" >> $GITHUB_STEP_SUMMARY
  276. echo "<summary>Projects processed (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
  277. echo "" >> $GITHUB_STEP_SUMMARY
  278. echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]' | while read project; do
  279. echo "- $project" >> $GITHUB_STEP_SUMMARY
  280. done
  281. echo "" >> $GITHUB_STEP_SUMMARY
  282. echo "</details>" >> $GITHUB_STEP_SUMMARY
  283. # Report build and test results
  284. if [[ "${{ needs.build-and-test.result }}" == "failure" ]]; then
  285. echo "## :x: Build or tests failed" >> $GITHUB_STEP_SUMMARY
  286. echo "<details>" >> $GITHUB_STEP_SUMMARY
  287. echo "<summary>Failed projects (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
  288. echo "" >> $GITHUB_STEP_SUMMARY
  289. failed_projects='${{ needs.build-and-test.outputs.failed_projects }}'
  290. if [[ -n "$failed_projects" ]]; then
  291. echo "$failed_projects" | jq -r '.[]' | while IFS=: read -r project failure_reason; do
  292. echo "- **$project**" >> $GITHUB_STEP_SUMMARY
  293. echo " - Failure reason: $failure_reason" >> $GITHUB_STEP_SUMMARY
  294. done
  295. else
  296. echo "No failed projects reported. This might indicate an unexpected error in the workflow." >> $GITHUB_STEP_SUMMARY
  297. fi
  298. echo "" >> $GITHUB_STEP_SUMMARY
  299. echo "</details>" >> $GITHUB_STEP_SUMMARY
  300. elif [[ "${{ needs.build-and-test.result }}" == "success" ]]; then
  301. echo "## :white_check_mark: All builds and tests passed" >> $GITHUB_STEP_SUMMARY
  302. else
  303. echo "## :warning: Build and test job was skipped or canceled" >> $GITHUB_STEP_SUMMARY
  304. fi