1+ name : Merge and Distribute Samples
2+
3+ on :
4+ push :
5+ branches :
6+ - main
7+ paths :
8+ - ' samples/**'
9+ pull_request :
10+ branches :
11+ - main
12+ types : [closed]
13+ workflow_dispatch :
14+
15+ jobs :
16+ process_samples :
17+ runs-on : ubuntu-latest
18+ if : github.event.pull_request.merged == true || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
19+
20+ permissions :
21+ contents : write
22+
23+ steps :
24+ - name : Checkout repository
25+ uses : actions/checkout@v4
26+ with :
27+ fetch-depth : 0
28+ token : ${{ secrets.GITHUB_TOKEN }}
29+
30+ - name : Set up Python
31+ uses : actions/setup-python@v5
32+ with :
33+ python-version : ' 3.11'
34+
35+ - name : Combine sample.json files
36+ id : combine
37+ continue-on-error : false
38+ run : |
39+ python3 << 'EOF'
40+ import json
41+ import os
42+ import sys
43+ from pathlib import Path
44+
45+ def flatten_samples(data):
46+ """Flatten nested arrays to get individual sample objects"""
47+ if isinstance(data, list):
48+ result = []
49+ for item in data:
50+ if isinstance(item, dict):
51+ # It's a sample object, add it
52+ result.append(item)
53+ elif isinstance(item, list):
54+ # It's a nested array, flatten it recursively
55+ result.extend(flatten_samples(item))
56+ return result
57+ elif isinstance(data, dict):
58+ # Single sample object
59+ return [data]
60+ return []
61+
62+ # Initialize collections
63+ valid_samples = []
64+ errors = []
65+
66+ # Get the current working directory as an absolute path
67+ base_path = Path.cwd().resolve()
68+
69+ # Find all sample.json files
70+ samples_dir = base_path / 'samples'
71+ if samples_dir.exists():
72+ sample_files = list(samples_dir.glob('**/assets/sample.json'))
73+ print(f"Found {len(sample_files)} sample.json files\n")
74+
75+ for sample_file in sample_files:
76+ # Resolve to absolute path first, then make relative
77+ try:
78+ absolute_path = sample_file.resolve()
79+ relative_path = str(absolute_path.relative_to(base_path))
80+ except Exception as e:
81+ print(f"✗ Error resolving path for {sample_file}: {str(e)}")
82+ continue
83+
84+ try:
85+ with open(sample_file, 'r', encoding='utf-8') as f:
86+ content = f.read().strip()
87+
88+ # Check if file is empty
89+ if not content:
90+ errors.append({
91+ "file": relative_path,
92+ "error": "Empty file"
93+ })
94+ print(f"✗ Empty file: {relative_path}")
95+ continue
96+
97+ # Try to parse as JSON
98+ try:
99+ sample_data = json.loads(content)
100+ # Flatten the data to ensure we get individual samples
101+ flattened = flatten_samples(sample_data)
102+ if flattened:
103+ valid_samples.extend(flattened)
104+ print(f"✓ Successfully processed: {relative_path} ({len(flattened)} sample(s))")
105+ else:
106+ errors.append({
107+ "file": relative_path,
108+ "error": "No valid samples found after parsing"
109+ })
110+ print(f"✗ No valid samples in: {relative_path}")
111+ except json.JSONDecodeError as e:
112+ errors.append({
113+ "file": relative_path,
114+ "error": f"JSON decode error at line {e.lineno}, column {e.colno}: {e.msg}"
115+ })
116+ print(f"✗ JSON error in {relative_path}: {str(e)}")
117+
118+ except PermissionError as e:
119+ errors.append({
120+ "file": relative_path,
121+ "error": f"Permission denied: {str(e)}"
122+ })
123+ print(f"✗ Permission error reading {relative_path}: {str(e)}")
124+
125+ except Exception as e:
126+ errors.append({
127+ "file": relative_path,
128+ "error": f"Unexpected error: {str(e)}"
129+ })
130+ print(f"✗ Unexpected error reading {relative_path}: {str(e)}")
131+ else:
132+ print(f"⚠️ Samples directory not found: {samples_dir}")
133+
134+ # Create .metadata directory if it doesn't exist
135+ metadata_dir = base_path / '.metadata'
136+ metadata_dir.mkdir(exist_ok=True)
137+
138+ # Write combined samples.json as a proper flat JSON array
139+ samples_output = metadata_dir / 'samples.json'
140+ try:
141+ with open(samples_output, 'w', encoding='utf-8') as f:
142+ json.dump(valid_samples, f, indent=4, ensure_ascii=False)
143+ print(f"\n✓ Successfully wrote {samples_output}")
144+ except Exception as e:
145+ print(f"\n✗ Error writing {samples_output}: {str(e)}")
146+ sys.exit(1)
147+
148+ print(f"\n📊 Summary:")
149+ print(f" Total files found: {len(sample_files) if 'sample_files' in locals() else 0}")
150+ print(f" ✓ Valid samples: {len(valid_samples)}")
151+ print(f" ✗ Errors: {len(errors)}")
152+
153+ # Handle errors.json
154+ errors_file = metadata_dir / 'errors.json'
155+
156+ if errors:
157+ # Write errors.json
158+ try:
159+ with open(errors_file, 'w', encoding='utf-8') as f:
160+ json.dump({
161+ "timestamp": "${{ github.event.head_commit.timestamp || github.event.pull_request.merged_at }}",
162+ "commit": "${{ github.sha }}",
163+ "errors": errors
164+ }, f, indent=2, ensure_ascii=False)
165+ print(f" ⚠️ Errors written to: {errors_file}")
166+ except Exception as e:
167+ print(f" ✗ Error writing errors.json: {str(e)}")
168+ else:
169+ # Remove errors.json if it exists
170+ if errors_file.exists():
171+ try:
172+ errors_file.unlink()
173+ print(f" ✓ No errors found. Removed existing errors.json")
174+ except Exception as e:
175+ print(f" ⚠️ Could not remove errors.json: {str(e)}")
176+ else:
177+ print(f" ✓ No errors found.")
178+
179+ # Set output for git commit step
180+ if errors:
181+ with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
182+ f.write(f"has_errors=true\n")
183+ else:
184+ with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
185+ f.write(f"has_errors=false\n")
186+
187+ # Exit successfully even if there were individual file errors
188+ # The workflow should only fail if it can't write the output files
189+ sys.exit(0)
190+
191+ EOF
192+
193+ - name : Check for changes
194+ id : check_changes
195+ run : |
196+ git diff --exit-code .metadata/ || echo "changes=true" >> $GITHUB_OUTPUT
197+ if git diff --exit-code .metadata/; then
198+ echo "changes=false" >> $GITHUB_OUTPUT
199+ else
200+ echo "changes=true" >> $GITHUB_OUTPUT
201+ fi
202+
203+ - name : Commit and push changes
204+ if : steps.check_changes.outputs.changes == 'true'
205+ run : |
206+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
207+ git config --local user.name "github-actions[bot]"
208+ git add .metadata/
209+
210+ if [ "${{ steps.combine.outputs.has_errors }}" == "true" ]; then
211+ git commit -m "chore: update samples metadata (with errors) [skip ci]"
212+ else
213+ git commit -m "chore: update samples metadata [skip ci]"
214+ fi
215+
216+ git push
217+
218+ - name : Check for sample errors
219+ if : always()
220+ run : |
221+ if [ -f .metadata/errors.json ]; then
222+ ERROR_COUNT=$(jq '.errors | length' . metadata/errors.json)
223+
224+ if [ "$ERROR_COUNT" -gt 0 ]; then
225+ echo "::warning:: Found $ERROR_COUNT sample validation error(s). Check the workflow summary for details."
226+
227+ # Output each error as a warning annotation with file reference
228+ jq -r '.errors[] | ":: warning file=\(.file)::\(.error)"' .metadata/errors.json
229+ fi
230+ fi
231+
232+ - name : Summary
233+ if : always()
234+ run : |
235+ echo "## Sample JSON Combination Results" >> $GITHUB_STEP_SUMMARY
236+ echo "" >> $GITHUB_STEP_SUMMARY
237+
238+ if [ -f . metadata/samples.json ]; then
239+ SAMPLE_COUNT=$(jq '. | length' .metadata/samples.json)
240+ echo "✅ **Valid Samples:** $SAMPLE_COUNT" >> $GITHUB_STEP_SUMMARY
241+ fi
242+
243+ if [ -f .metadata/errors.json ]; then
244+ ERROR_COUNT=$(jq '.errors | length' .metadata/errors.json)
245+ echo "" >> $GITHUB_STEP_SUMMARY
246+ echo "⚠️ **Errors Found:** $ERROR_COUNT" >> $GITHUB_STEP_SUMMARY
247+ echo "" >> $GITHUB_STEP_SUMMARY
248+ echo "<details><summary>View errors</summary>" >> $GITHUB_STEP_SUMMARY
249+ echo "" >> $GITHUB_STEP_SUMMARY
250+ echo "\`\`\`json" >> $GITHUB_STEP_SUMMARY
251+ cat . metadata/errors.json >> $GITHUB_STEP_SUMMARY
252+ echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
253+ echo "</details>" >> $GITHUB_STEP_SUMMARY
254+ else
255+ echo "" >> $GITHUB_STEP_SUMMARY
256+ echo "✅ **No Errors**" >> $GITHUB_STEP_SUMMARY
257+ fi
258+
259+ if [ "${{ steps. check_changes.outputs.changes }}" != "true" ]; then
260+ echo "" >> $GITHUB_STEP_SUMMARY
261+ echo "ℹ️ No changes detected in metadata files." >> $GITHUB_STEP_SUMMARY
262+ fi
0 commit comments