Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
244 changes: 34 additions & 210 deletions .github/PRE_RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -1,223 +1,47 @@
# Pre-Release Validation Checklist

This checklist ensures SDK releases meet quality standards. **Complete ALL items before publishing to PyPI.**
The authoritative release design is documented in
[`docs/RELEASE_PROCESS.md`](../docs/RELEASE_PROCESS.md). Complete every gate
below before publishing a non-prerelease GitHub Release.

## Automated Checks (Run Script)
## Automated Gate

```bash
./scripts/pre-release-validation.sh
```

This script runs all automated validations and reports pass/fail status.

## Manual Checklist

### 1. Version Management ✅
- [ ] Version bumped in `pyproject.toml`
- [ ] Version bumped in `oilpriceapi/__init__.py`
- [ ] Version updated in `CHANGELOG.md`
- [ ] CHANGELOG has comprehensive release notes
- [ ] No `UNRELEASED` sections in CHANGELOG

### 2. Code Quality ✅
- [ ] All unit tests pass (`pytest tests/unit -v`)
- [ ] All integration tests pass (`pytest tests/integration -v`)
- [ ] Test coverage ≥ 80% (`pytest --cov`)
- [ ] No linting errors (`ruff check .`)
- [ ] No type errors (`mypy oilpriceapi`)
- [ ] Code formatted (`black --check .`)

### 3. Integration Validation ✅
- [ ] Historical endpoint tests pass (catches timeout bug)
- [ ] Performance baselines met:
- 1-week queries: <30s
- 1-month queries: <60s
- 1-year queries: <120s
- [ ] All commodities tested
- [ ] Error handling verified

### 4. Documentation ✅
- [ ] README.md updated with new features
- [ ] API documentation current
- [ ] Code examples work
- [ ] Migration guide included (if breaking changes)
- [ ] Docstrings updated for new/changed functions

### 5. Build & Package ✅
- [ ] Clean build: `rm -rf dist/ build/ *.egg-info`
- [ ] Build succeeds: `python -m build`
- [ ] Wheel created: `ls dist/*.whl`
- [ ] Source distribution created: `ls dist/*.tar.gz`
- [ ] Package installs locally: `pip install dist/*.whl`
- [ ] Imports work: `python -c "import oilpriceapi; print(oilpriceapi.__version__)"`

### 6. Backwards Compatibility ✅
- [ ] No breaking changes (or documented in CHANGELOG)
- [ ] Existing code samples still work
- [ ] Deprecations properly warned
- [ ] Migration guide provided (if needed)

### 7. Security ✅
- [ ] No hardcoded credentials
- [ ] No secrets in code or tests
- [ ] Dependencies scanned: `pip-audit`
- [ ] SECURITY.md reviewed and current

### 8. Git & GitHub ✅
- [ ] All changes committed
- [ ] Commit message follows convention
- [ ] Git tag created: `git tag v1.X.Y`
- [ ] Tag pushed: `git push --tags`
- [ ] No uncommitted changes

### 9. PyPI Publishing ✅
- [ ] Test PyPI upload works: `twine upload --repository testpypi dist/*`
- [ ] Test installation from TestPyPI
- [ ] Production PyPI upload: `twine upload dist/*`
- [ ] Verify on PyPI: https://pypi.org/project/oilpriceapi/
- [ ] Installation works: `pip install --upgrade oilpriceapi`

### 10. Post-Release ✅
- [ ] GitHub release created with notes
- [ ] Documentation site updated
- [ ] Announcement prepared (if major release)
- [ ] Monitor error tracking for 24 hours
- [ ] Check PyPI download stats

## What Would Have Caught the v1.4.1 Bug?

The historical timeout bug (reported by idan@comity.ai) would have been caught by:

1. ✅ **Integration Tests** (`tests/integration/test_historical_endpoints.py`)
- `test_7_day_query_uses_past_week_endpoint` - Would fail (67s timeout)
- `test_365_day_query_uses_past_year_endpoint` - Would fail (30s timeout)

2. ✅ **Performance Baselines** (`TestHistoricalPerformanceBaselines`)
- All tests would fail with timeouts

