Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
618f059
chore: upgrade CI/CD pipeline with comprehensive testing and code qua…
Feb 21, 2026
cadd44d
docs: update CHANGELOG with CI/CD improvements (issue #12)
Feb 21, 2026
ec71ce9
ruff impro
rilma Feb 21, 2026
2872f41
ruff fix
rilma Feb 21, 2026
a78179b
mypy fixes
rilma Feb 21, 2026
e0511b4
single Python version only
rilma Feb 21, 2026
2fe1a2d
Update .github/workflows/smoke.yml
rilma Feb 21, 2026
e1ad012
Update .github/workflows/smoke.yml
rilma Feb 21, 2026
a358f77
Update README.md
rilma Feb 21, 2026
9d10895
Update .github/workflows/smoke.yml
rilma Feb 21, 2026
b41257b
Update pyproject.toml
rilma Feb 21, 2026
39e2059
Update .github/workflows/smoke.yml
rilma Feb 21, 2026
e654ff1
fix: use os.path.commonpath for path traversal check and raise ValueE…
Copilot Feb 21, 2026
e18b276
fix: handle ValueError from os.path.commonpath for cross-drive paths
Copilot Feb 21, 2026
e39217a
test: mock wget.download in test_retrieve to avoid network calls
Copilot Feb 21, 2026
0ad436a
test: address code review - explicit buf.seek(0) and assert mock call…
Copilot Feb 21, 2026
f03e10a
unit-testing
rilma Feb 21, 2026
51ca90e
update log
rilma Feb 21, 2026
c8d0cb7
Update pyiri2016/api/update.py
rilma Feb 21, 2026
67a182b
Update .github/workflows/smoke.yml
rilma Feb 21, 2026
dfa4d22
Fix variable shadowing in is_within_directory inner function
Copilot Feb 21, 2026
b6f1d8b
Update pyproject.toml
rilma Feb 21, 2026
3d1e664
Update Makefile
rilma Feb 21, 2026
250c619
Update .github/workflows/smoke.yml
rilma Feb 21, 2026
86840ed
Add negative test cases for path-traversal protection in update.retri…
Copilot Feb 21, 2026
ecd31b3
Fix tarfile symlink/hardlink escape vulnerability in safe_extract
Copilot Feb 21, 2026
82316fa
fix test cases
rilma Feb 21, 2026
77c8b80
fix deprecation warnings due to Latex characters
rilma Feb 21, 2026
7517370
figures
rilma Feb 21, 2026
6117e49
format
rilma Feb 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 71 additions & 14 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,82 @@
name: Smoke
name: CI

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:
smoke:
build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
Comment thread
rilma marked this conversation as resolved.

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y gfortran cmake ninja-build

- name: Install dependencies
run: make dev

- name: Compile check (smoke test)
run: make smoke

- name: Generate coverage report
run: make coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: false

lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff

- name: Lint with ruff
run: ruff check pyiri2016 tests settings examples scripts

- name: Format check with ruff
run: ruff format pyiri2016 tests settings examples scripts --check

typecheck:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y gfortran cmake
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy numpy

- name: Install dependencies and run smoke test
run: make dev && make smoke
- name: Type check with mypy
run: mypy --ignore-missing-imports
26 changes: 26 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Pre-commit hooks for pyIRI2016
# Install with: pre-commit install
# Run manually: pre-commit run --all-files

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.0
hooks:
- id: ruff
name: ruff check
args: [--fix]
- id: ruff-format
name: ruff format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1
hooks:
- id: mypy
name: mypy type check
additional_dependencies: [
"numpy",
]
args: [
--ignore-missing-imports,
]
exclude: "^(build|dist|source|\.venv)"
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Better separation between `make dev` (development) and `make install` (user installation)
- **.gitignore enhancements**: Added `.venv/`, `.pytest_cache/`, `htmlcov/` for cleaner repository
- **Updated GitHub Actions workflow**: Replaced uv package manager with system dependencies (gfortran, cmake)
- **Comprehensive CI/CD pipeline** (issue #12): Upgraded from minimal smoke test to full testing and quality checks
- Initial effort towards Multi-version Python testing: Set up to 3.11
- Automated pytest with coverage reports
- Ruff linting and code formatting checks
- Mypy type checking
- Codecov coverage integration
- 3 parallel CI jobs: build-and-test, lint, typecheck
- **Code quality tools**:
- Ruff: Integrated for linting and code formatting (configured in `pyproject.toml`)
- Mypy: Integrated for static type checking with NumPy support
- **Pre-commit hooks** (issue #12): Added `.pre-commit-config.yaml` for automated local development checks
- Ruff check and format on commit
- Mypy type checking on commit
- Install with: `pre-commit install && pre-commit run --all-files`
- **Enhanced Makefile targets** (issue #12): New quality assurance commands
- `make lint`: Check code with ruff
- `make lint-fix`: Auto-fix linting issues
- `make format`: Auto-format code with ruff
- `make typecheck`: Run mypy static type checking
- `make pre-commit`: Run all quality checks (lint + typecheck + test)

### Changed

Expand Down
21 changes: 18 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY : build coverage install smoke health test test-examples dev dev-plotting clean-venv
.PHONY : build coverage install smoke health test test-examples dev dev-plotting clean-venv lint lint-fix typecheck format pre-commit

export PYTHONIOENCODING=utf-8
export LC_ALL=en_US.UTF-8
Expand All @@ -12,14 +12,14 @@ dev:
./.venv/bin/python -m pip install --upgrade pip wheel setuptools
./.venv/bin/python -m pip install 'numpy>=2.0' simple-settings beautifulsoup4 wget 'scikit-build-core' cmake ninja charset-normalizer
bash -c 'VIRTUAL_ENV=./.venv PYTHONIOENCODING=utf-8 LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 ./.venv/bin/python -m pip install -e .'
./.venv/bin/python -m pip install pre-commit coverage pytest pytest-cov parameterized
./.venv/bin/python -m pip install pre-commit coverage pytest pytest-cov parameterized ruff mypy

dev-plotting:
[ -d .venv ] || python3 -m venv .venv
./.venv/bin/python -m pip install --upgrade pip wheel setuptools
./.venv/bin/python -m pip install 'numpy>=2.0' simple-settings beautifulsoup4 wget 'scikit-build-core' cmake ninja charset-normalizer
bash -c 'VIRTUAL_ENV=./.venv PYTHONIOENCODING=utf-8 LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 ./.venv/bin/python -m pip install -e .[plotting]'
./.venv/bin/python -m pip install pre-commit coverage pytest pytest-cov parameterized
./.venv/bin/python -m pip install pre-commit coverage pytest pytest-cov parameterized ruff mypy

build:
[ -d .venv ] || python3 -m venv .venv
Expand Down Expand Up @@ -60,3 +60,18 @@ test-examples:
MPLBACKEND=Agg ./.venv/bin/python examples/iri1DExample08.py > /dev/null && echo "✓ iri1DExample08.py passed" || echo "✗ iri1DExample08.py failed"
MPLBACKEND=Agg ./.venv/bin/python scripts/iri2DExample01.py > /dev/null && echo "✓ iri2DExample01.py passed" || echo "✗ iri2DExample01.py failed"
MPLBACKEND=Agg ./.venv/bin/python scripts/iri2DExample02.py > /dev/null && echo "✓ iri2DExample02.py passed" || echo "✗ iri2DExample02.py failed"

lint:
./.venv/bin/python -m ruff check pyiri2016 tests settings examples scripts

lint-fix:
./.venv/bin/python -m ruff check pyiri2016 tests settings examples scripts --fix

format:
./.venv/bin/python -m ruff format pyiri2016 tests settings examples scripts

typecheck:
./.venv/bin/python -m mypy pyiri2016 tests examples scripts --ignore-missing-imports

pre-commit: lint typecheck test
@echo "✅ All checks passed!"
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[![alt tag](https://zenodo.org/badge/DOI/10.5281/zenodo.240895.svg)](https://doi.org/10.5281/zenodo.240895)
[![Smoke](https://github.com/rilma/pyIRI2016/actions/workflows/smoke.yml/badge.svg?branch=main)](https://github.com/rilma/pyIRI2016/actions/workflows/smoke.yml)
[![Smoke CI](https://github.com/rilma/pyIRI2016/actions/workflows/smoke.yml/badge.svg?branch=main)](https://github.com/rilma/pyIRI2016/actions/workflows/smoke.yml)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md)

# pyIRI2016

Expand Down Expand Up @@ -48,13 +50,31 @@ Creates source distribution and binary wheel in `dist/` directory.
## Test

```sh
# Run health checks
make health

# Run syntax check (smoke test)
make smoke

# Run all tests
make test

# Run tests with coverage report
make coverage

# Code quality checks
make lint # Check code with ruff
make lint-fix # Auto-fix linting issues
make format # Auto-format code with ruff
make typecheck # Type checking with mypy

# Run all quality checks
make pre-commit # lint + typecheck + test
```

`make smoke` runs a fast, CI-safe syntax check without network or Fortran build requirements.
`make health` verifies Python, gfortran, and cmake are available, then runs `make smoke`.
**`make smoke`**: Runs fast syntax validation without network or Fortran build requirements.
**`make health`**: Verifies Python, gfortran, and cmake are available, then runs smoke tests.
**`make coverage`**: Runs all tests with coverage analysis and generates HTML report in `htmlcov/`.

## Examples

Expand All @@ -64,8 +84,10 @@ Example outputs:

| | |
|---|---|
| ![Height Profile](figures/iri1DExample01.png) | ![Latitude Profile](figures/iri1DExample02.png) |
| ![Time Profile](figures/iri1DExample08.png) | ![Height vs Time](figures/iri2DExample01.png) |
| ![Height Profile](figures/iri1DExample01.png) |
| ![Height vs Time](figures/iri2DExample01.png) |
| ![Time Profile](figures/iri1DExample08.png) |
| ![Latitude Profile](figures/iri1DExample02.png) |

## Reference

Expand Down
7 changes: 3 additions & 4 deletions examples/example01.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
from pyiri2016 import IRI2016


if __name__ == '__main__':
if __name__ == "__main__":

def main1():

Obj = IRI2016()
IRIData, IRIDATAAdd = Obj.IRI()
print(IRIData['ne'])
print(IRIDATAAdd['NmF2'], IRIDATAAdd['hmF2'])

print(IRIData["ne"])
print(IRIDATAAdd["NmF2"], IRIDATAAdd["hmF2"])

main1()
7 changes: 2 additions & 5 deletions examples/example02.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@
from pyiri2016 import IRI2016Profile


if __name__ == '__main__':

if __name__ == "__main__":

def main1():

iri2016Obj = IRI2016Profile(option=1, verbose=True)

IRI2016Profile(option=1, verbose=True)

# Height Profile Example
main1()

51 changes: 30 additions & 21 deletions examples/iri1DExample01.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,65 @@
from pyiri2016 import IRI2016Profile
from numpy import arange
from matplotlib.pyplot import figure, legend, savefig, close
import seaborn
from pathlib import Path
import uuid

def example01():

""" Height Profile Example """
def example01():
"""Height Profile Example"""

altlim = [100., 1000.]
altstp = 5.
altlim = [100.0, 1000.0]
altstp = 5.0
lat, lon = -11.95, -76.77
year, month, dom = 2003, 11, 21

iri2016Obj = IRI2016Profile(altlim=altlim, altstp=altstp, lat=lat, \
lon=lon, year=year, month=month, dom=dom, option=1, verbose=False)
iri2016Obj = IRI2016Profile(
altlim=altlim,
altstp=altstp,
lat=lat,
lon=lon,
year=year,
month=month,
dom=dom,
option=1,
verbose=False,
)

altbins = arange(altlim[0], altlim[1] + altstp, altstp)

nalt = len(altbins)
index = range(nalt)

fig = figure(figsize=(16,6))
fig = figure(figsize=(16, 6))

pn = fig.add_subplot(121)
ne = iri2016Obj.a[0, index]
pn.plot(ne, altbins, label='N$_e$')
ne = iri2016Obj.a[0, index]
pn.plot(ne, altbins, label="N$_e$")
pn.set_title(iri2016Obj.title1)
pn.set_xlabel('Density (m$^{-3}$)')
pn.set_ylabel('Altitude (km)')
pn.set_xscale('log')
legend(loc='best')
pn.set_xlabel("Density (m$^{-3}$)")
pn.set_ylabel("Altitude (km)")
pn.set_xscale("log")
legend(loc="best")

pn = fig.add_subplot(122)
ti = iri2016Obj.a[2, index]
te = iri2016Obj.a[3, index]
pn.plot(ti, altbins, label='T$_i$')
pn.plot(te, altbins, label='T$_e$')
pn.plot(ti, altbins, label="T$_i$")
pn.plot(te, altbins, label="T$_e$")
pn.set_title(iri2016Obj.title2)
pn.set_xlabel('Temperature ($^\circ$)')
pn.set_ylabel('Altitude (km)')
legend(loc='best')
pn.set_xlabel("Temperature ($^\circ$)")
pn.set_ylabel("Altitude (km)")
legend(loc="best")

# Save to figures folder with unique UUID suffix
output_dir = Path(__file__).parent.parent / "figures"
output_dir.mkdir(exist_ok=True)
unique_id = str(uuid.uuid4())[:8]
output_file = output_dir / f"iri1DExample01_{unique_id}.png"
savefig(output_file, dpi=100, bbox_inches='tight')
savefig(output_file, dpi=100, bbox_inches="tight")
print(f"✓ Plot saved to: {output_file}")
close(fig)

if __name__ == '__main__':

if __name__ == "__main__":
example01()
Loading
Loading