diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000000..84f601adc4 --- /dev/null +++ b/.cruft.json @@ -0,0 +1,23 @@ +{ + "template": "https://github.com/viseshrp/yapc", + "commit": "b123cc504566db88232428f02c8c72f913433f9b", + "checkout": null, + "context": { + "cookiecutter": { + "author": "Visesh Prasad", + "email": "viseshrprasad@gmail.com", + "github_username": "viseshrp", + "pypi_username": "viseshrp", + "project_name": "pydynamicreporting", + "project_slug": "pydynamicreporting", + "project_description": "This is a template repository for Python projects that use uv for their dependency management.", + "cli_tool": "n", + "codecov": "y", + "git_init": "n", + "github_actions": "y", + "_template": "https://github.com/viseshrp/yapc", + "_commit": "b123cc504566db88232428f02c8c72f913433f9b" + } + }, + "directory": null +} diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 6b102116c8..0000000000 --- a/.flake8 +++ /dev/null @@ -1,44 +0,0 @@ -[flake8] -exclude = venv, __init__.py, doc/_build -select = B,B9,C,D,DAR,E,F,N,RST,S,W -count = True -max-complexity = 10 -max-line-length = 100 -statistics = True -ignore = - # Whitespace before ':'. - E203, - # Whitespace after ':'. - E231, - # Line too long (82 > 79 characters). - E501, - # Line break occurred before a binary operator. - W503, - # Invalid escape sequence - W605, - # undefined name name - F821, - # Function is too complex - C901, - # https://docs.python.org/3/tutorial/errors.html#exception-chaining for details - B904, - # camelcase imported as lowercase - N813 - # Data class should either be immutable or use __slots__ to save memory - B903 - # class name should use CapWords convention - N801 - # function name should be lowercase - N802 - # exception name should be named with an Error suffix - N818 - # line too long - B950 - # Module imported but unused - F401 - # manually surrounded by quotes, consider using the `!r` conversion flag - B907 - # zip() without an explicit strict= parameter set. - B905 - # do not compare types, for exact checks use `is` / `is not` - E721 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..62e6b26a06 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,16 @@ +# Always use LF line endings in the repository, normalize line endings +* text=auto eol=lf + +# Explicitly set for Python files +*.py text eol=lf + +# Explicitly set for Markdown and YAML +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf + +# Set for TOML +*.toml text eol=lf + +# Set for GitHub Actions +.github/** text eol=lf diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..8d7b0b1de2 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +# Pull Request + +Fixes # + +## Proposed Changes + +- +- +- diff --git a/.github/actions/setup-python-env/action.yml b/.github/actions/setup-python-env/action.yml new file mode 100644 index 0000000000..13b8e733ea --- /dev/null +++ b/.github/actions/setup-python-env/action.yml @@ -0,0 +1,52 @@ +name: "Setup Python Environment" +description: "Set up Python environment for the given Python version" + +inputs: + python-version: + description: "Python version to use" + required: false + default: "3.13" + uv-version: + description: "uv version to use" + required: false + default: "latest" + +runs: + using: "composite" + steps: + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + version: ${{ inputs.uv-version }} + enable-cache: true + cache-suffix: ${{ inputs.python-version }} + + - name: Cache .venv directory + uses: actions/cache@v4 + with: + path: .venv + key: uv-venv-${{ inputs.python-version }}-${{ hashFiles('pyproject.toml', 'uv.lock') }} + restore-keys: | + uv-venv-${{ inputs.python-version }}- + + - name: Disable pip version check (optional) + run: echo "PIP_DISABLE_PIP_VERSION_CHECK=1" >> $GITHUB_ENV + shell: bash + + - name: Install Python dependencies + run: make install + shell: bash + + - name: Add .venv to PATH (cross-platform) + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + echo "$(pwd)/.venv/Scripts" >> $GITHUB_PATH + else + echo "$(pwd)/.venv/bin" >> $GITHUB_PATH + fi + shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d15adba99f..c4fdf0f3d6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,23 @@ version: 2 updates: - - package-ecosystem: "pip" # See documentation for possible values - directory: "/" # Location of package manifests + # 1. Python dependencies from pyproject.toml + - package-ecosystem: "pip" + directory: "/" schedule: interval: "daily" + open-pull-requests-limit: 1 + labels: + - "dependencies" + - "automerge" + # 2. GitHub-hosted things: + # - GitHub Actions in .github/workflows/ + # - pre-commit hook revs in .pre-commit-config.yaml - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" + open-pull-requests-limit: 1 + labels: + - "dependencies" + - "automerge" diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000000..06f78600bd --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,44 @@ +name: Automerge + +on: + workflow_dispatch: + pull_request: + types: + - labeled + - synchronize + - opened + - reopened + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + automerge: + if: contains(github.event.pull_request.labels.*.name, 'automerge') + runs-on: ubuntu-latest + steps: + - name: Debug PR info + run: | + echo "PR author: ${{ github.event.pull_request.user.login }}" + echo "Labels: ${{ toJson(github.event.pull_request.labels) }}" + + - name: Auto-approve + if: | + github.event.pull_request.user.login == 'github-actions[bot]' || + github.event.pull_request.user.login == 'dependabot[bot]' || + github.event.pull_request.user.login == 'viseshrp' + uses: hmarr/auto-approve-action@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + pull-request-number: ${{ github.event.pull_request.number }} + + - name: Enable auto-merge + uses: peter-evans/enable-pull-request-automerge@v3 + env: + GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + with: + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + merge-method: squash + pull-request-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..cb7aa6a4c5 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,48 @@ +name: "CodeQL Python" + +on: + workflow_dispatch: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + # Every night at 11:00 PM Eastern / 04:00 AM UTC + - cron: '0 4 * * *' + +jobs: + analyze: + name: CodeQL Python Analysis + runs-on: ubuntu-latest + + permissions: + # Required to upload code scanning results + security-events: write + + # Only needed if you pull in private/internal packs + packages: read + + # Required to read your repo’s contents + actions: read + contents: read + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + Environment + uses: ./.github/actions/setup-python-env + + - name: Initialize CodeQL for Python + uses: github/codeql-action/init@v3 + with: + languages: python + # Python is interpreted; no build step required + build-mode: none + queries: +security-extended,security-and-quality + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + # Tag results clearly for Python + category: "/language:python" diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml new file mode 100644 index 0000000000..9eb69e1681 --- /dev/null +++ b/.github/workflows/create-draft-release.yml @@ -0,0 +1,52 @@ +name: Create Draft Release + +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + draft-release: + runs-on: ubuntu-latest + permissions: + contents: write # needed to create a release & upload artifacts + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Set up Python Env + uses: ./.github/actions/setup-python-env + + - name: Run version checks + run: make check-version + + - name: Build package + run: make build + + - name: Check for existing release + run: | + if gh release view "${{ github.ref_name }}" > /dev/null 2>&1; then + echo "❌ Release for tag '${{ github.ref_name }}' already exists." + exit 1 + fi + env: + GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + + - name: Create Draft GitHub Release with Artifacts + run: | + gh release create "${{ github.ref_name }}" \ + --title "Release ${GITHUB_REF_NAME}" \ + --notes "See [CHANGELOG.md](./CHANGELOG.md)" \ + --draft \ + dist/* + env: + GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml new file mode 100644 index 0000000000..6071d8ae22 --- /dev/null +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -0,0 +1,71 @@ +name: Pre-commit Autoupdate + +on: + schedule: + - cron: '0 4 * * *' # Daily at 4 AM UTC / 11 PM Eastern + workflow_dispatch: + +permissions: + pull-requests: write + contents: write + issues: write # Needed to create labels + +jobs: + autoupdate: + if: startsWith(github.ref, 'refs/heads/main') # Ensure we only run from main + name: Autoupdate Pre-commit Hooks + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: main + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + fetch-depth: 0 + + - name: Cache pre-commit hooks + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Set up Python + Environment + uses: ./.github/actions/setup-python-env + + - name: Run pre-commit autoupdate + run: uv run pre-commit autoupdate + continue-on-error: true + + - name: Run pre-commit checks + run: make check + + - name: Show updated hooks (if any) + run: git diff .pre-commit-config.yaml || true + + - name: Commit changes + run: | + git add .pre-commit-config.yaml + git commit -m "chore(pre-commit): Update pre-commit hooks" || echo "No changes to commit" + + - name: Ensure labels exist + run: | + gh label create dependencies --description "Dependency updates" --color FFCD00 || echo "Label 'dependencies' already exists" + gh label create chore --description "Chores and maintenance" --color 00CED1 || echo "Label 'chore' already exists" + gh label create automerge --description "automerge" --color 3E0651 || echo "Label 'automerge' already exists" + env: + GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + + - name: Create Pull Request for Updated Hooks + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + base: main + branch: chore/pre-commit-update + title: "chore(pre-commit): Update pre-commit hooks" + commit-message: "chore(pre-commit): Update pre-commit hooks" + body: | + # Update pre-commit hooks + + - This PR updates the versions of pre-commit hooks to their latest releases. + labels: dependencies, chore, automerge + delete-branch: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..9b6d321160 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,43 @@ +name: Release + +on: + workflow_dispatch: + release: + types: [ published ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: read # just checkout code + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for hatch-vcs to generate full version + fetch-tags: true + + - name: Set up Python Env + uses: ./.github/actions/setup-python-env + + - name: Run version checks + run: make check-version + + - name: Build project + run: make build + + - name: Check dist + run: make check-dist + + - name: Publish package to PyPI + run: make publish + env: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/validate-codecov-config.yml b/.github/workflows/validate-codecov-config.yml new file mode 100644 index 0000000000..62abe43f30 --- /dev/null +++ b/.github/workflows/validate-codecov-config.yml @@ -0,0 +1,16 @@ +name: validate-codecov-config + +on: + workflow_dispatch: + pull_request: + paths: [ codecov.yml ] + push: + branches: [ main ] + +jobs: + validate-codecov-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Validate codecov configuration + run: curl -sSL --fail-with-body --data-binary @codecov.yml https://codecov.io/validate diff --git a/.github/workflows/vhs.yml b/.github/workflows/vhs.yml new file mode 100644 index 0000000000..346d4c8f8e --- /dev/null +++ b/.github/workflows/vhs.yml @@ -0,0 +1,60 @@ +name: Auto-update demo.gif + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - demo.tape + - .github/workflows/vhs.yml + +permissions: + contents: write + pull-requests: write + +jobs: + vhs: + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + + - name: Set Git identity + run: | + git config user.name "vhs-action πŸ“Ό" + git config user.email "actions@github.com" + + - name: Set up Python + virtual environment + uses: ./.github/actions/setup-python-env + + - name: Install VHS + run: brew install vhs + + - name: Generate demo.gif + run: vhs demo.tape + + - name: Ensure labels exist + run: | + gh label create chore --description "Chores and maintenance" --color 00CED1 || echo "Label 'chore' already exists" + gh label create automerge --description "automerge" --color 3E0651 || echo "Label 'automerge' already exists" + env: + GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + + - name: Create PR for updated demo.gif + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + base: main + branch: chore/demo-gif-update + title: "chore(demo): update demo.gif" + commit-message: "chore(demo): update demo.gif" + body: | + This PR updates the autogenerated `demo.gif` based on the latest `demo.tape`. + + - Regenerated using [VHS](https://github.com/charmbracelet/vhs) + labels: automerge, chore + delete-branch: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..cb161290a6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/), +and this project adheres to [Semantic Versioning](https://semver.org/). + +## [0.0.2] - + +### Added + +- + +### Changed + +- + +### Deprecated + +- + +### Removed + +- + +### Fixed + +- + +## Security + +- + +## [0.0.1] - + +### Added + +- Stub release diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index 0c4df936c3..0000000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,9 +0,0 @@ -======= -History -======= - - -0.1.0 (01-01-1970) ------------------- - -* First release on PyPI. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..e03f98f468 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,133 @@ +# Contributing to `pydynamicreporting` + +We absolutely welcome any code contributions and we hope that this +guide will facilitate an understanding of the ``pydynamicreporting`` code +repository. It is important to note that while the ``pydynamicreporting`` +software package is maintained by ANSYS and any submissions will be +reviewed thoroughly before merging, we still seek to foster a community +that can support user questions and develop new features to make this +software a useful tool for all users. As such, we welcome and encourage +any questions or submissions to this repository. + +Please reference the `PyAnsys Developer's Guide `_ for the full documentation +regarding contributing to the ``pydynamicreporting`` project. + +You can contribute in many ways: + +## Types of Contributions + +### Report Bugs + +Report bugs at + +If you are reporting a bug, please include: + +- Your operating system name and version. +- Any details about your local setup that might be helpful in troubleshooting. +- Detailed steps to reproduce the bug. + +### Fix Bugs + +Look through the GitHub issues for bugs. +Anything tagged with "bug" and "help wanted" is open to whoever wants to +implement a fix for it. + +### Implement Features + +Look through the GitHub issues for features. +Anything tagged with "enhancement" and "help wanted" is open to whoever +wants to implement it. + +### Write Documentation + +`pydynamicreporting` could always use more documentation, whether as part of the official docs, +in docstrings, or even on the web in blog posts, articles, and such. + +### Submit Feedback + +The best way to send feedback is to file an issue at +. + +If you are proposing a new feature: + +- Explain in detail how it would work. +- Keep the scope as narrow as possible, to make it easier to implement. +- Remember that this is a volunteer-driven project, and that + contributions are welcome :) + +## Get Started + +Ready to contribute? Here's how to set up `pydynamicreporting` for local development. +Please note this documentation assumes you already have `uv` and `Git` installed. + +1. Fork the `pydynamicreporting` repo on GitHub. + +2. Clone your fork locally: + + ```bash + cd + git clone git@github.com:YOUR_NAME/pydynamicreporting.git + ``` + +3. Navigate into the project folder: + + ```bash + cd pydynamicreporting + ``` + +4. Install and activate the environment: + + ```bash + uv sync + ``` + +5. Install pre-commit to run linters/formatters at commit time: + + ```bash + uv run pre-commit install + ``` + +6. Create a branch for local development: + + ```bash + git checkout -b name-of-your-bugfix-or-feature + ``` + +7. Add test cases for your changes in the `tests` directory. + +8. Check formatting and style: + + ```bash + make check + ``` + +9. Run unit tests: + + ```bash + make test + ``` + +10. (Optional) Run `tox` to test against multiple Python versions: + + ```bash + tox + ``` + +11. Commit your changes and push your branch: + + ```bash + git add . + git commit -m "Your detailed description of your changes." + git push origin name-of-your-bugfix-or-feature + ``` + +12. Submit a pull request through the GitHub website. + +## Pull Request Guidelines + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. + +2. If the pull request adds functionality, update the documentation. + Add a docstring, and update the feature list in `README.md`. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 404a052483..0000000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,14 +0,0 @@ -Contributing -############ - -We absolutely welcome any code contributions and we hope that this -guide will facilitate an understanding of the ``pydynamicreporting`` code -repository. It is important to note that while the ``pydynamicreporting`` -software package is maintained by ANSYS and any submissions will be -reviewed thoroughly before merging, we still seek to foster a community -that can support user questions and develop new features to make this -software a useful tool for all users. As such, we welcome and encourage -any questions or submissions to this repository. - -Please reference the `PyAnsys Developer's Guide `_ for the full documentation -regarding contributing to the ``pydynamicreporting`` project. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000..f3a88e7a59 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE +include README.md +include CHANGELOG.md + +recursive-include tests * diff --git a/Makefile b/Makefile index ac2b21f476..8d245f3c1d 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ install-dev: pip install -e .[dev] pull-docker: - bash .ci/pull_adr_image.sh + bash scripts/pull_adr_image.sh test: pip install -e .[test] diff --git a/codecov.yml b/codecov.yml index a793f59195..6f602d7b89 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,8 +1,9 @@ coverage: + range: 70..100 + round: down + precision: 1 status: project: default: - informational: true - patch: - default: - informational: true \ No newline at end of file + target: 85% + threshold: 0.5% diff --git a/codespell.txt b/codespell.txt new file mode 100644 index 0000000000..f19ae3ec82 --- /dev/null +++ b/codespell.txt @@ -0,0 +1 @@ +pydynamicreporting diff --git a/demo.tape b/demo.tape new file mode 100644 index 0000000000..89d8636264 --- /dev/null +++ b/demo.tape @@ -0,0 +1,44 @@ +Set Shell bash +Set FontSize 18 +Set Width 800 +Set Height 600 +Output demo.gif + +Hide Sleep 2s Show + +Hide +Type "clear" +Enter +Show + + +# Python library demo +Type "python" +Sleep 3s +Enter + +Type "import pydynamicreporting" +Sleep 2s +Enter +# Pause for dramatic effect... +Sleep 4s + +Type "help(pydynamicreporting)" +Sleep 2s +Enter +Sleep 6s + +Type "pydynamicreporting.__version__" +Sleep 2s +Enter +Sleep 4s + +Type "exit()" +Sleep 2s +Enter +Sleep 4s + +Hide +Type "clear" +Enter +Show diff --git a/scripts/check_changelog_date.py b/scripts/check_changelog_date.py new file mode 100644 index 0000000000..254c8d8f46 --- /dev/null +++ b/scripts/check_changelog_date.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +Check release date for a given version in CHANGELOG.md. +""" + +from datetime import date +from pathlib import Path +import sys + +CHANGELOG_PATH = Path("CHANGELOG.md") + + +def main(version: str) -> None: + if not CHANGELOG_PATH.exists(): + print(f"❌ ERROR: {CHANGELOG_PATH} does not exist.") + sys.exit(1) + + today = date.today().isoformat() + target_line = f"## [{version}] - {today}" + + found = False + lines = CHANGELOG_PATH.read_text(encoding="utf-8").splitlines() + + for i, line in enumerate(lines): + if line.strip() == target_line: + print(f"πŸ” Found line {i + 1}: {target_line}") + found = True + break + + if not found: + print("❌ ERROR: CHANGELOG.md is not ready for release.") + print(f" Expected line: {target_line}") + print("Tip: Check if it's still marked as '[Unreleased]' and update it to today's date.") + sys.exit(1) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: uv run python scripts/check_changelog_date.py ") + sys.exit(1) + + main(sys.argv[1]) diff --git a/.ci/pull_adr_image.sh b/scripts/pull_adr_image.sh similarity index 100% rename from .ci/pull_adr_image.sh rename to scripts/pull_adr_image.sh diff --git a/scripts/tag_release.sh b/scripts/tag_release.sh new file mode 100644 index 0000000000..182ae9285a --- /dev/null +++ b/scripts/tag_release.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +VERSION=$(hatch version | sed 's/\.dev.*//') +echo "🏷 Releasing version: $VERSION" + +# Update changelog date for this version +uv run python scripts/check_changelog_date.py "$VERSION" + +# Assert we're on main and clean +branch=$(git rev-parse --abbrev-ref HEAD) +if [[ "$branch" != "main" ]]; then + echo "❌ ERROR: Must be on main branch to tag a release." + exit 1 +fi + +if ! git diff --quiet HEAD; then + echo "❌ ERROR: Working directory is dirty. Commit your changes through a PR." + exit 1 +fi + +# Create and push tag only +git tag "v$VERSION" -m "Release v$VERSION" +git push origin "v$VERSION" +echo "βœ… Tag v$VERSION pushed successfully." diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000000..b69c8aed5a --- /dev/null +++ b/tox.ini @@ -0,0 +1,19 @@ +[tox] +min_version = 4.0 +env_list = py39, py310, py311, py312, py313 +skipsdist = true + +[gh-actions] +python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + 3.13: py313 + +[testenv] +pass_env = PYTHON_VERSION +allowlist_externals = uv +commands = + uv sync --python {envpython} + uv run python -m pytest tests --cov --cov-config=pyproject.toml --cov-report=xml:coverage.xml