1+ name : Poseidon
2+
3+ on :
4+ schedule :
5+ - cron : " 0 0 * * *"
6+ push :
7+ branches :
8+ - main
9+ pull_request :
10+ types : [opened, synchronize, reopened]
11+ branches :
12+ - main
13+
14+ env :
15+ MAX_JOBS : 64
16+ MIN_PROJECTS_PER_JOB : 4
17+ MIN_PROJECTS_FOR_MATRIX : 4
18+
19+ jobs :
20+ changes :
21+ runs-on : ubuntu-latest
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 }}
28+ steps :
29+ - uses : actions/checkout@v4
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+ poseidon:
37+ - added|modified: '**/poseidon/**'
38+ workflow:
39+ - added|modified: '.github/workflows/poseidon.yml'
40+ - name : Analyze Changes
41+ id : analyze
42+ run : |
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 "poseidon" | 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.poseidon }}" == "true" ]]; then
55+ changed_files=(${{ steps.changes.outputs.poseidon_files }})
56+ projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep poseidon | sed 's#/poseidon/.*#/poseidon#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
76+ run : |
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+ # Generate matrix based on number of projects
83+ if [ "$total_projects" -lt "$min_projects_for_matrix" ]; then
84+ echo "matrix=[0]" >> $GITHUB_OUTPUT
85+ else
86+ projects_per_job=$(( (total_projects + max_jobs - 1) / max_jobs ))
87+ projects_per_job=$(( projects_per_job > min_projects_per_job ? projects_per_job : min_projects_per_job ))
88+ num_jobs=$(( (total_projects + projects_per_job - 1) / projects_per_job ))
89+
90+ indices=$(seq 0 $(( num_jobs - 1 )))
91+ echo "matrix=[$(echo $indices | tr ' ' ',')]" >> $GITHUB_OUTPUT
92+ fi
93+
94+ build-and-test :
95+ needs : changes
96+ if : needs.changes.outputs.total_projects != '0'
97+ runs-on : ubuntu-latest
98+ strategy :
99+ fail-fast : false
100+ matrix :
101+ index : ${{ fromJson(needs.changes.outputs.matrix) }}
102+ name : build-and-test-group-${{ matrix.index }}
103+ outputs :
104+ failed_projects : ${{ steps.set-failed.outputs.failed_projects }}
105+ steps :
106+ - uses : actions/checkout@v4
107+ -
uses :
heyAyushh/[email protected] 108+ with :
109+ anchor-version : 0.30.1
110+ solana-cli-version : stable
111+ node-version : 20.x
112+ use-avm : false
113+
114+ # Get Poseidon repo commit SHA for cache key
115+ - name : Get Poseidon commit SHA
116+ id : poseidon-sha
117+ run : |
118+ POSEIDON_SHA=$(git ls-remote https://github.com/Turbin3/poseidon HEAD | cut -f1)
119+ echo "sha=$POSEIDON_SHA" >> $GITHUB_OUTPUT
120+
121+ # Setup Rust cache
122+ - name : Setup Rust cache
123+ uses : Swatinem/rust-cache@v2
124+ with :
125+ shared-key : " poseidon-cli"
126+ cache-on-failure : true
127+ workspaces : |
128+ ~/.cargo/bin/poseidon -> Cargo.lock
129+
130+ # Cache Poseidon binary
131+ - name : Cache Poseidon binary
132+ id : cache-poseidon
133+ uses : actions/cache@v3
134+ with :
135+ path : ~/.cargo/bin/poseidon
136+ key : poseidon-${{ runner.os }}-${{ steps.poseidon-sha.outputs.sha }}
137+
138+ # Install Poseidon CLI only if not cached
139+ - name : Install Poseidon CLI
140+ if : steps.cache-poseidon.outputs.cache-hit != 'true'
141+ run : |
142+ cargo install --git https://github.com/Turbin3/poseidon
143+ - name : Display Versions and Install pnpm
144+ run : |
145+ solana -V
146+ solana-keygen new --no-bip39-passphrase
147+ rustc -V
148+ poseidon --version
149+ npm i -g pnpm
150+ - name : Build and Test
151+ env :
152+ TOTAL_PROJECTS : ${{ needs.changes.outputs.total_projects }}
153+ PROJECTS_PER_JOB : ${{ env.MIN_PROJECTS_PER_JOB }}
154+ run : |
155+ function build_and_test() {
156+ local project=$1
157+ echo "Building and Testing $project"
158+ cd "$project" || return 1
159+
160+ # Install dependencies
161+ if ! pnpm install --frozen-lockfile; then
162+ echo "::error::pnpm install failed for $project"
163+ echo "$project: pnpm install failed" >> $GITHUB_WORKSPACE/failed_projects.txt
164+ cd - > /dev/null
165+ return 1
166+ fi
167+
168+ # Run poseidon build
169+ if ! poseidon build; then
170+ echo "::error::poseidon build failed for $project"
171+ echo "$project: poseidon build failed" >> $GITHUB_WORKSPACE/failed_projects.txt
172+ rm -rf target
173+ cd - > /dev/null
174+ return 1
175+ fi
176+
177+
178+ # Run poseidon test
179+ if ! poseidon test; then
180+ echo "::error::poseidon test failed for $project"
181+ echo "$project: poseidon test failed" >> $GITHUB_WORKSPACE/failed_projects.txt
182+ rm -rf target node_modules
183+ cd - > /dev/null
184+ return 1
185+ fi
186+
187+ echo "Build and tests succeeded for $project."
188+ rm -rf target node_modules
189+ cd - > /dev/null
190+ return 0
191+ }
192+
193+ # Determine which projects to build in this job
194+ readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
195+ start_index=$(( ${{ matrix.index }} * PROJECTS_PER_JOB ))
196+ end_index=$(( start_index + PROJECTS_PER_JOB ))
197+ end_index=$(( end_index > TOTAL_PROJECTS ? TOTAL_PROJECTS : end_index ))
198+
199+ echo "Projects to build and test in this job"
200+ for i in $(seq $start_index $(( end_index - 1 ))); do
201+ echo "${all_projects[$i]}"
202+ done
203+
204+ # Build and test projects
205+ failed=false
206+ failed_projects=()
207+ for i in $(seq $start_index $(( end_index - 1 ))); do
208+ echo "::group::Building and testing ${all_projects[$i]}"
209+ if ! build_and_test "${all_projects[$i]}"; then
210+ failed=true
211+ failed_projects+=("${all_projects[$i]}")
212+ fi
213+ echo "::endgroup::"
214+ done
215+
216+ if [[ "$failed" == "true" ]]; then
217+ echo "::group::Failed projects"
218+ cat $GITHUB_WORKSPACE/failed_projects.txt
219+ echo "::endgroup::"
220+ echo "failed_projects=${failed_projects[@]}" >> $GITHUB_OUTPUT
221+ exit 1
222+ else
223+ echo "failed_projects=" >> $GITHUB_OUTPUT
224+ fi
225+
226+ - name : Set failed projects output
227+ id : set-failed
228+ if : failure()
229+ run : |
230+ # Prepare failed projects list for output
231+ failed_projects=$(cat $GITHUB_WORKSPACE/failed_projects.txt | jq -R -s -c 'split("\n")[:-1]')
232+ echo "failed_projects=$failed_projects" >> $GITHUB_OUTPUT
233+
234+ summary :
235+ needs : [changes, build-and-test]
236+ if : always()
237+ runs-on : ubuntu-latest
238+ steps :
239+ - uses : actions/checkout@v4
240+ - name : Create job summary
241+ run : |
242+ echo "## Poseidon Workflow Summary" >> $GITHUB_STEP_SUMMARY
243+ echo "- Total projects: ${{ needs.changes.outputs.total_projects }}" >> $GITHUB_STEP_SUMMARY
244+
245+ # List all processed projects
246+ echo "<details>" >> $GITHUB_STEP_SUMMARY
247+ echo "<summary>Projects processed (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
248+ echo "" >> $GITHUB_STEP_SUMMARY
249+ echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]' | while read project; do
250+ echo "- $project" >> $GITHUB_STEP_SUMMARY
251+ done
252+ echo "" >> $GITHUB_STEP_SUMMARY
253+ echo "</details>" >> $GITHUB_STEP_SUMMARY
254+
255+ # Report build and test results
256+ if [[ "${{ needs.build-and-test.result }}" == "failure" ]]; then
257+ echo "## :x: Build or tests failed" >> $GITHUB_STEP_SUMMARY
258+ echo "<details>" >> $GITHUB_STEP_SUMMARY
259+ echo "<summary>Failed projects (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
260+ echo "" >> $GITHUB_STEP_SUMMARY
261+ failed_projects='${{ needs.build-and-test.outputs.failed_projects }}'
262+ if [[ -n "$failed_projects" ]]; then
263+ echo "$failed_projects" | jq -r '.[]' | while IFS=: read -r project failure_reason; do
264+ echo "- **$project**" >> $GITHUB_STEP_SUMMARY
265+ echo " - Failure reason: $failure_reason" >> $GITHUB_STEP_SUMMARY
266+ done
267+ else
268+ echo "No failed projects reported. This might indicate an unexpected error in the workflow." >> $GITHUB_STEP_SUMMARY
269+ fi
270+ echo "" >> $GITHUB_STEP_SUMMARY
271+ echo "</details>" >> $GITHUB_STEP_SUMMARY
272+ elif [[ "${{ needs.build-and-test.result }}" == "success" ]]; then
273+ echo "## :white_check_mark: All builds and tests passed" >> $GITHUB_STEP_SUMMARY
274+ else
275+ echo "## :warning: Build and test job was skipped or canceled" >> $GITHUB_STEP_SUMMARY
276+ fi
0 commit comments