3. ✅ **Pre-Release Script** (`scripts/pre-release-validation.sh`)
- Integration tests would fail
- Script would prevent release

## Automation Script

The `pre-release-validation.sh` script automates items 1-7:
Run from a clean checkout of the release commit:

```bash
# Run full validation
./scripts/pre-release-validation.sh

# Run with verbose output
./scripts/pre-release-validation.sh --verbose

# Skip slow tests (for quick checks)
./scripts/pre-release-validation.sh --skip-slow
python -m pip install --upgrade pip
python -m pip install -e '.[dev]' build pip-audit
ruff check oilpriceapi/
mypy oilpriceapi/ --ignore-missing-imports
pytest tests/ --ignore=tests/integration --ignore=tests/contract -m 'not slow'
python scripts/validate_storefront_claims.py
python scripts/generate_snippet_manifest.py --source-commit "$(git rev-parse HEAD)" --output artifacts/snippets/oilpriceapi-python-snippets-v1.json
python -m build
./scripts/clean-wheel-smoke.sh
pip-audit
```

**Exit Codes:**
- `0` - All checks passed, ready to release
- `1` - One or more checks failed, DO NOT release

## Emergency Release Procedure
The hosted Python 3.8-3.12 matrix, keyless and keyed live tests, canonical
production snippets, and `Scheduled SDK Synthetic` must all be green at the
same release commit. The repository test gate enforces at least 50% aggregate
coverage; increases to that threshold require a reviewed test-coverage change.

If critical bug requires immediate release:

1. Run minimum validation:
```bash
pytest tests/unit -v --tb=short
pytest tests/integration/test_historical_endpoints.py -v
```

2. Verify the specific fix works

3. Document in CHANGELOG as emergency release

4. **Still run full validation after emergency release**

## Failed Validation - What to Do

### Tests Failed
1. Fix failing tests
2. Re-run full validation
3. Update CHANGELOG if fixes required code changes

### Performance Regression
1. Investigate using profiling
2. Fix performance issue
3. Re-establish baseline

### Documentation Missing
1. Update documentation
2. Add code examples
3. Test examples actually work

### Build Failed
1. Check `pyproject.toml` for errors
2. Verify all files included in manifest
3. Test clean build: `rm -rf dist/ && python -m build`

## Version History

| Version | Date | Validator | Result | Notes |
|---------|------|-----------|--------|-------|
| v1.4.2 | 2025-12-16 | Manual | ✅ Pass | Fixed historical timeout bug |
| v1.4.1 | 2025-12-15 | None | ❌ Fail | Historical timeout bug shipped |

*Note: v1.4.1 did not use this checklist, which is why the bug reached production.*

## Integration with CI/CD

### GitHub Actions (Recommended)

```yaml
name: Pre-Release Validation

on:
push:
tags:
- 'v*'

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install dependencies
run: |
pip install -e ".[dev]"
pip install twine pip-audit

- name: Run pre-release validation
env:
OILPRICEAPI_KEY: ${{ secrets.OILPRICEAPI_KEY }}
run: ./scripts/pre-release-validation.sh

- name: Build package
if: success()
run: python -m build

- name: Publish to PyPI
if: success()
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: twine upload dist/*
```
## Release Metadata

## Contact
- `pyproject.toml` and `oilpriceapi/version.py` contain the same version.
- `CHANGELOG.md` has one release section for that version with customer-visible
behavior and recovery guidance.
- The version is absent from PyPI and from existing GitHub releases.
- The release tag is exactly `v<package-version>`.
- The worktree is clean and the tag resolves to the reviewed main commit.

Questions about the validation process:
- GitHub Issues: https://github.com/OilpriceAPI/python-sdk/issues
- Email: support@oilpriceapi.com
## Publication And Recovery

## Related Issues
Publish through a non-prerelease GitHub Release only. The `Publish to PyPI`
workflow verifies the tag, repeats the tests and dependency audit, builds and
installs the wheel in a clean environment, attaches the snippet manifest, and
uses PyPI trusted publishing. Do not upload with Twine or a local API token.

