From 94c8591e9be1c0fc64b02819bba4c131e59d977f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Nov 2025 01:24:08 +0000 Subject: [PATCH] feat: add modern GitHub Actions using ruff and PEP 517 tools Add modernized CI/CD actions with significant improvements: New Actions: - install-deps: Modern dependency installation (pyproject.toml support) - ruff-format: Fast code formatting (replaces black, 10-100x faster) - ruff-lint: Comprehensive linting (replaces pylint, flake8, isort) - run-tests: Streamlined pytest execution with coverage - version-bump: Semantic versioning with pyproject.toml support - build-dist: PEP 517 compliant builds (replaces setup.py) - pypi-upload: Modern PyPI publishing - git-commit: Simple git operations without dependencies - git-tag: Git tagging without external tools Benefits: - 10-100x faster linting and formatting with ruff - Modern PEP 517/518 compliant packaging - Simpler, more maintainable actions - Better pyproject.toml support - Reduced dependencies Added: - resources/ci-modern.yml: Complete modern CI workflow template - resources/MODERN_ACTIONS.md: Comprehensive documentation and migration guide These actions complement existing ones and provide a modern path forward for projects using current Python packaging standards. --- actions/build-dist/action.yml | 87 ++++++++++ actions/git-commit/action.yml | 89 ++++++++++ actions/git-tag/action.yml | 57 ++++++ actions/install-deps/action.yml | 109 ++++++++++++ actions/pypi-upload/action.yml | 63 +++++++ actions/ruff-format/action.yml | 47 +++++ actions/ruff-lint/action.yml | 69 ++++++++ actions/run-tests/action.yml | 101 +++++++++++ actions/version-bump/action.yml | 105 +++++++++++ resources/MODERN_ACTIONS.md | 296 ++++++++++++++++++++++++++++++++ resources/ci-modern.yml | 131 ++++++++++++++ 11 files changed, 1154 insertions(+) create mode 100644 actions/build-dist/action.yml create mode 100644 actions/git-commit/action.yml create mode 100644 actions/git-tag/action.yml create mode 100644 actions/install-deps/action.yml create mode 100644 actions/pypi-upload/action.yml create mode 100644 actions/ruff-format/action.yml create mode 100644 actions/ruff-lint/action.yml create mode 100644 actions/run-tests/action.yml create mode 100644 actions/version-bump/action.yml create mode 100644 resources/MODERN_ACTIONS.md create mode 100644 resources/ci-modern.yml diff --git a/actions/build-dist/action.yml b/actions/build-dist/action.yml new file mode 100644 index 0000000..4193ad7 --- /dev/null +++ b/actions/build-dist/action.yml @@ -0,0 +1,87 @@ +name: "Build Distribution" +description: "Build Python distribution packages using modern PEP 517 build tools" + +inputs: + build-backend: + description: "Build backend to use (defaults to auto-detect from pyproject.toml)" + required: false + output-dir: + description: "Output directory for built distributions" + required: false + default: "dist" + sdist: + description: "Build source distribution" + required: false + default: "true" + wheel: + description: "Build wheel distribution" + required: false + default: "true" + ssh-private-key: + description: "SSH private key for installing private dependencies" + required: false + +runs: + using: 'composite' + steps: + - name: Configure SSH + if: ${{ inputs.ssh-private-key }} + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ inputs.ssh-private-key }} + + - name: Install Build Tools + shell: bash + run: | + set -e + echo "Installing modern build tools" + python -m pip install --upgrade pip build + + - name: Install Dependencies + shell: bash + run: | + set -e + echo "::group::Installing project dependencies" + + # Try different dependency files + if [ -f "pyproject.toml" ]; then + echo "Installing from pyproject.toml" + python -m pip install -e . || echo "Could not install in editable mode, continuing..." + elif [ -f "setup.cfg" ]; then + echo "Installing from setup.cfg" + python -m pip install -e . || echo "Could not install in editable mode, continuing..." + elif [ -f "requirements.txt" ]; then + echo "Installing from requirements.txt" + python -m pip install -r requirements.txt + fi + + echo "::endgroup::" + + - name: Build Distributions + shell: bash + run: | + set -e + echo "::group::Building distributions" + + # Determine what to build + build_args="" + + if [ "${{ inputs.sdist }}" = "true" ] && [ "${{ inputs.wheel }}" = "false" ]; then + build_args="--sdist" + elif [ "${{ inputs.wheel }}" = "true" ] && [ "${{ inputs.sdist }}" = "false" ]; then + build_args="--wheel" + fi + # If both true or both not specified, build will create both by default + + # Set output directory + if [ -n "${{ inputs.output-dir }}" ]; then + build_args="$build_args --outdir ${{ inputs.output-dir }}" + fi + + echo "Running: python -m build $build_args" + python -m build $build_args + + echo "Built distributions:" + ls -lh ${{ inputs.output-dir }}/ + + echo "::endgroup::" diff --git a/actions/git-commit/action.yml b/actions/git-commit/action.yml new file mode 100644 index 0000000..641072a --- /dev/null +++ b/actions/git-commit/action.yml @@ -0,0 +1,89 @@ +name: "Git Commit" +description: "Commit and push changes to repository - modern, dependency-free" + +inputs: + commit-message: + description: "Commit message" + required: true + add-pattern: + description: "Pattern for files to add (defaults to all changes)" + required: false + default: "." + push: + description: "Push changes to remote after committing" + required: false + default: "true" + branch: + description: "Branch to push to (defaults to current branch)" + required: false + ssh-private-key: + description: "SSH private key for pushing to remote" + required: false + git-email: + description: "Git user email" + required: false + default: "github-actions[bot]@users.noreply.github.com" + git-username: + description: "Git user name" + required: false + default: "github-actions[bot]" + allow-empty: + description: "Allow empty commits" + required: false + default: "false" + +runs: + using: 'composite' + steps: + - name: Configure SSH + if: ${{ inputs.ssh-private-key }} + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ inputs.ssh-private-key }} + + - name: Configure Git + shell: bash + run: | + set -e + git config --global user.email "${{ inputs.git-email }}" + git config --global user.name "${{ inputs.git-username }}" + echo "Configured git as: ${{ inputs.git-username }} <${{ inputs.git-email }}>" + + - name: Commit Changes + shell: bash + run: | + set -e + + # Add files + echo "Adding files matching pattern: ${{ inputs.add-pattern }}" + git add ${{ inputs.add-pattern }} + + # Check if there are changes to commit + if git diff --staged --quiet; then + echo "No changes to commit" + if [ "${{ inputs.allow-empty }}" = "true" ]; then + echo "Creating empty commit (allow-empty=true)" + git commit --allow-empty -m "${{ inputs.commit-message }}" + else + echo "Skipping commit (no changes and allow-empty=false)" + exit 0 + fi + else + echo "Committing changes" + git commit -m "${{ inputs.commit-message }}" + fi + + - name: Push Changes + if: ${{ inputs.push == 'true' }} + shell: bash + run: | + set -e + + if [ -n "${{ inputs.branch }}" ]; then + branch="${{ inputs.branch }}" + else + branch=$(git rev-parse --abbrev-ref HEAD) + fi + + echo "Pushing to branch: $branch" + git push origin "$branch" diff --git a/actions/git-tag/action.yml b/actions/git-tag/action.yml new file mode 100644 index 0000000..93d7b03 --- /dev/null +++ b/actions/git-tag/action.yml @@ -0,0 +1,57 @@ +name: "Git Tag" +description: "Tag repository and push tag to remote - modern, dependency-free" + +inputs: + tag: + description: "Tag name (e.g., version number)" + required: true + message: + description: "Tag message (creates annotated tag if provided)" + required: false + push: + description: "Push tag to remote after creating" + required: false + default: "true" + force: + description: "Force create tag (overwrites existing tag)" + required: false + default: "false" + +runs: + using: 'composite' + steps: + - name: Create Tag + shell: bash + run: | + set -e + + tag_args="" + + if [ "${{ inputs.force }}" = "true" ]; then + tag_args="$tag_args -f" + fi + + if [ -n "${{ inputs.message }}" ]; then + echo "Creating annotated tag: ${{ inputs.tag }}" + git tag $tag_args -a "${{ inputs.tag }}" -m "${{ inputs.message }}" + else + echo "Creating lightweight tag: ${{ inputs.tag }}" + git tag $tag_args "${{ inputs.tag }}" + fi + + echo "Tag created: ${{ inputs.tag }}" + + - name: Push Tag + if: ${{ inputs.push == 'true' }} + shell: bash + run: | + set -e + + push_args="" + + if [ "${{ inputs.force }}" = "true" ]; then + push_args="$push_args --force" + fi + + echo "Pushing tag to remote: ${{ inputs.tag }}" + git push $push_args origin "${{ inputs.tag }}" diff --git a/actions/install-deps/action.yml b/actions/install-deps/action.yml new file mode 100644 index 0000000..9827065 --- /dev/null +++ b/actions/install-deps/action.yml @@ -0,0 +1,109 @@ +name: Install Dependencies +description: 'Modern dependency installation supporting pyproject.toml and requirements.txt' + +inputs: + pypi-packages: + description: 'Python packages to install (space-separated)' + required: false + apt-packages: + description: 'APT packages to install (space-separated)' + required: false + npm-packages: + description: 'NPM packages to install (space-separated)' + required: false + dependency-files: + description: 'Comma-separated paths to dependency files (requirements.txt, setup.cfg, pyproject.toml)' + required: false + extras: + description: 'Install extras from pyproject.toml (e.g., "dev,test")' + required: false + ssh-private-key: + description: 'SSH private key for installing private packages from source' + required: false + +runs: + using: 'composite' + steps: + - name: Configure SSH + if: ${{ inputs.ssh-private-key }} + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ inputs.ssh-private-key }} + + - name: Install PyPI Packages + if: ${{ inputs.pypi-packages }} + shell: bash + run: | + set -e + echo "::group::Pip Installation Log" + echo "Upgrading pip" + python -m pip install --upgrade pip + + echo "Installing packages: ${{ inputs.pypi-packages }}" + python -m pip install ${{ inputs.pypi-packages }} + + echo "Installed package versions:" + python -m pip list | grep -E "$(echo ${{ inputs.pypi-packages }} | tr ' ' '|')" || true + echo "::endgroup::" + + - name: Install APT Packages + if: ${{ inputs.apt-packages }} + shell: bash + run: | + set -e + echo "::group::APT Installation Log" + echo "Updating APT repositories" + sudo apt-get update + echo "Installing packages: ${{ inputs.apt-packages }}" + sudo apt-get install -y ${{ inputs.apt-packages }} + echo "::endgroup::" + + - name: Install NPM Packages + if: ${{ inputs.npm-packages }} + shell: bash + run: | + set -e + echo "::group::NPM Installation Log" + echo "Installing packages: ${{ inputs.npm-packages }}" + npm install -g ${{ inputs.npm-packages }} + echo "::endgroup::" + + - name: Install from Dependency Files + if: ${{ inputs.dependency-files }} + shell: bash + run: | + set -e + echo "::group::Dependencies Installation Log" + python -m pip install --upgrade pip + + IFS=',' read -ra paths <<< "${{ inputs.dependency-files }}" + for path in "${paths[@]}"; do + path=$(echo "$path" | xargs) # Trim whitespace + + if [[ $path == *.txt ]]; then + echo "Installing from requirements file: $path" + python -m pip install -r "$path" + + elif [[ $path == *.toml ]]; then + echo "Installing from pyproject.toml: $path" + if [ -n "${{ inputs.extras }}" ]; then + python -m pip install -e ".[${{ inputs.extras }}]" + else + python -m pip install -e . + fi + + elif [[ $path == *.cfg ]]; then + echo "Installing from setup.cfg: $path" + # For backward compatibility, use isee if available + if python -c "import isee" 2>/dev/null; then + isee install-requires + isee tests-require || true + else + python -m pip install -e . + fi + fi + done + + echo "Installed Python packages:" + python -m pip list + echo "::endgroup::" diff --git a/actions/pypi-upload/action.yml b/actions/pypi-upload/action.yml new file mode 100644 index 0000000..4ec18ab --- /dev/null +++ b/actions/pypi-upload/action.yml @@ -0,0 +1,63 @@ +name: "PyPI Upload" +description: "Upload distributions to PyPI using twine" + +inputs: + pypi-username: + description: "PyPI username (use __token__ for token-based auth)" + required: true + pypi-password: + description: "PyPI password or token" + required: true + repository-url: + description: "PyPI repository URL (defaults to pypi.org, use https://test.pypi.org/legacy/ for test)" + required: false + default: "https://upload.pypi.org/legacy/" + packages-path: + description: "Path to packages to upload (glob pattern)" + required: false + default: "dist/*" + skip-existing: + description: "Skip files that already exist on PyPI" + required: false + default: "false" + verbose: + description: "Verbose output" + required: false + default: "true" + +runs: + using: 'composite' + steps: + - name: Install Twine + shell: bash + run: | + set -e + echo "Installing twine" + python -m pip install --upgrade twine + + - name: Upload to PyPI + shell: bash + run: | + set -e + + twine_args="--non-interactive" + + if [ "${{ inputs.verbose }}" = "true" ]; then + twine_args="$twine_args --verbose" + fi + + if [ "${{ inputs.skip-existing }}" = "true" ]; then + twine_args="$twine_args --skip-existing" + fi + + echo "Uploading packages from: ${{ inputs.packages-path }}" + echo "Repository URL: ${{ inputs.repository-url }}" + + twine upload \ + $twine_args \ + --repository-url "${{ inputs.repository-url }}" \ + --username "${{ inputs.pypi-username }}" \ + --password "${{ inputs.pypi-password }}" \ + ${{ inputs.packages-path }} + + echo "Upload complete!" diff --git a/actions/ruff-format/action.yml b/actions/ruff-format/action.yml new file mode 100644 index 0000000..3d74dc4 --- /dev/null +++ b/actions/ruff-format/action.yml @@ -0,0 +1,47 @@ +name: "Ruff Format" +description: "Format source code using ruff - the modern, fast Python formatter" + +inputs: + line-length: + description: "Maximum line length" + required: false + default: "88" + target-path: + description: "Path to format (defaults to current directory)" + required: false + default: "." + check-only: + description: "Check formatting without making changes" + required: false + default: "false" + extra-args: + description: "Additional arguments to pass to ruff format" + required: false + +runs: + using: composite + steps: + - name: Install Ruff + shell: bash + run: | + set -e + echo "Installing ruff" + python -m pip install --upgrade ruff + + - name: Format Source Code + shell: bash + run: | + set -e + + args="--line-length=${{ inputs.line-length }}" + + if [ "${{ inputs.check-only }}" = "true" ]; then + args="$args --check --diff" + fi + + if [ -n "${{ inputs.extra-args }}" ]; then + args="$args ${{ inputs.extra-args }}" + fi + + echo "Running: ruff format $args ${{ inputs.target-path }}" + ruff format $args ${{ inputs.target-path }} diff --git a/actions/ruff-lint/action.yml b/actions/ruff-lint/action.yml new file mode 100644 index 0000000..3f2903c --- /dev/null +++ b/actions/ruff-lint/action.yml @@ -0,0 +1,69 @@ +name: "Ruff Lint" +description: "Lint and validate Python code using ruff - replaces pylint, flake8, and isort" + +inputs: + root-dir: + description: "Root directory to validate (defaults to current directory)" + required: false + default: "." + select: + description: "Comma-separated list of rule codes to enable (e.g., 'E,F,I')" + required: false + ignore: + description: "Comma-separated list of rule codes to ignore" + required: false + exclude: + description: "Comma-separated paths to exclude (e.g., 'tests,examples,scrap')" + required: false + fix: + description: "Automatically fix violations when possible" + required: false + default: "false" + output-format: + description: "Output format (text, json, github)" + required: false + default: "github" + extra-args: + description: "Additional arguments to pass to ruff check" + required: false + +runs: + using: 'composite' + steps: + - name: Install Ruff + shell: bash + run: | + set -e + echo "Installing ruff" + python -m pip install --upgrade ruff + + - name: Lint Source Code + shell: bash + run: | + set -e + + args="--output-format=${{ inputs.output-format }}" + + if [ "${{ inputs.fix }}" = "true" ]; then + args="$args --fix" + fi + + if [ -n "${{ inputs.select }}" ]; then + args="$args --select=${{ inputs.select }}" + fi + + if [ -n "${{ inputs.ignore }}" ]; then + args="$args --ignore=${{ inputs.ignore }}" + fi + + if [ -n "${{ inputs.exclude }}" ]; then + exclude_paths="${{ inputs.exclude }}" + args="$args --exclude=$exclude_paths" + fi + + if [ -n "${{ inputs.extra-args }}" ]; then + args="$args ${{ inputs.extra-args }}" + fi + + echo "Running: ruff check $args ${{ inputs.root-dir }}" + ruff check $args ${{ inputs.root-dir }} diff --git a/actions/run-tests/action.yml b/actions/run-tests/action.yml new file mode 100644 index 0000000..487031c --- /dev/null +++ b/actions/run-tests/action.yml @@ -0,0 +1,101 @@ +name: "Run Tests" +description: "Run pytest tests including doctests - modern and streamlined" + +inputs: + root-dir: + description: "Root directory containing tests (defaults to current directory)" + required: false + default: "." + exclude: + description: "Comma-separated paths to exclude (e.g., 'examples,scrap')" + required: false + extra-packages: + description: "Additional PyPI packages to install alongside pytest" + required: false + skip-doctests: + description: "Skip doctests (only run regular tests)" + required: false + default: "false" + coverage: + description: "Enable coverage reporting" + required: false + default: "false" + pytest-args: + description: "Additional arguments to pass to pytest" + required: false + default: "-v" + +runs: + using: 'composite' + steps: + - name: Install Pytest + shell: bash + run: | + set -e + packages="pytest" + + if [ "${{ inputs.coverage }}" = "true" ]; then + packages="$packages pytest-cov" + fi + + if [ -n "${{ inputs.extra-packages }}" ]; then + packages="$packages ${{ inputs.extra-packages }}" + fi + + echo "Installing: $packages" + python -m pip install $packages + + - name: Install Testing Extras + shell: bash + run: | + # Try to install testing extras from pyproject.toml or setup.cfg + if [ -f "pyproject.toml" ]; then + echo "Installing testing extras from pyproject.toml" + python -m pip install -e ".[testing]" 2>/dev/null || \ + python -m pip install -e ".[test]" 2>/dev/null || \ + echo "No testing extras found in pyproject.toml" + elif [ -f "setup.cfg" ]; then + echo "Attempting to install testing extras from setup.cfg" + python -c " + import sys + try: + from isee.pip_utils import install_extras + install_extras('testing') + except Exception as e: + print(f'Could not install testing extras: {e}') + " || true + fi + + - name: Run Tests + shell: bash + run: | + set -e + + pytest_args="${{ inputs.pytest-args }}" + + # Build ignore flags + if [ -n "${{ inputs.exclude }}" ]; then + IFS=',' read -ra exclude_paths <<< "${{ inputs.exclude }}" + for path in "${exclude_paths[@]}"; do + path=$(echo "$path" | xargs) # Trim whitespace + if [ -n "$path" ]; then + pytest_args="$pytest_args --ignore=${{ inputs.root-dir }}/$path" + fi + done + fi + + # Add coverage if requested + if [ "${{ inputs.coverage }}" = "true" ]; then + pytest_args="$pytest_args --cov=${{ inputs.root-dir }} --cov-report=term-missing" + fi + + # Run with or without doctests + if [ "${{ inputs.skip-doctests }}" = "true" ]; then + echo "Running: pytest $pytest_args ${{ inputs.root-dir }}" + pytest $pytest_args "${{ inputs.root-dir }}" + else + echo "Running: pytest $pytest_args --doctest-modules ${{ inputs.root-dir }}" + pytest $pytest_args --doctest-modules \ + -o doctest_optionflags="ELLIPSIS IGNORE_EXCEPTION_DETAIL" \ + "${{ inputs.root-dir }}" + fi diff --git a/actions/version-bump/action.yml b/actions/version-bump/action.yml new file mode 100644 index 0000000..ee1c92c --- /dev/null +++ b/actions/version-bump/action.yml @@ -0,0 +1,105 @@ +name: "Version Bump" +description: "Generate semantic version and update pyproject.toml or setup.cfg" + +inputs: + config-file: + description: "Config file to update (auto-detects pyproject.toml or setup.cfg if not specified)" + required: false + +outputs: + version: + description: "The generated version number" + value: ${{ steps.gen_version.outputs.version }} + +runs: + using: 'composite' + steps: + - name: Install Dependencies + shell: bash + run: | + set -e + python -m pip install --upgrade pip + + # Install isee for version generation + if [ "${{ github.repository }}" = "i2mint/isee" ]; then + echo "Installing isee from source" + python -m pip install -e . + else + python -m pip install isee + fi + + # Install toml tools for Python < 3.11 or for writing + python -m pip install tomli tomli-w 2>/dev/null || true + + - name: Generate Version Number + shell: bash + id: gen_version + run: | + set -e + + # Generate the new version + VERSION=$(isee gen-semver --output-mode=print) + + # Validate the version string + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid version format: $VERSION" + exit 1 + fi + + # Export to GitHub environment and output + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_OUTPUT + + echo "Generated version: $VERSION" + + - name: Update Configuration File + shell: bash + run: | + set -e + VERSION="${{ steps.gen_version.outputs.version }}" + + # Determine which config file to update + if [ -n "${{ inputs.config-file }}" ]; then + config_file="${{ inputs.config-file }}" + elif [ -f "pyproject.toml" ]; then + config_file="pyproject.toml" + elif [ -f "setup.cfg" ]; then + config_file="setup.cfg" + else + echo "No configuration file found (pyproject.toml or setup.cfg)" + exit 1 + fi + + echo "Updating $config_file with version: $VERSION" + + # Update the appropriate file + if [[ "$config_file" == *.toml ]]; then + python - <=1.20", +] + +[project.optional-dependencies] +dev = ["ruff", "pytest"] +test = ["pytest", "pytest-cov"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.ruff] +line-length = 88 +select = ["E", "F", "I"] +exclude = ["tests", "examples"] +``` + +### Backward Compatible: setup.cfg + +Still supported for existing projects: + +```ini +[metadata] +name = my-package +version = 0.1.0 + +[options] +install_requires = + numpy>=1.20 + +[options.extras_require] +testing = + pytest + pytest-cov +``` + +## Performance Comparison + +| Tool | Lines/sec | Relative Speed | +|------|-----------|----------------| +| ruff | ~50,000 | 10-100x faster | +| black | ~5,000 | baseline | +| pylint | ~2,000 | 25x slower | + +## Key Benefits + +1. **Speed**: Ruff is 10-100x faster than black/pylint +2. **Simplicity**: Fewer dependencies and tools +3. **Modern**: PEP 517/518 compliant +4. **Maintainable**: Clear, focused actions +5. **Standard**: Uses official Python packaging tools + +## Questions? + +See the main [isee README](../README.md) for general CI documentation. diff --git a/resources/ci-modern.yml b/resources/ci-modern.yml new file mode 100644 index 0000000..e44c7ec --- /dev/null +++ b/resources/ci-modern.yml @@ -0,0 +1,131 @@ +name: Continuous Integration (Modern) +on: [push, pull_request] + +env: + PROJECT_NAME: #PROJECT_NAME# + +jobs: + validation: + name: Validation + if: "!contains(github.event.head_commit.message, '[skip ci]')" + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.12"] + + 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 Dependencies + uses: i2mint/isee/actions/install-deps@master + with: + dependency-files: pyproject.toml + extras: dev,test + # Fallback for projects still using setup.cfg: + # dependency-files: setup.cfg + # ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} # Uncomment for private dependencies + + - name: Format Source Code + uses: i2mint/isee/actions/ruff-format@master + with: + line-length: 88 + target-path: . + + - name: Lint Validation + uses: i2mint/isee/actions/ruff-lint@master + with: + root-dir: ${{ env.PROJECT_NAME }} + select: E,F,I,N,W # Error, pyflakes, import, naming, warnings + exclude: tests,examples,scrap + output-format: github + + - name: Run Tests + uses: i2mint/isee/actions/run-tests@master + with: + root-dir: ${{ env.PROJECT_NAME }} + exclude: examples,scrap + coverage: true + pytest-args: -v --tb=short + + publish: + name: Publish + if: "!contains(github.event.head_commit.message, '[skip ci]') && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')" + needs: validation + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.12"] + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Format Source Code + uses: i2mint/isee/actions/ruff-format@master + + - name: Update Version Number + id: version + uses: i2mint/isee/actions/version-bump@master + # Specify config file if needed: + # with: + # config-file: pyproject.toml + + - name: Build Distribution + uses: i2mint/isee/actions/build-dist@master + with: + sdist: true + wheel: true + # Uncomment for private dependencies: + # with: + # ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Publish to PyPI + uses: i2mint/isee/actions/pypi-upload@master + with: + pypi-username: ${{ secrets.PYPI_USERNAME }} + pypi-password: ${{ secrets.PYPI_PASSWORD }} + skip-existing: false + + - name: Commit Changes + uses: i2mint/isee/actions/git-commit@master + with: + commit-message: "**CI** Formatted code + Updated version to ${{ steps.version.outputs.version }} [skip ci]" + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + push: true + + - name: Tag Repository + uses: i2mint/isee/actions/git-tag@master + with: + tag: ${{ steps.version.outputs.version }} + message: "Release version ${{ steps.version.outputs.version }}" + push: true + + github-pages: + name: Publish GitHub Pages + + permissions: + contents: write + pages: write + id-token: write + + if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)" + needs: publish + runs-on: ubuntu-latest + + steps: + - uses: i2mint/epythet/actions/publish-github-pages@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + ignore: "tests/,scrap/,examples/"