eo-processor 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eo-processor might be problematic. Click here for more details.
- eo_processor-0.4.0/.github/FUNDING.yml +15 -0
- eo_processor-0.4.0/.github/copilot-instructions.md +322 -0
- eo_processor-0.4.0/.github/workflows/ci.yml +98 -0
- eo_processor-0.4.0/.github/workflows/jules-autofix.yml +130 -0
- eo_processor-0.4.0/.github/workflows/release.yml +238 -0
- eo_processor-0.4.0/.gitignore +73 -0
- eo_processor-0.4.0/AGENTS.md +444 -0
- eo_processor-0.4.0/CHANGELOG +122 -0
- eo_processor-0.4.0/CONTRIBUTING.md +220 -0
- eo_processor-0.4.0/Cargo.lock +387 -0
- eo_processor-0.4.0/Cargo.toml +26 -0
- eo_processor-0.4.0/LICENSE +21 -0
- eo_processor-0.4.0/Makefile +77 -0
- eo_processor-0.4.0/PKG-INFO +542 -0
- eo_processor-0.4.0/QUICKSTART.md +245 -0
- eo_processor-0.4.0/README.md +512 -0
- eo_processor-0.4.0/SECURITY.md +69 -0
- eo_processor-0.4.0/architecture/ADR-template.md +0 -0
- eo_processor-0.4.0/coverage-badge.svg +21 -0
- eo_processor-0.4.0/coverage.xml +64 -0
- eo_processor-0.4.0/examples/README.md +90 -0
- eo_processor-0.4.0/examples/basic_usage.py +133 -0
- eo_processor-0.4.0/examples/map_blocks.py +207 -0
- eo_processor-0.4.0/examples/spatial_distances.py +227 -0
- eo_processor-0.4.0/examples/spectral_indices_extended.py +300 -0
- eo_processor-0.4.0/examples/temporal_operations.py +90 -0
- eo_processor-0.4.0/examples/xarray_dask_usage.py +261 -0
- eo_processor-0.4.0/pyproject.toml +60 -0
- eo_processor-0.4.0/pytest.ini +6 -0
- eo_processor-0.4.0/python/eo_processor/__init__.py +379 -0
- eo_processor-0.4.0/python/eo_processor/__init__.pyi +60 -0
- eo_processor-0.4.0/scripts/benchmark.py +666 -0
- eo_processor-0.4.0/scripts/eo_cli.py +450 -0
- eo_processor-0.4.0/scripts/generate_coverage_badge.py +154 -0
- eo_processor-0.4.0/scripts/jules_session_manager.py +89 -0
- eo_processor-0.4.0/scripts/version.py +199 -0
- eo_processor-0.4.0/src/indices.rs +1008 -0
- eo_processor-0.4.0/src/lib.rs +40 -0
- eo_processor-0.4.0/src/spatial.rs +452 -0
- eo_processor-0.4.0/src/temporal.rs +235 -0
- eo_processor-0.4.0/src/tests.rs +48 -0
- eo_processor-0.4.0/tests/__init__.py +0 -0
- eo_processor-0.4.0/tests/test_indices.py +341 -0
- eo_processor-0.4.0/tests/test_spatial.py +67 -0
- eo_processor-0.4.0/tests/test_spatial_distances.py +179 -0
- eo_processor-0.4.0/tests/test_temporal.py +46 -0
- eo_processor-0.4.0/tox.ini +74 -0
- eo_processor-0.4.0/uv.lock +1595 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
4
|
+
patreon: bnjam
|
|
5
|
+
open_collective: # Replace with a single Open Collective username
|
|
6
|
+
ko_fi: # Replace with a single Ko-fi username
|
|
7
|
+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
8
|
+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
9
|
+
liberapay: # Replace with a single Liberapay username
|
|
10
|
+
issuehunt: # Replace with a single IssueHunt username
|
|
11
|
+
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
|
12
|
+
polar: # Replace with a single Polar username
|
|
13
|
+
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
|
14
|
+
thanks_dev: # Replace with a single thanks.dev username
|
|
15
|
+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# GitHub Copilot / AI Agent Instructions
|
|
2
|
+
|
|
3
|
+
This document defines how AI code assistants (e.g., GitHub Copilot, autonomous agents) should behave when generating, modifying, or proposing code in the `eo-processor` repository. It complements `AGENTS.md` and focuses on inline assistance, code completion, and automated changes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Scope of Assistance
|
|
8
|
+
|
|
9
|
+
AI assistance MAY:
|
|
10
|
+
- Suggest Rust functions implementing EO spectral indices and numeric transforms
|
|
11
|
+
- Provide Python integration layers (imports, type stubs, tests)
|
|
12
|
+
- Improve performance in pure Rust (keeping safety)
|
|
13
|
+
- Add unit tests and benchmarks (lightweight)
|
|
14
|
+
- Refactor for clarity without breaking public APIs
|
|
15
|
+
- Maintain or regenerate `coverage-badge.svg` via existing script logic
|
|
16
|
+
- Propose documentation updates (README, QUICKSTART)
|
|
17
|
+
- Add missing type hints, docstrings, or comments
|
|
18
|
+
|
|
19
|
+
AI assistance MUST NOT:
|
|
20
|
+
- Introduce external services, network calls, or file I/O into the core computational path
|
|
21
|
+
- Add unchecked dependencies without justification
|
|
22
|
+
- Use `unsafe` Rust blocks (unless explicitly approved and documented)
|
|
23
|
+
- Commit partial implementations (functions without tests or exports)
|
|
24
|
+
- Generate large boilerplate unrelated to repository goals
|
|
25
|
+
- Fabricate data, benchmarks, or security claims
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Repository Structure Awareness
|
|
30
|
+
|
|
31
|
+
Key areas for code suggestions:
|
|
32
|
+
- `src/` – Rust source (PyO3 module functions)
|
|
33
|
+
- `python/eo_processor/` – Python package exports and type stubs
|
|
34
|
+
- `tests/` – Python tests (add test_*.py files here)
|
|
35
|
+
- `scripts/` – Utility scripts (badge generation, etc.)
|
|
36
|
+
- `pyproject.toml` – Metadata, versioning, dependencies
|
|
37
|
+
- `tox.ini` – Test environments, coverage configuration
|
|
38
|
+
|
|
39
|
+
Do not invent directories. Use existing patterns (e.g., 1D, 2D variants where appropriate).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Coding Conventions
|
|
44
|
+
|
|
45
|
+
Rust:
|
|
46
|
+
- Format with `cargo fmt`
|
|
47
|
+
- Zero clippy warnings (`cargo clippy -- -D warnings`)
|
|
48
|
+
- Prefer slice/array operations over explicit loops where clear
|
|
49
|
+
- Avoid `unwrap()`—use explicit error handling or safe assumptions
|
|
50
|
+
- Enforce shape matching before arithmetic when combining arrays
|
|
51
|
+
|
|
52
|
+
Python:
|
|
53
|
+
- Export new functions in `__init__.py`
|
|
54
|
+
- Add type stubs in `__init__.pyi`
|
|
55
|
+
- Keep tests deterministic (no random seeds unless specified)
|
|
56
|
+
- Use NumPy array assertions (`np.allclose`, `np.isfinite`)
|
|
57
|
+
- Prefer descriptive test names (`test_<function>_<case>`)
|
|
58
|
+
|
|
59
|
+
Documentation:
|
|
60
|
+
- Add formulas for new spectral indices
|
|
61
|
+
- Include minimal runnable examples
|
|
62
|
+
- Maintain consistency with existing README function lists
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 4. Performance Guidance
|
|
67
|
+
|
|
68
|
+
When suggesting optimizations:
|
|
69
|
+
- Preserve numerical correctness (tolerance ≤ 1e-9)
|
|
70
|
+
- Prefer vectorized ndarray operations in Rust
|
|
71
|
+
- Avoid micro-optimizations that reduce readability unless >20% speed gain
|
|
72
|
+
- Suggest benchmarking snippets only if relevant
|
|
73
|
+
|
|
74
|
+
Benchmarks must be realistic (large arrays, e.g., 5000 x 5000).
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 5. Safe Rust Patterns
|
|
79
|
+
|
|
80
|
+
DO:
|
|
81
|
+
- Use `ndarray` arithmetic operations
|
|
82
|
+
- Validate shapes early
|
|
83
|
+
- Use small constants like `1e-10` for numeric stability
|
|
84
|
+
|
|
85
|
+
DON'T:
|
|
86
|
+
- Introduce `unsafe`
|
|
87
|
+
- Allocate excessively in tight loops
|
|
88
|
+
- Return inconsistent array dimensions
|
|
89
|
+
- Silently swallow errors
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 6. Adding New Functionality (Checklist)
|
|
94
|
+
|
|
95
|
+
For each new function:
|
|
96
|
+
1. Implement in Rust (`src/lib.rs`)
|
|
97
|
+
2. Register with `#[pyfunction]` and add to `#[pymodule]`
|
|
98
|
+
3. Export in `python/eo_processor/__init__.py`
|
|
99
|
+
4. Add type stub in `python/eo_processor/__init__.pyi`
|
|
100
|
+
5. Create test file in `tests/`
|
|
101
|
+
6. Update README function list and examples
|
|
102
|
+
7. Run full pre-commit checklist (Section 8)
|
|
103
|
+
8. Consider version bump (patch/minor/major)
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 7. Versioning Rules (SemVer)
|
|
108
|
+
|
|
109
|
+
- Patch: Bug fixes / internal refactors
|
|
110
|
+
- Minor: New functions (backward-compatible)
|
|
111
|
+
- Major: Breaking changes (signature changes, removals)
|
|
112
|
+
|
|
113
|
+
AI agents should tag suggestions that require version bumps.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 8. Pre-Commit / Pre-Push Checklist (MANDATORY)
|
|
118
|
+
|
|
119
|
+
Before generating or validating a commit, ensure:
|
|
120
|
+
- [ ] Rust code formatted (`cargo fmt`)
|
|
121
|
+
- [ ] No clippy warnings
|
|
122
|
+
- [ ] `cargo check --lib` succeeds (or `cargo test` if tests exist)
|
|
123
|
+
- [ ] Python style/lint: ruff passes
|
|
124
|
+
- [ ] `mypy python/eo_processor` has no new errors
|
|
125
|
+
- [ ] Tests pass (`tox` or individual env)
|
|
126
|
+
- [ ] Coverage updated if logic changed (`tox -e coverage`)
|
|
127
|
+
- [ ] Badge regenerated if coverage changed:
|
|
128
|
+
`python scripts/generate_coverage_badge.py coverage.xml coverage-badge.svg`
|
|
129
|
+
- [ ] README / docs updated for public API changes
|
|
130
|
+
- [ ] Version updated if needed
|
|
131
|
+
- [ ] No secrets or unintended large binaries staged
|
|
132
|
+
- [ ] Commit message follows convention (Section 9)
|
|
133
|
+
- [ ] Performance impact evaluated (if relevant)
|
|
134
|
+
|
|
135
|
+
If any step fails, DO NOT propose commit.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 9. Commit Message Pattern
|
|
140
|
+
|
|
141
|
+
Format:
|
|
142
|
+
```
|
|
143
|
+
<type>(scope): concise summary
|
|
144
|
+
|
|
145
|
+
Optional body explaining rationale, benchmarks, or docs
|
|
146
|
+
Refs: issue numbers, benchmarks, links
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Types:
|
|
150
|
+
- feat
|
|
151
|
+
- fix
|
|
152
|
+
- perf
|
|
153
|
+
- docs
|
|
154
|
+
- test
|
|
155
|
+
- chore
|
|
156
|
+
- build
|
|
157
|
+
- ci
|
|
158
|
+
|
|
159
|
+
Example:
|
|
160
|
+
```
|
|
161
|
+
feat(indices): add Enhanced Vegetation Index (EVI)
|
|
162
|
+
|
|
163
|
+
Implements EVI with 1D/2D variants. Adds tests and docs.
|
|
164
|
+
Benchmarked at 1.28x speed vs NumPy baseline.
|
|
165
|
+
Refs: #45
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 10. When NOT to Suggest Code
|
|
171
|
+
|
|
172
|
+
AI agents should refrain if:
|
|
173
|
+
- Required context (file content) is missing
|
|
174
|
+
- Task involves strategic redesign (defer to human proposal)
|
|
175
|
+
- Operation would introduce external network access
|
|
176
|
+
- Performance trade-offs cannot be reasonably estimated
|
|
177
|
+
- Security posture could be affected (e.g., unsafe / dynamic execution)
|
|
178
|
+
|
|
179
|
+
Instead, propose clarification questions or planning steps.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 11. Testing Guidance
|
|
184
|
+
|
|
185
|
+
Minimal test template:
|
|
186
|
+
```python
|
|
187
|
+
def test_new_index_basic():
|
|
188
|
+
import numpy as np
|
|
189
|
+
from eo_processor import new_index
|
|
190
|
+
a = np.array([0.6, 0.7, 0.8])
|
|
191
|
+
b = np.array([0.2, 0.3, 0.4])
|
|
192
|
+
out = new_index(a, b)
|
|
193
|
+
assert out.shape == a.shape
|
|
194
|
+
assert np.isfinite(out).all()
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Edge case tests:
|
|
198
|
+
- Zeros / near-zero denominators
|
|
199
|
+
- Matching shape enforcement
|
|
200
|
+
- Large arrays performance (sanity only; avoid huge runtime)
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 12. Coverage Badge Regeneration
|
|
205
|
+
|
|
206
|
+
Trigger conditions:
|
|
207
|
+
- Python code modified
|
|
208
|
+
- Tests added/removed
|
|
209
|
+
- Coverage threshold changes
|
|
210
|
+
|
|
211
|
+
Sequence:
|
|
212
|
+
1. `tox -e coverage`
|
|
213
|
+
2. Confirm `coverage.xml` exists
|
|
214
|
+
3. Run script
|
|
215
|
+
4. Verify SVG text alignment locally
|
|
216
|
+
5. Stage updated badge
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 13. Documentation Updates
|
|
221
|
+
|
|
222
|
+
For new features:
|
|
223
|
+
- Add function signature to README
|
|
224
|
+
- Add usage snippet
|
|
225
|
+
- Provide formula
|
|
226
|
+
- Mention typical EO use-case
|
|
227
|
+
- Optional: scientific reference (if standard index)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 14. Performance Claim Template
|
|
232
|
+
|
|
233
|
+
If suggesting optimization:
|
|
234
|
+
```
|
|
235
|
+
Benchmark:
|
|
236
|
+
Array size: 5000 x 5000
|
|
237
|
+
Old: 1.42s
|
|
238
|
+
New: 1.05s
|
|
239
|
+
Speedup: 1.35x
|
|
240
|
+
|
|
241
|
+
Methodology:
|
|
242
|
+
Single run, warm cache discarded
|
|
243
|
+
Python timing via time.time()
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 15. Do / Don’t Summary
|
|
249
|
+
|
|
250
|
+
DO:
|
|
251
|
+
- Maintain parity between Rust and Python exports
|
|
252
|
+
- Use explicit naming (`index_1d`, `index_2d`)
|
|
253
|
+
- Keep code minimal and safe
|
|
254
|
+
- Suggest incremental changes
|
|
255
|
+
|
|
256
|
+
DON’T:
|
|
257
|
+
- Combine unrelated changes in one diff
|
|
258
|
+
- Introduce unstable dependencies
|
|
259
|
+
- Modify unrelated files casually
|
|
260
|
+
- Reduce test coverage
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 16. Rollback Strategy (If Committed Mistake Detected)
|
|
265
|
+
|
|
266
|
+
1. Reset local changes: `git reset --hard HEAD~1` (if safe)
|
|
267
|
+
2. Revert remote commit: create a `revert:` commit
|
|
268
|
+
3. Re-run full checklist
|
|
269
|
+
4. Open issue documenting root cause if systemic
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 17. Example Rust Function Pattern (Reusable)
|
|
274
|
+
|
|
275
|
+
```rust
|
|
276
|
+
#[pyfunction]
|
|
277
|
+
fn example_index<'py>(
|
|
278
|
+
py: Python<'py>,
|
|
279
|
+
a: PyReadonlyArray2<f64>,
|
|
280
|
+
b: PyReadonlyArray2<f64>,
|
|
281
|
+
) -> PyResult<&'py PyArray2<f64>> {
|
|
282
|
+
let a_arr = a.as_array();
|
|
283
|
+
let b_arr = b.as_array();
|
|
284
|
+
assert_eq!(a_arr.shape(), b_arr.shape(), "Input arrays must have matching shapes");
|
|
285
|
+
let numerator = &a_arr - &b_arr;
|
|
286
|
+
let denominator = &a_arr + &b_arr + 1e-10;
|
|
287
|
+
let out = numerator / denominator;
|
|
288
|
+
Ok(out.into_pyarray(py))
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 18. Red Flags (Require Human Review)
|
|
295
|
+
|
|
296
|
+
- Proposal to add GPU or distributed code paths
|
|
297
|
+
- Changes to module initialization signature
|
|
298
|
+
- Introduction of concurrency primitives (threads / async)
|
|
299
|
+
- Large memory allocations (> several GB)
|
|
300
|
+
- Breaking API removals
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 19. Optional Extensions (Future Suggestions)
|
|
305
|
+
|
|
306
|
+
Agents may propose (but not implement without approval):
|
|
307
|
+
- Additional indices: EVI, SAVI, NBR, GCI
|
|
308
|
+
- Sliding window operations
|
|
309
|
+
- Batch spectral composite builder
|
|
310
|
+
- Multi-threaded internal tiling (must justify)
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 20. Final Rule
|
|
315
|
+
|
|
316
|
+
If uncertain about spec or impact → STOP and generate a structured clarification prompt rather than guessing.
|
|
317
|
+
|
|
318
|
+
Quality > speed. Safety > novelty.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
End of GitHub Copilot / AI Agent Instructions.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# --- TRIGGER: Runs on Pull Requests ---
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, synchronize, reopened, edited]
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
CARGO_TERM_COLOR: always
|
|
14
|
+
RUN_BENCHMARKS: false
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: Tests via tox (Python ${{ matrix.python-version }})
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: write # Needed for coverage badge/artifacts
|
|
22
|
+
strategy:
|
|
23
|
+
matrix:
|
|
24
|
+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
25
|
+
steps:
|
|
26
|
+
- name: Checkout repository
|
|
27
|
+
uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
# --- SETUP ---
|
|
30
|
+
- name: Install Rust toolchain
|
|
31
|
+
# Required for the 'clippy' environment and maturin builds
|
|
32
|
+
uses: dtolnay/rust-toolchain@stable
|
|
33
|
+
|
|
34
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
35
|
+
uses: actions/setup-python@v5
|
|
36
|
+
with:
|
|
37
|
+
python-version: ${{ matrix.python-version }}
|
|
38
|
+
|
|
39
|
+
- name: Install uv and tools
|
|
40
|
+
uses: astral-sh/setup-uv@v3
|
|
41
|
+
|
|
42
|
+
- name: Install tox and plugins
|
|
43
|
+
# Install tox and the tox-gh-actions plugin into the base environment
|
|
44
|
+
run: uv pip install --system tox tox-gh-actions
|
|
45
|
+
|
|
46
|
+
# --- LINT & CLIPPY STEPS (Run once on 3.11) ---
|
|
47
|
+
# Running static checks only on one version to save time
|
|
48
|
+
- name: 🔎 Run Lint (tox -e lint)
|
|
49
|
+
if: matrix.python-version == '3.11'
|
|
50
|
+
run: tox -e lint
|
|
51
|
+
|
|
52
|
+
- name: 🔎 Run Rust Checks (tox -e clippy)
|
|
53
|
+
if: matrix.python-version == '3.11'
|
|
54
|
+
run: tox -e clippy
|
|
55
|
+
|
|
56
|
+
# --- CORE TESTS STEP (Run on both 3.11 and 3.12) ---
|
|
57
|
+
- name: 🧪 Run Tests (tox -e py${{ matrix.python-version }})
|
|
58
|
+
id: tests_step
|
|
59
|
+
# Runs 'maturin develop' and 'pytest -q'
|
|
60
|
+
run: tox -e py${{ matrix.python-version }}
|
|
61
|
+
|
|
62
|
+
# --- COVERAGE & BADGE STEPS (Run once on 3.12) ---
|
|
63
|
+
- name: 📊 Run Coverage Threshold Check
|
|
64
|
+
if: matrix.python-version == '3.12'
|
|
65
|
+
id: coverage_step
|
|
66
|
+
# This assumes the tox 'coverage' environment produces a coverage.xml file in the repository root.
|
|
67
|
+
run: |
|
|
68
|
+
set -euo pipefail
|
|
69
|
+
tox -e coverage
|
|
70
|
+
python scripts/generate_coverage_badge.py coverage.xml coverage-badge.svg || echo "Badge generation skipped"
|
|
71
|
+
|
|
72
|
+
- name: 🚀 Run Example Scripts (Smoke Tests)
|
|
73
|
+
if: matrix.python-version == '3.12'
|
|
74
|
+
run: |
|
|
75
|
+
set -e
|
|
76
|
+
echo "Running smoke-test examples..."
|
|
77
|
+
uv run python examples/basic_usage.py
|
|
78
|
+
uv run python examples/temporal_operations.py
|
|
79
|
+
uv run python examples/spatial_distances.py
|
|
80
|
+
# Optional dependencies may not be installed in minimal CI; tolerate failure.
|
|
81
|
+
uv run python examples/xarray_dask_usage.py || echo "xarray/dask example skipped (optional deps missing)"
|
|
82
|
+
uv run python examples/map_blocks.py || echo "map_blocks example skipped (optional deps missing)"
|
|
83
|
+
|
|
84
|
+
- name: 🏁 Benchmark (Optional)
|
|
85
|
+
if: matrix.python-version == '3.12' && env.RUN_BENCHMARKS == 'true'
|
|
86
|
+
run: |
|
|
87
|
+
set -e
|
|
88
|
+
echo "Running benchmark suite (with NumPy baseline comparisons)..."
|
|
89
|
+
uv run python scripts/benchmark.py --group all --height 1024 --width 1024 --time 8 --points-a 500 --points-b 500 --point-dim 8 --loops 2 --warmups 1 --compare-numpy --json-out benchmark-results.json --quiet
|
|
90
|
+
echo "Benchmark JSON generated: benchmark-results.json"
|
|
91
|
+
- name: 📦 Upload Benchmark Artifact
|
|
92
|
+
if: matrix.python-version == '3.12' && env.RUN_BENCHMARKS == 'true'
|
|
93
|
+
uses: actions/upload-artifact@v4
|
|
94
|
+
with:
|
|
95
|
+
name: benchmark-results
|
|
96
|
+
path: benchmark-results.json
|
|
97
|
+
if-no-files-found: warn
|
|
98
|
+
retention-days: 7
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
name: 🤖 Jules Autofix
|
|
2
|
+
|
|
3
|
+
# This workflow runs only when the main CI workflow completes and fails on a pull_request
|
|
4
|
+
on:
|
|
5
|
+
workflow_run:
|
|
6
|
+
workflows: [CI]
|
|
7
|
+
types: [completed]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_sha }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
jules_autofix:
|
|
15
|
+
name: Run Jules Fix Session
|
|
16
|
+
# Use a faster, lighter runner since it's mostly Python scripting
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
pull-requests: write # Needed to comment on the PR
|
|
20
|
+
contents: read # Needed to checkout code
|
|
21
|
+
actions: read # Needed to list/download artifacts
|
|
22
|
+
|
|
23
|
+
# Only run if the CI failed AND it was triggered by a pull request
|
|
24
|
+
if: |
|
|
25
|
+
github.event.workflow_run.conclusion == 'failure' &&
|
|
26
|
+
github.event.workflow_run.event == 'pull_request'
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
# Use the commit SHA from the failed run to ensure context is correct
|
|
33
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
34
|
+
|
|
35
|
+
- name: Get PR Number and Author
|
|
36
|
+
id: get_pr
|
|
37
|
+
uses: actions/github-script@v6
|
|
38
|
+
with:
|
|
39
|
+
script: |
|
|
40
|
+
// Find the PR associated with the commit SHA from the failed workflow run
|
|
41
|
+
const { data: pullRequest } = await github.rest.pulls.list({
|
|
42
|
+
owner: context.repo.owner,
|
|
43
|
+
repo: context.repo.repo,
|
|
44
|
+
head: context.repo.owner + ':' + context.event.workflow_run.head_branch,
|
|
45
|
+
});
|
|
46
|
+
if (pullRequest.length === 0) {
|
|
47
|
+
core.info('Could not find corresponding pull request. Skipping.');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
core.setOutput('pr_number', pullRequest[0].number);
|
|
51
|
+
core.setOutput('pr_author', pullRequest[0].user.login);
|
|
52
|
+
core.setOutput('pr_repo', pullRequest[0].base.repo.full_name);
|
|
53
|
+
|
|
54
|
+
- name: Check if Author is "bnjam" (or other approved user)
|
|
55
|
+
# This conditional check ensures the script only runs for approved users, matching the original logic
|
|
56
|
+
if: ${{ steps.get_pr.outputs.pr_author != 'bnjam' }}
|
|
57
|
+
run: |
|
|
58
|
+
echo "Skipping Jules session. PR author is not 'bnjam'."
|
|
59
|
+
exit 0
|
|
60
|
+
|
|
61
|
+
- name: Set up Python
|
|
62
|
+
uses: actions/setup-python@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: "3.12"
|
|
65
|
+
|
|
66
|
+
# --- Download Artifacts from failed run ---
|
|
67
|
+
- name: 📥 Find and Download Failure Context
|
|
68
|
+
uses: actions/github-script@v6
|
|
69
|
+
id: download_artifact
|
|
70
|
+
with:
|
|
71
|
+
script: |
|
|
72
|
+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
|
73
|
+
owner: context.repo.owner,
|
|
74
|
+
repo: context.repo.repo,
|
|
75
|
+
run_id: context.event.workflow_run.id,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Find the artifact uploaded by the failing test job (e.g., failure-context-3.12)
|
|
79
|
+
const matchArtifact = artifacts.data.artifacts.find(artifact =>
|
|
80
|
+
artifact.name.startsWith('failure-context-')
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (matchArtifact) {
|
|
84
|
+
const download = await github.rest.actions.downloadArtifact({
|
|
85
|
+
owner: context.repo.owner,
|
|
86
|
+
repo: context.repo.repo,
|
|
87
|
+
artifact_id: matchArtifact.id,
|
|
88
|
+
archive_format: 'zip',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const fs = require('fs');
|
|
92
|
+
const unzip = require('unzip-stream');
|
|
93
|
+
|
|
94
|
+
// This downloads the zip, extracts it, and makes the error message available
|
|
95
|
+
fs.writeFileSync('failure-context.zip', Buffer.from(download.data));
|
|
96
|
+
fs.mkdirSync('failure-context');
|
|
97
|
+
const readStream = fs.createReadStream('failure-context.zip');
|
|
98
|
+
const extractStream = unzip.Extract({ path: 'failure-context' });
|
|
99
|
+
|
|
100
|
+
await new Promise((resolve, reject) => {
|
|
101
|
+
extractStream.on('close', resolve);
|
|
102
|
+
extractStream.on('error', reject);
|
|
103
|
+
readStream.pipe(extractStream);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log('Successfully downloaded and extracted failure context.');
|
|
107
|
+
} else {
|
|
108
|
+
core.setFailed('Could not find failure-context artifact. Aborting autofix attempt.');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
- name: Install dependencies for API interaction
|
|
112
|
+
run: pip install requests # Need 'requests' to talk to the Jules API
|
|
113
|
+
|
|
114
|
+
- name: 💬 Run Jules Session Manager
|
|
115
|
+
env:
|
|
116
|
+
JULES_API_KEY: ${{ secrets.JULES_API_KEY }}
|
|
117
|
+
REPO_NAME: ${{ steps.get_pr.outputs.pr_repo }}
|
|
118
|
+
PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
|
|
119
|
+
BRANCH_NAME: ${{ github.event.workflow_run.head_branch }}
|
|
120
|
+
ERROR_MESSAGE: ""
|
|
121
|
+
run: |
|
|
122
|
+
# Load the error message from the downloaded artifact
|
|
123
|
+
if [ -f failure-context/error_message.txt ]; then
|
|
124
|
+
export ERROR_MESSAGE=$(cat failure-context/error_message.txt)
|
|
125
|
+
else
|
|
126
|
+
export ERROR_MESSAGE="CI failed, but the specific error message artifact was missing."
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Execute the Python script that interacts with the fixing system
|
|
130
|
+
python ./scripts/jules_session_manager.py
|