- [#20](https://github.com/OilpriceAPI/python-sdk/issues/20) - Integration tests
- [#21](https://github.com/OilpriceAPI/python-sdk/issues/21) - Performance baselines
- [#22](https://github.com/OilpriceAPI/python-sdk/issues/22) - Pre-release validation (this document)
PyPI artifacts are immutable. If a production defect appears, stop promotion,
yank the affected version, add a failing regression test, and publish a new
patch version through the same gate.
9 changes: 7 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e '.[dev]'
pip install -e '.[dev]' pip-audit

- name: Verify release tag matches package version
env:
Expand All @@ -37,11 +37,13 @@ jobs:

- name: Lint source with ruff
run: ruff check oilpriceapi/
continue-on-error: true

- name: Run unit tests
run: pytest tests/ --ignore=tests/integration --ignore=tests/contract -m 'not slow' --cov=oilpriceapi -v

- name: Audit installed dependencies
run: pip-audit

publish:
name: Publish to PyPI
needs: test
Expand All @@ -68,6 +70,9 @@ jobs:
- name: Build package
run: python -m build

- name: Install and import the exact built wheel
run: ./scripts/clean-wheel-smoke.sh

- name: Build signed snippet manifest
run: |
python scripts/generate_snippet_manifest.py \
Expand Down
22 changes: 9 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.12.0] - 2026-08-11

### Added

- Add sync and async `client.commodities.search(...)`, backed by the current
Expand All @@ -24,6 +26,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Stop retrying exhausted daily, monthly, and trial quota responses. Sync and
async clients now make one request at a durable quota wall while preserving
bounded retry behavior for recoverable hourly and ambiguous 429 responses.
- Replace the demo synthetic's fixed catalogue-size assertion with an
integrity contract for the original core codes and every usable returned
row. Request, transport, and operating-system failures now fail the monitor
instead of being converted to skips.
- Preserve each API record's currency and unit in current and historical
DataFrames instead of labeling a missing currency as USD.
- Remove exact duplicate records introduced by overlapping page boundaries,
Expand Down Expand Up @@ -383,19 +392,6 @@ print(df[["state", "price", "updated_at"]])

---

## [Unreleased]

### Planned

- CLI tool (`oilprice` command)
- WebSocket support for real-time prices
- Advanced caching with Redis
- Technical indicators (RSI, MACD, Bollinger Bands)
- More visualization styles
- Jupyter notebook widgets

---

## Release Notes

### How to Upgrade
Expand Down
2 changes: 1 addition & 1 deletion EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ print("📊 Powered by https://oilpriceapi.com")

Ready to build with these examples?

1. **[Sign up for free](https://oilpriceapi.com/auth/signup)** - Get 1,000 free requests/month
1. **[Sign up for free](https://oilpriceapi.com/auth/signup)** - Get 50 requests/day
2. **[Install the SDK](https://pypi.org/project/oilpriceapi/)** - `pip install oilpriceapi`
3. **[Read the docs](https://docs.oilpriceapi.com/sdk/python)** - Complete API reference
4. **[Choose a plan](https://oilpriceapi.com/pricing)** - Upgrade for more requests
Expand Down
2 changes: 1 addition & 1 deletion oilpriceapi/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
Used in __init__.py, client.py, and async_client.py.
"""

__version__ = "1.11.0"
__version__ = "1.12.0"
SDK_VERSION = __version__
SDK_NAME = "oilpriceapi-python"
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
# Setuptools 77+ warns on the legacy license table, while the older backend
# available on supported Python 3.8 cannot parse the replacement string form.
requires = ["setuptools>=70.1,<77", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "oilpriceapi"
version = "1.11.0"
version = "1.12.0"
description = "Official Python SDK for source-timestamped OilPriceAPI energy data"
authors = [
{name = "OilPriceAPI", email = "support@oilpriceapi.com"}
Expand Down Expand Up @@ -108,6 +110,8 @@ include = '\.pyi?$'
[tool.ruff]
line-length = 100
target-version = "py38"

[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
Expand Down
Loading