@@ -11,107 +11,246 @@ on:
11
11
branches :
12
12
- main
13
13
14
+ env :
15
+ MAX_JOBS : 64
16
+ MIN_PROJECTS_PER_JOB : 4
17
+ MIN_PROJECTS_FOR_MATRIX : 4
18
+
14
19
jobs :
15
- build :
20
+ changes :
16
21
runs-on : ubuntu-latest
17
- strategy :
18
- matrix :
19
- node-version : [20.x]
20
- solana-version : [stable]
22
+ permissions :
23
+ pull-requests : read
24
+ outputs :
25
+ changed_projects : ${{ steps.analyze.outputs.changed_projects }}
26
+ total_projects : ${{ steps.analyze.outputs.total_projects }}
27
+ matrix : ${{ steps.matrix.outputs.matrix }}
21
28
steps :
22
29
- uses : actions/checkout@v4
23
- - name : Use Node.js ${{ matrix.node-version }}
24
- uses : actions/setup-node@v4
30
+ - uses : dorny/paths-filter@v3
31
+ id : changes
32
+ if : github.event_name == 'pull_request'
25
33
with :
26
- node-version : ${{ matrix.node-version }}
27
- check-latest : true
28
- - uses : heyAyushh/[email protected]
29
- with :
30
- solana-cli-version : ${{ matrix.solana-version }}
31
- - run : solana -V
32
- shell : bash
33
- - name : Install pnpm
34
+ list-files : shell
35
+ filters : |
36
+ native:
37
+ - added|modified: '**/native/**'
38
+ workflow:
39
+ - added|modified: '.github/workflows/ solana-native.yml'
40
+ - name : Analyze Changes
41
+ id : analyze
34
42
run : |
35
- npm install --global pnpm
36
- - name : Build Native programs
43
+ # Generate ignore pattern, excluding comments
44
+ ignore_pattern=$(grep -v '^#' .github/.ghaignore | grep -v '^$' | tr '\n' '|' | sed 's/|$//')
45
+ echo "Ignore pattern: $ignore_pattern"
46
+
47
+ function get_projects() {
48
+ find . -type d -name "native" | grep -vE "$ignore_pattern" | sort
49
+ }
50
+
51
+ # Determine which projects to build and test
52
+ if [[ "${{ github.event_name }}" == "push" || "${{ github.event_name }}" == "schedule" || "${{ steps.changes.outputs.workflow }}" == "true" ]]; then
53
+ projects=$(get_projects)
54
+ elif [[ "${{ steps.changes.outputs.native }}" == "true" ]]; then
55
+ changed_files=(${{ steps.changes.outputs.native_files }})
56
+ projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep native | sed 's#/native/.*#/native#g'; done | grep -vE "$ignore_pattern" | sort -u)
57
+ else
58
+ projects=""
59
+ fi
60
+
61
+ # Output project information
62
+ if [[ -n "$projects" ]]; then
63
+ echo "Projects to build and test"
64
+ echo "$projects"
65
+ total_projects=$(echo "$projects" | wc -l)
66
+ echo "Total projects: $total_projects"
67
+ echo "total_projects=$total_projects" >> $GITHUB_OUTPUT
68
+ echo "changed_projects=$(echo "$projects" | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
69
+ else
70
+ echo "No projects to build and test."
71
+ echo "total_projects=0" >> $GITHUB_OUTPUT
72
+ echo "changed_projects=[]" >> $GITHUB_OUTPUT
73
+ fi
74
+ - name : Generate matrix
75
+ id : matrix
37
76
run : |
38
- declare -a ProjectDirs=($(find . -type d -name "native"| grep -v -f <(grep . .github/.ghaignore | grep -v '^$')))
39
- echo "Projects to Build:"
40
- printf "%s\n" "${ProjectDirs[@]}"
41
- for projectDir in "${ProjectDirs[@]}"; do
42
- echo "
43
- ********
44
- Building $projectDir
45
- ********"
46
- cd $projectDir
47
- if pnpm build; then
48
- echo "Build succeeded for $projectDir."
49
- else
50
- failed=true
51
- failed_builds+=($projectDir)
52
- echo "Build failed for $projectDir. Continuing with the next program."
53
- fi
54
- cd - > /dev/null
55
- done
56
- if [ "$failed" = true ]; then
57
- echo "Programs that failed building:"
58
- printf "%s\n" "${failed_builds[@]}"
59
- exit 1
77
+ total_projects=${{ steps.analyze.outputs.total_projects }}
78
+ max_jobs=${{ env.MAX_JOBS }}
79
+ min_projects_per_job=${{ env.MIN_PROJECTS_PER_JOB }}
80
+ min_projects_for_matrix=${{ env.MIN_PROJECTS_FOR_MATRIX }}
81
+
82
+ if [ "$total_projects" -lt "$min_projects_for_matrix" ]; then
83
+ echo "matrix=[0]" >> $GITHUB_OUTPUT
60
84
else
61
- echo "All programs built successfully."
85
+ projects_per_job=$(( (total_projects + max_jobs - 1) / max_jobs ))
86
+ projects_per_job=$(( projects_per_job > min_projects_per_job ? projects_per_job : min_projects_per_job ))
87
+ num_jobs=$(( (total_projects + projects_per_job - 1) / projects_per_job ))
88
+
89
+ indices=$(seq 0 $(( num_jobs - 1 )))
90
+ echo "matrix=[$(echo $indices | tr ' ' ',')]" >> $GITHUB_OUTPUT
62
91
fi
63
- shell : bash
64
92
65
- test :
93
+ build-and-test :
94
+ needs : changes
95
+ if : needs.changes.outputs.total_projects != '0'
66
96
runs-on : ubuntu-latest
67
97
strategy :
98
+ fail-fast : false
68
99
matrix :
69
- node-version : [20.x]
70
- solana-version : [1.18.17, stable]
100
+ index : ${{ fromJson(needs.changes.outputs.matrix) }}
101
+ name : build-and-test-group-${{ matrix.index }}
102
+ outputs :
103
+ failed_projects : ${{ steps.set-failed.outputs.failed_projects }}
71
104
steps :
72
105
- uses : actions/checkout@v4
73
- - name : Use Node.js ${{ matrix.node-version }}
106
+ - name : Use Node.js
74
107
uses : actions/setup-node@v4
75
108
with :
76
- node-version : ${{ matrix.node-version }}
109
+ node-version : 20.x
77
110
check-latest : true
78
- -
uses :
heyAyushh/[email protected]
79
- with :
80
- solana-cli-version : ${{ matrix.solana-version }}
81
- - run : solana block
82
- shell : bash
83
- - name : Install pnpm
111
+ - name : Setup build environment
112
+ id : setup
84
113
run : |
114
+ # Create the build and test function
115
+ cat << 'EOF' > build_and_test.sh
116
+ function build_and_test() {
117
+ local project=$1
118
+ local solana_version=$2
119
+ echo "Building and Testing $project with Solana $solana_version"
120
+ cd "$project" || return 1
121
+
122
+ # Install dependencies
123
+ if ! pnpm install --frozen-lockfile; then
124
+ echo "::error::pnpm install failed for $project"
125
+ echo "$project: pnpm install failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
126
+ cd - > /dev/null
127
+ return 1
128
+ fi
129
+
130
+ # Build
131
+ if ! pnpm build; then
132
+ echo "::error::build failed for $project"
133
+ echo "$project: build failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
134
+ cd - > /dev/null
135
+ return 1
136
+ fi
137
+
138
+ # Test
139
+ if ! pnpm build-and-test; then
140
+ echo "::error::tests failed for $project"
141
+ echo "$project: tests failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
142
+ cd - > /dev/null
143
+ return 1
144
+ fi
145
+
146
+ echo "Build and tests succeeded for $project with $solana_version version."
147
+ cd - > /dev/null
148
+ return 0
149
+ }
150
+
151
+ function process_projects() {
152
+ local solana_version=$1
153
+
154
+ readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
155
+ start_index=$(( ${{ matrix.index }} * ${{ env.MIN_PROJECTS_PER_JOB }} ))
156
+ end_index=$(( start_index + ${{ env.MIN_PROJECTS_PER_JOB }} ))
157
+ end_index=$(( end_index > ${{ needs.changes.outputs.total_projects }} ? ${{ needs.changes.outputs.total_projects }} : end_index ))
158
+
159
+ echo "Projects to build and test in this job"
160
+ for i in $(seq $start_index $(( end_index - 1 ))); do
161
+ echo "${all_projects[$i]}"
162
+ done
163
+
164
+ failed=false
165
+ for i in $(seq $start_index $(( end_index - 1 ))); do
166
+ echo "::group::Building and testing ${all_projects[$i]}"
167
+ if ! build_and_test "${all_projects[$i]}" "$solana_version"; then
168
+ failed=true
169
+ fi
170
+ echo "::endgroup::"
171
+ done
172
+
173
+ return $([ "$failed" = true ] && echo 1 || echo 0)
174
+ }
175
+ EOF
176
+
177
+ # Make the script executable
178
+ chmod +x build_and_test.sh
179
+
180
+ # Install pnpm
85
181
npm install --global pnpm
86
- - name : Test solana native programs
182
+ - name : Setup Solana stable
183
+
184
+ with :
185
+ solana-cli-version : stable
186
+ - name : Build and Test with Stable
187
+ run : |
188
+ source build_and_test.sh
189
+ solana -V
190
+ rustc -V
191
+ process_projects "stable"
192
+ - name : Setup Solana 1.18.17
193
+
194
+ with :
195
+ solana-cli-version : 1.18.17
196
+ - name : Build and Test with 1.18.17
87
197
run : |
198
+ source build_and_test.sh
88
199
solana -V
89
200
rustc -V
90
- declare -a ProjectDirs=($(find . -type d -name "native"| grep -v -f <(grep . .github/.ghaignore | grep -v '^$')))
91
- echo "Projects to Test:"
92
- printf "%s\n" "${ProjectDirs[@]}"
93
- for projectDir in "${ProjectDirs[@]}"; do
94
- echo "
95
- ********
96
- Testing $projectDir
97
- ********"
98
- cd $projectDir
99
- pnpm install --frozen-lockfile
100
- if pnpm build-and-test; then
101
- echo "Tests succeeded for $projectDir."
201
+ process_projects "1.18.17"
202
+
203
+ - name : Set failed projects output
204
+ id : set-failed
205
+ if : failure()
206
+ run : |
207
+ if [ -f "$GITHUB_WORKSPACE/failed_projects.txt" ]; then
208
+ failed_projects=$(cat $GITHUB_WORKSPACE/failed_projects.txt | jq -R -s -c 'split("\n")[:-1]')
209
+ echo "failed_projects=$failed_projects" >> $GITHUB_OUTPUT
210
+ else
211
+ echo "failed_projects=[]" >> $GITHUB_OUTPUT
212
+ fi
213
+
214
+ summary :
215
+ needs : [changes, build-and-test]
216
+ if : always()
217
+ runs-on : ubuntu-latest
218
+ steps :
219
+ - uses : actions/checkout@v4
220
+ - name : Create job summary
221
+ run : |
222
+ echo "## Native Workflow Summary" >> $GITHUB_STEP_SUMMARY
223
+ echo "- Total projects: ${{ needs.changes.outputs.total_projects }}" >> $GITHUB_STEP_SUMMARY
224
+
225
+ # List all processed projects
226
+ echo "<details>" >> $GITHUB_STEP_SUMMARY
227
+ echo "<summary>Projects processed (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
228
+ echo "" >> $GITHUB_STEP_SUMMARY
229
+ echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]' | while read project; do
230
+ echo "- $project" >> $GITHUB_STEP_SUMMARY
231
+ done
232
+ echo "" >> $GITHUB_STEP_SUMMARY
233
+ echo "</details>" >> $GITHUB_STEP_SUMMARY
234
+
235
+ # Report build and test results
236
+ if [[ "${{ needs.build-and-test.result }}" == "failure" ]]; then
237
+ echo "## :x: Build or tests failed" >> $GITHUB_STEP_SUMMARY
238
+ echo "<details>" >> $GITHUB_STEP_SUMMARY
239
+ echo "<summary>Failed projects (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
240
+ echo "" >> $GITHUB_STEP_SUMMARY
241
+ failed_projects='${{ needs.build-and-test.outputs.failed_projects }}'
242
+ if [[ -n "$failed_projects" ]]; then
243
+ echo "$failed_projects" | jq -r '.[]' | while IFS=: read -r project failure_reason; do
244
+ echo "- **$project**" >> $GITHUB_STEP_SUMMARY
245
+ echo " - Failure reason: $failure_reason" >> $GITHUB_STEP_SUMMARY
246
+ done
102
247
else
103
- failed=true
104
- failed_tests+=($projectDir)
105
- echo "Tests failed for $projectDir. Continuing with the next program."
248
+ echo "No failed projects reported. This might indicate an unexpected error in the workflow." >> $GITHUB_STEP_SUMMARY
106
249
fi
107
- cd - > /dev/null
108
- done
109
- if [ "$failed" = true ]; then
110
- echo "*****************************"
111
- echo "Programs that failed testing:"
112
- printf "%s\n" "${failed_tests[@]}"
113
- exit 1
250
+ echo "" >> $GITHUB_STEP_SUMMARY
251
+ echo "</details>" >> $GITHUB_STEP_SUMMARY
252
+ elif [[ "${{ needs.build-and-test.result }}" == "success" ]]; then
253
+ echo "## :white_check_mark: All builds and tests passed" >> $GITHUB_STEP_SUMMARY
114
254
else
115
- echo "All tests passed."
255
+ echo "## :warning: Build and test job was skipped or canceled" >> $GITHUB_STEP_SUMMARY
116
256
fi
117
- shell : bash
0 commit comments