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
Binary file added .coverage
Binary file not shown.
63 changes: 63 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Publish to PyPI

on:
release:
types: [published]
workflow_dispatch:
inputs:
skip_tests:
description: 'Skip test job (use with caution)'
required: false
type: boolean
default: false

jobs:
test:
if: ${{ !inputs.skip_tests }}
uses: ./.github/workflows/test.yml

build:
needs: test
if: ${{ always() && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.12

- name: Build package
run: |
uv build

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/chuk-math-gym
permissions:
id-token: write

steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
verbose: true
81 changes: 81 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Create Release

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to create release for (e.g., v1.0.0)'
required: true
type: string

jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get version from tag
id: get_version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG=${{ inputs.tag }}
else
TAG=${GITHUB_REF#refs/tags/}
fi
VERSION=${TAG#v}
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "version=${VERSION}" >> $GITHUB_OUTPUT

- name: Verify version matches pyproject.toml
run: |
PYPROJECT_VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)
if [ "${{ steps.get_version.outputs.version }}" != "${PYPROJECT_VERSION}" ]; then
echo "Error: Tag version (${{ steps.get_version.outputs.version }}) does not match pyproject.toml version (${PYPROJECT_VERSION})"
exit 1
fi

- name: Get previous tag
id: get_previous_tag
run: |
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "${{ steps.get_version.outputs.tag }}" | tail -1)
if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" = "${{ steps.get_version.outputs.tag }}" ]; then
# If no previous tag, use first commit
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
fi
echo "previous_tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT

- name: Generate changelog
id: changelog
run: |
echo "## What's Changed" > RELEASE_NOTES.md
echo "" >> RELEASE_NOTES.md

# Get commits since last tag
git log ${{ steps.get_previous_tag.outputs.previous_tag }}..${{ steps.get_version.outputs.tag }} \
--pretty=format:"* %s (%h)" \
--no-merges >> RELEASE_NOTES.md

echo "" >> RELEASE_NOTES.md
echo "" >> RELEASE_NOTES.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.get_previous_tag.outputs.previous_tag }}...${{ steps.get_version.outputs.tag }}" >> RELEASE_NOTES.md

cat RELEASE_NOTES.md

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.get_version.outputs.tag }}
name: Release ${{ steps.get_version.outputs.tag }}
body_path: RELEASE_NOTES.md
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70 changes: 70 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Test

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

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}

- name: Create virtual environment and install dependencies
run: |
uv venv
uv pip install -e ".[dev]"

- name: Run linting
run: |
uv run ruff check src tests
uv run ruff format --check src tests

- name: Run type checking
run: |
uv run mypy src || echo "Type check found issues (non-blocking)"

- name: Run tests with coverage
run: |
uv run pytest tests/ -v --cov=src/chuk_math_gym --cov-report=term --cov-report=xml --cov-report=html

- name: Upload coverage reports
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false

- name: Check coverage threshold
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
run: |
uv run python -c "
import xml.etree.ElementTree as ET
tree = ET.parse('coverage.xml')
root = tree.getroot()
coverage = float(root.attrib['line-rate']) * 100
print(f'Coverage: {coverage:.2f}%')
if coverage < 70:
print(f'Coverage {coverage:.2f}% is below 70% threshold')
exit(1)
"
Loading
Loading