5b23b36261
* ci: use npm ci instead of npm install for reproducible installs Pins the node binding dependency install to the committed package-lock.json integrity hashes (OpenSSF Scorecard PinnedDependencies). npm ci installs strictly from the lockfile; npm install could resolve newer patch versions. Covers ci.yml and both release.yml node steps. * ci: hash-pin Python dev tooling in ci.yml (Scorecard #19) Replaces the unpinned 'pip install maturin pytest numpy hypothesis' with a hash-locked '--require-hashes -r' install (OpenSSF Scorecard PinnedDependencies). Two lock files are needed because numpy publishes no single release with wheels for both cp39 and cp313 (<=2.0.2 has cp39 only, >=2.1 drops cp39): ci-dev-py39.txt numpy 2.0.2 (Python 3.9, + tomli/exceptiongroup) ci-dev-py3.txt numpy 2.4.6 (Python 3.10+) The step selects the file by matrix.python-version under shell: bash. Both are generated from ci-dev.in via uv (scripts/update-lockfiles.sh, added next). * ci: hash-pin Python deps in bench.yml (Scorecard #16) Replaces the unpinned 'pip install maturin numpy pandas talipp finta' with a hash-locked '--require-hashes -r .github/requirements/bench.txt' install. bench.yml runs on a single Python version (3.11), so one lock file (generated from bench.in via uv) is sufficient. * build: add scripts/update-lockfiles.sh to regenerate all lockfiles One command refreshes every committed lockfile across languages: Cargo.lock and fuzz/Cargo.lock (cargo update), the Node binding package-lock.json, and the hash-pinned Python requirements under .github/requirements/ (uv pip compile --generate-hashes). Uses uv for the Python locks so a target Python version's hashed transitive closure can be resolved without that interpreter installed (needed for the numpy cp39/cp313 split); bootstraps uv if absent. .gitattributes pins *.sh to LF so the script stays runnable on Linux/macOS. * ci: split ci-dev requirements per Python version + Dependabot rehash Splits the single ci-dev.in into ci-dev-py39.in (numpy <2.1, the last series with cp39 wheels) and ci-dev-py3.in (3.10+), giving a 1:1 .in->.txt layout. The cap keeps Python 3.9 permanently installable and stops Dependabot from proposing 3.9-breaking numpy bumps. Adds a Dependabot pip entry on /.github/requirements so the hash-locked tooling is kept current automatically; the canonical manual refresh stays scripts/update-lockfiles.sh. Only the '# via -r' provenance lines in the .txt change; no package versions or hashes move. * ci: cache pip and npm downloads in the PR-loop jobs Adds setup-python cache: pip (ci.yml python matrix + bench.yml, keyed on the hash-locked requirements) and setup-node cache: npm (ci.yml node job, keyed on bindings/node/package-lock.json), on both the primary and retry setup steps. Scoped to jobs that actually install dependencies; the examples-smoke and clippy-bindings jobs install nothing and are left uncached. release.yml is intentionally left out: it runs only on tag push (not the PR loop) and is the publish-critical path, so no caching is added there. * docs: document hash-pinned requirements and update-lockfiles.sh Updates the lockfile-policy table: the bindings/python row no longer claims CI installs tooling unpinned, and a new .github/requirements row documents the hash-locked CI/bench tooling and the per-Python-version ci-dev split. Adds a paragraph pointing contributors at scripts/update-lockfiles.sh (uv-based, self-bootstrapping) as the canonical lockfile refresh. * docs: changelog entry for hash-pinned CI dependency installs
125 lines
5.9 KiB
Markdown
125 lines
5.9 KiB
Markdown
# Contributing to Wickra
|
|
|
|
Thanks for your interest in improving Wickra. This document explains how to
|
|
build the project, the standards a change must meet, and how to get it merged.
|
|
|
|
## License of contributions
|
|
|
|
Wickra is licensed under the **PolyForm Noncommercial License 1.0.0** (see
|
|
[`LICENSE`](LICENSE)). By submitting a contribution you agree that it is
|
|
licensed to the project under those same terms. The Noncommercial license
|
|
permits use for any purpose **other than** a commercial one; keep that in mind
|
|
when proposing features or depending on Wickra elsewhere.
|
|
|
|
## Project layout
|
|
|
|
| Path | Contents |
|
|
| --- | --- |
|
|
| `crates/wickra-core` | The indicator engine — every indicator lives here. |
|
|
| `crates/wickra` | Thin umbrella crate re-exporting `wickra-core`. |
|
|
| `crates/wickra-data` | CSV reader, tick aggregator, resampler, Binance feed. |
|
|
| `bindings/python` | PyO3 bindings (`wickra` on PyPI). |
|
|
| `bindings/node` | napi-rs bindings (`wickra` on npm). |
|
|
| `bindings/wasm` | wasm-bindgen bindings (`wickra-wasm` on npm). |
|
|
| `examples/` | Runnable examples. |
|
|
| `docs/` | Pointer to the project Wiki, which holds all documentation. |
|
|
|
|
## Building and testing
|
|
|
|
### Rust
|
|
|
|
```bash
|
|
cargo fmt --all --check
|
|
cargo clippy --workspace --all-targets -- -D warnings
|
|
cargo test --workspace
|
|
cargo test -p wickra-data --features live-binance
|
|
```
|
|
|
|
The minimum supported Rust version is **1.86** for the workspace crates and
|
|
**1.88** for `bindings/node`; the `msrv` CI job enforces both. These floors are
|
|
not chosen freely — they are the lowest versions our dependencies allow
|
|
(criterion 0.8.2, the bench dev-dependency, requires 1.86; napi-build 2.3.2
|
|
requires 1.88). We keep the MSRV at that dependency-forced floor on purpose so
|
|
the library builds for the widest possible audience; please don't raise it
|
|
without a dependency that actually requires it.
|
|
|
|
### Python
|
|
|
|
```bash
|
|
cd bindings/python
|
|
python -m maturin build --release --out dist
|
|
python -m pip install --force-reinstall --no-deps dist/wickra-*.whl
|
|
python -m pytest -q
|
|
```
|
|
|
|
### Node
|
|
|
|
```bash
|
|
cd bindings/node
|
|
npm install
|
|
npx napi build --platform --release
|
|
node --test __tests__/
|
|
```
|
|
|
|
### WASM
|
|
|
|
```bash
|
|
wasm-pack build bindings/wasm --target web --release --features panic-hook
|
|
wasm-pack test --node bindings/wasm
|
|
```
|
|
|
|
## Lockfile policy
|
|
|
|
| Component | Lockfile | Tracked? | Why |
|
|
| --- | --- | --- | --- |
|
|
| Workspace (Rust) | `Cargo.lock` | **yes** | The workspace ships binaries (examples, fuzz harness) and CI builds, so the dependency graph is pinned for reproducible builds. |
|
|
| `bindings/node` | `package-lock.json` | **yes** | Reproducible `npm install` for the native binding. |
|
|
| `examples/node` | `package-lock.json` | **yes** | Same — the runnable Node examples link the binding via a `file:` dependency. |
|
|
| `bindings/python` | — | n/a (no lockfile) | The published package pins only `numpy>=1.22` at runtime; its native code is pinned through the workspace `Cargo.lock`. The CI/bench dev tooling it installs is hash-locked separately — see the `.github/requirements` row. |
|
|
| `.github/requirements` | `*.txt` (hash-pinned) | **yes** | CI/bench Python tooling, locked with `uv pip compile --generate-hashes` (OpenSSF Scorecard PinnedDependencies). `ci-dev` is split per Python version — `ci-dev-py39.txt` and `ci-dev-py3.txt` — because numpy ships no single release with wheels for both cp39 and cp313; `bench.txt` covers the single-version bench job. |
|
|
| `fuzz` | `fuzz/Cargo.lock` | **no** (ignored) | `fuzz/` is a detached crate; `cargo-fuzz init` generates `fuzz/.gitignore` which ignores its `Cargo.lock`. The fuzz smoke job resolves dependencies fresh, so the lock is not needed for reproducibility here. |
|
|
| `site` (marketing) | `package-lock.json` | **no** (ghost-ignored) | The VitePress site is a local-only project excluded via `.git/info/exclude`; its lockfile stays local. |
|
|
|
|
When adding a new committed Node package, commit its `package-lock.json` too and
|
|
remove any matching ignore rule. Do **not** add a top-level `package-lock.json` —
|
|
the repository root is not an npm package.
|
|
|
|
To refresh every committed lockfile in the workspace — `Cargo.lock`,
|
|
`fuzz/Cargo.lock`, the Node binding lock, and the hash-pinned Python
|
|
requirements — run `./scripts/update-lockfiles.sh`. It uses `uv` for the Python
|
|
locks (and bootstraps it on Linux/macOS if absent) so each target Python
|
|
version's hashed transitive closure can be regenerated without that interpreter
|
|
installed. Dependabot also keeps the `.github/requirements` pins current.
|
|
|
|
## Standards for a change
|
|
|
|
- **Formatting & lints.** `cargo fmt` must leave the tree unchanged and
|
|
`cargo clippy ... -D warnings` must be clean. CI gates both.
|
|
- **Tests.** New behaviour needs tests; bug fixes need a regression test.
|
|
- **Indicator correctness.** A new or changed indicator must have a
|
|
reference-value test against a known-good source (TA-Lib, pandas-ta, or a
|
|
hand-computed value) and a `reset` test.
|
|
- **Streaming parity.** An indicator's `batch` output must equal the sequence
|
|
of `update` calls.
|
|
- **Bindings.** A change to a public indicator API must be mirrored across the
|
|
Python, Node, and WASM bindings, including their type stubs / `.d.ts`.
|
|
- **Docs.** Update the relevant page on the
|
|
[documentation site](https://docs.wickra.org) and the
|
|
`README.md` when behaviour or the public API changes. The docs live in
|
|
a separate git repository: `https://github.com/wickra-lib/wickra-docs`.
|
|
- **Changelog.** Add an entry under `## [Unreleased]` in `CHANGELOG.md`.
|
|
|
|
## Commit and pull-request workflow
|
|
|
|
1. Branch off `main`.
|
|
2. Keep commits focused — one logical change per commit, with an imperative
|
|
subject line and a body explaining *why*.
|
|
3. Open a pull request against `main` and fill in the template.
|
|
4. CI must be green before review.
|
|
|
|
## Reporting bugs and proposing features
|
|
|
|
Use the issue templates under
|
|
[`.github/ISSUE_TEMPLATE`](.github/ISSUE_TEMPLATE). For security-sensitive
|
|
reports, follow [`SECURITY.md`](SECURITY.md) instead of opening a public issue.
|