npdatetime 0.2.4

Astronomical calculator for Bikram Sambat calendar based on solar and lunar positions. High-performance Nepali (Bikram Sambat) datetime library with multi-language bindings
Documentation
name: Publish All Packages

on:
   push:
      tags:
         - "v*.*.*" # Triggers on version tags like v0.1.0, v1.2.3
   workflow_dispatch: # Allows manual triggering

env:
   CARGO_TERM_COLOR: always
   PYO3_USE_ABI3_FORWARD_COMPATIBILITY: 1

permissions:
   contents: write
   id-token: write # Required for OIDC publishing if used

jobs:
   # Job 0: Sync version and push back to master
   sync-version-to-main:
      name: 🔄 Sync Version to Master
      runs-on: ubuntu-latest
      if: startsWith(github.ref, 'refs/tags/v')
      steps:
         - name: Checkout code
           uses: actions/checkout@v4
           with:
              ref: master
              token: ${{ secrets.PAT_TOKEN }}

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

         - name: Install Rust toolchain
           uses: dtolnay/rust-toolchain@stable

         - name: Install wasm-pack
           uses: jetli/wasm-pack-action@v0.4.0
           with:
              version: 'latest'

         - name: Setup Node.js
           uses: actions/setup-node@v4
           with:
              node-version: "20"

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Update version in files
           run: python update_version.py ${{ steps.version.outputs.VERSION }}

         - name: Build WASM and assets
           run: |
              cd bindings/javascript
              wasm-pack build --target web --release --out-name npdatetime_wasm --scope 4mritgiri
              npm pkg --prefix pkg set name=@4mritgiri/npdatetime
              cd ../django
              python build_assets.py

         - name: Commit and push changes
           run: |
              git config --global user.name "github-actions[bot]"
              git config --global user.email "github-actions[bot]@users.noreply.github.com"
              git add .
              git commit -m "chore: bump version to ${{ steps.version.outputs.VERSION }} and build assets [skip ci]" || echo "No changes to commit"
              git push origin master
           env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

   # Job 1: Publish Rust Crate to crates.io
   publish-rust:
      name: 📦 Publish Rust Crate
      needs: sync-version-to-main
      runs-on: ubuntu-latest
      steps:
         - name: Checkout code
           uses: actions/checkout@v4

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

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Update version in files
           run: python update_version.py ${{ steps.version.outputs.VERSION }}

         - name: Install Rust toolchain
           uses: dtolnay/rust-toolchain@stable

         - name: Cache cargo registry
           uses: actions/cache@v4
           with:
              path: ~/.cargo/registry
              key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

         - name: Cache cargo index
           uses: actions/cache@v4
           with:
              path: ~/.cargo/git
              key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

         - name: Run tests
           run: cargo test --all-features

         - name: Build release
           run: cargo build --release

         - name: Publish to crates.io
           run: cargo publish --allow-dirty
           env:
             CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }}

   # Job 2: Build and Publish WASM to NPM
   publish-wasm:
      name: 📦 Publish WASM/JavaScript to NPM
      needs: sync-version-to-main
      runs-on: ubuntu-latest
      steps:
         - name: Checkout code
           uses: actions/checkout@v4

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

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Update version in files
           run: python update_version.py ${{ steps.version.outputs.VERSION }}

         - name: Install Rust toolchain
           uses: dtolnay/rust-toolchain@stable

         - name: Install wasm-pack
           uses: jetli/wasm-pack-action@v0.4.0
           with:
              version: 'latest'

         - name: Setup Node.js
           uses: actions/setup-node@v4
           with:
              node-version: "20"
              registry-url: "https://registry.npmjs.org"

         - name: Build WASM package
           run: |
              cd bindings/javascript
              wasm-pack build --target web --release --out-name npdatetime_wasm --scope 4mritgiri
              npm pkg --prefix pkg set name=@4mritgiri/npdatetime
 
         - name: Publish to NPM
           run: |
              cd bindings/javascript
              npm publish --access public || echo "Publish failed, might already exist or auth issue"
           env:
              NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

         - name: Upload WASM package artifact
           uses: actions/upload-artifact@v4
           with:
              name: wasm-pkg
              path: bindings/javascript/pkg/

   # Job 3: Publish Python Package to PyPI
   publish-python:
      name: 📦 Publish Python Package
      needs: sync-version-to-main
      runs-on: ${{ matrix.os }}
      strategy:
         matrix:
            os: [ubuntu-latest, macos-latest, windows-latest]
            python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
      steps:
         - name: Checkout code
           uses: actions/checkout@v4

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

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Update version in files
           run: python update_version.py ${{ steps.version.outputs.VERSION }}

         - name: Install Rust toolchain
           uses: dtolnay/rust-toolchain@stable

         - name: Install maturin
           run: pip install maturin

         - name: Build Python wheels
           run: |
              cd bindings/python
              maturin build --release --out dist --interpreter python${{ matrix.python-version }}

         - name: Upload wheels
           uses: actions/upload-artifact@v4
           with:
              name: wheels-${{ matrix.os }}-${{ matrix.python-version }}
              path: bindings/python/dist/*.whl

   publish-python-wheels:
      name: 📦 Publish Python Wheels to PyPI
      needs: publish-python
      runs-on: ubuntu-latest
      steps:
         - name: Download wheels
           uses: actions/download-artifact@v4
           with:
              pattern: wheels-*
              path: dist
              merge-multiple: true

         - name: Publish to PyPI
           uses: pypa/gh-action-pypi-publish@release/v1
           with:
              packages-dir: dist
              password: ${{ secrets.PYPI_TOKEN }}
              skip-existing: true

   # Job 4: Publish Django Package to PyPI
   publish-django:
      name: 📦 Publish Django Package
      runs-on: ubuntu-latest
      needs: [publish-wasm, publish-python-wheels]
      steps:
         - name: Checkout code
           uses: actions/checkout@v4

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

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Update version in files
           run: python update_version.py ${{ steps.version.outputs.VERSION }}

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

         - name: Download WASM package
           uses: actions/download-artifact@v4
           with:
              name: wasm-pkg
              path: bindings/javascript/pkg

         - name: Sync assets to Django package
           run: |
              cd bindings/django
              python build_assets.py

         - name: Build Django package
           run: |
              cd bindings/django
              python -m build

         - name: Publish to PyPI
           uses: pypa/gh-action-pypi-publish@release/v1
           with:
              packages-dir: bindings/django/dist
              password: ${{ secrets.PYPI_TOKEN }}
              skip-existing: true

   # Job 5: Build PHP Extension
   publish-php:
      name: 📦 Build PHP Extension
      needs: sync-version-to-main
      runs-on: ${{ matrix.os }}
      strategy:
         matrix:
            os: [ubuntu-latest, macos-latest]
            php-version: ['8.0', '8.1', '8.2', '8.3']
      steps:
         - name: Checkout code
           uses: actions/checkout@v4

         - name: Set up PHP
           uses: shivammathur/setup-php@v2
           with:
              php-version: ${{ matrix.php-version }}
              extensions: none
              tools: none

         - name: Install Rust toolchain
           uses: dtolnay/rust-toolchain@stable

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Update version in files
           run: python update_version.py ${{ steps.version.outputs.VERSION }}

         - name: Set RUSTFLAGS for macOS
           if: runner.os == 'macOS'
           run: echo "RUSTFLAGS=-C link-arg=-undefined -C link-arg=dynamic_lookup" >> $GITHUB_ENV

         - name: Build PHP extension
           run: |
              cd bindings/php
              cargo build --release

         - name: Upload PHP extension artifact
           uses: actions/upload-artifact@v4
           with:
              name: php-extension-${{ matrix.os }}-php${{ matrix.php-version }}
              path: |
                 bindings/php/target/release/libnpdatetime_php.*
                 bindings/php/target/release/npdatetime_php.*

   # Job 6: Create GitHub Release
   create-release:
      name: 🎉 Create GitHub Release
      needs: [publish-rust, publish-wasm, publish-python-wheels, publish-django, publish-php]
      runs-on: ubuntu-latest
      if: startsWith(github.ref, 'refs/tags/')
      steps:
         - name: Checkout code
           uses: actions/checkout@v4

         - name: Extract version from tag
           id: version
           run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_OUTPUT
           shell: bash

         - name: Create Release
           uses: softprops/action-gh-release@v2
           with:
              name: Release v${{ steps.version.outputs.VERSION }}
              body: |
                 ## 🎉 Release v${{ steps.version.outputs.VERSION }}

                 ### Published Packages:
                  - 🦀 Rust: [`npdatetime`](https://crates.io/crates/npdatetime) v${{ steps.version.outputs.VERSION }}
                  - 🐍 Python: [`npdatetime`](https://pypi.org/project/npdatetime/) v${{ steps.version.outputs.VERSION }}
                  - 🌐 JavaScript: [`@4mritgiri/npdatetime`](https://www.npmjs.com/package/@4mritgiri/npdatetime) v${{ steps.version.outputs.VERSION }}
                  - 🎯 Django: [`django-npdt`](https://pypi.org/project/django-npdt/) v${{ steps.version.outputs.VERSION }}
                  - 🐘 PHP: Extension binaries available in release assets

                 See [CHANGELOG.md](CHANGELOG.md) for details.
              draft: false
              prerelease: false
           env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}