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
154 changes: 95 additions & 59 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,132 +13,168 @@
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: 'pip'

- name: Cache pre-commit
uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}

- name: Install pre-commit
run: pip install pre-commit

- name: Run pre-commit
run: pre-commit run --all-files

tests:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: ${{ matrix.os }} / ${{ matrix.python-version }}
name: Tests / ${{ matrix.os }} / ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.9', 'pypy-3.10', 'pypy-3.11']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
include:
- os: ubuntu-latest
python-version: '3.15'
experimental: true
- os: ubuntu-latest
python-version: 'pypy-3.9'
- os: ubuntu-latest
python-version: 'pypy-3.10'
- os: ubuntu-latest
python-version: 'pypy-3.11'
continue-on-error: ${{ matrix.experimental || false }}

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

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: ${{ matrix.experimental || false }}
cache: 'pip'

- name: Cache Pipenv virtualenv
id: cache-pipenv
uses: actions/cache@v4
with:
path: |
.venv
~/.cache/pip
key: ${{ runner.os }}-${{ matrix.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }}

- name: Install pipenv
run: python -m pip install --upgrade pip pipenv

- name: Install dependencies
shell: bash
env:
PIPENV_NOSPIN: "1"
PIPENV_VENV_IN_PROJECT: "1"
PIPENV_NOSPIN: '1'
PIPENV_VENV_IN_PROJECT: '1'
run: |
if [ "${{ runner.os }}" = "Windows" ]; then
export PIPENV_PYTHON="${pythonLocation}\\python.exe"
else
export PIPENV_PYTHON="${pythonLocation}/bin/python"
fi
pipenv install --dev

- name: Display Python version
- name: Run tests
if: ${{ !(matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13') }}
env:
PIPENV_NOSPIN: "1"
PIPENV_VENV_IN_PROJECT: "1"
run: pipenv run python -V

- name: Run tests (with coverage on Linux CPython 3.12)
if: ${{ runner.os == 'Linux' && matrix.python-version == '3.12' }}
env:
PIPENV_NOSPIN: "1"
PIPENV_VENV_IN_PROJECT: "1"
run: pipenv run pytest -ra --cov=persiantools --cov-report xml:coverage.xml tests/
PIPENV_NOSPIN: '1'
PIPENV_VENV_IN_PROJECT: '1'
run: pipenv run pytest -ra tests/

- name: Run tests (no coverage)
if: ${{ !(runner.os == 'Linux' && matrix.python-version == '3.12') }}
- name: Run tests with coverage
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
env:
PIPENV_NOSPIN: "1"
PIPENV_VENV_IN_PROJECT: "1"
run: pipenv run pytest -ra tests/
PIPENV_NOSPIN: '1'
PIPENV_VENV_IN_PROJECT: '1'
run: pipenv run pytest -ra --cov=persiantools --cov-report=xml tests/

- name: Upload coverage to Codecov
if: ${{ runner.os == 'Linux' && matrix.python-version == '3.12' }}
uses: codecov/codecov-action@v4
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
files: coverage.xml
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

lint:
name: Lint
build:
Comment on lines 41 to 116

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 10 days ago

In general, this issue is fixed by explicitly setting a minimal permissions block either at the workflow root (applies to all jobs) or at the individual job level. Since the publish-test-pypi and publish-pypi jobs already specify the extra id-token: write permission they need, the cleanest approach is to set a restrictive default at the workflow root (e.g., contents: read) and then rely on existing job-level permissions for the publishing jobs.

The single best fix here without changing functionality is:

  • Add a workflow-level permissions block near the top of .github/workflows/ci.yml, right after the on: block and before concurrency:.
  • Set contents: read as the default, which is sufficient for actions/checkout@v4 and all the current jobs’ behavior (lint, tests, build). The publishing jobs already define permissions: id-token: write, which will override the workflow default for those jobs and keep them functioning as before.

Concretely:

  • Edit .github/workflows/ci.yml.

  • After line 10 (workflow_dispatch:) insert:

    permissions:
      contents: read
  • No other imports, methods, or definitions are required; this is purely a workflow YAML change.

Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,6 +8,9 @@
     branches: ["**"]
   workflow_dispatch:
 
+permissions:
+  contents: read
+
 concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
EOF
@@ -8,6 +8,9 @@
branches: ["**"]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Copilot is powered by AI and may make mistakes. Always verify output.
name: Build
runs-on: ubuntu-latest
needs: [lint, tests]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
python-version: '3.13'
cache: 'pip'
- name: Install pre-commit
run: pip install pre-commit
- name: Cache pre-commit
uses: actions/cache@v4

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

- name: Build sdist and wheel
run: python -m build

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
run: pre-commit run --all-files
name: dist
path: dist/
if-no-files-found: error

build-and-publish:
publish-test-pypi:
Comment on lines +117 to +144

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 10 days ago

In general, you should explicitly specify permissions for the workflow (or per job) so the GITHUB_TOKEN is restricted to the least privileges necessary. For build and test jobs that only read the repository and interact with external services via their own tokens (like Codecov or PyPI OIDC), contents: read is usually sufficient, and sometimes even contents: none if actions/checkout is not used.

For this workflow, the cleanest and least-disruptive fix is:

  • Add a permissions block at the workflow root that applies to all jobs by default, setting contents: read. This covers lint, tests, and build, which only need to read the repo and do not appear to need write access to anything in GitHub.
  • Keep the existing per-job permissions blocks for publish-test-pypi and publish-pypi (they already request id-token: write), letting GitHub merge the defaults with job-specific overrides. They do not need contents write access either, so the root contents: read works fine.

Concretely:

  • In .github/workflows/ci.yml, add:

    permissions:
      contents: read

    after the on: block and before the concurrency: block (around line 11).

  • No other changes to steps or jobs are required.

This adds explicit minimal permissions without changing existing functionality.

Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,6 +8,9 @@
     branches: ["**"]
   workflow_dispatch:
 
+permissions:
+  contents: read
+
 concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
EOF
@@ -8,6 +8,9 @@
branches: ["**"]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Copilot is powered by AI and may make mistakes. Always verify output.
name: Publish to Test PyPI
if: github.repository == 'majiidd/persiantools' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
needs: [build]
environment: test-pypi
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
fetch-depth: 0

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'

- name: Build sdist and wheel
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade build
python -m build --sdist --wheel --outdir dist/ .
name: dist
path: dist/

- name: Publish to Test PyPI
if: always()
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
verbose: true

publish-pypi:
name: Publish to PyPI
if: github.repository == 'majiidd/persiantools' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
needs: [build, publish-test-pypi]
environment: pypi
permissions:
id-token: write
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
verbose: true
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [5.5.0](https://github.com/majiidd/persiantools/compare/5.4.0...5.5.0) - 2026-01-30

- Python 3.14 support.
- Migrated from `setup.py` to `pyproject.toml` (PEP 621).
- Improved CI/CD pipeline with job dependencies and parallel execution.
- Marked Python 3.15 as experimental with allowed failures.

## [5.4.0](https://github.com/majiidd/persiantools/compare/5.3.0...5.4.0) - 2025-09-26

- Removed `pytz` dependency; migrated fully to `zoneinfo` and `datetime.timezone`.
Expand Down
67 changes: 66 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,71 @@
[build-system]
requires = ["setuptools>=80.0"]
build-backend = "setuptools.build_meta"

[project]
name = "persiantools"
dynamic = ["version"]
description = "Jalali date and datetime with other tools"
readme = "README.md"
license = "MIT"
requires-python = ">=3.9"
authors = [
{ name = "Majid Hajiloo", email = "majid.hajiloo@gmail.com" }
]
keywords = [
"jalali",
"shamsi",
"persian",
"digits",
"characters",
"converter",
"jalalidate",
"jalalidatetime",
"date",
"datetime",
"jdate",
"jdatetime",
"farsi",
]
classifiers = [
"Intended Audience :: Developers",
"Natural Language :: Persian",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Localization",
"Topic :: Utilities",
]

dependencies = [
"tzdata; platform_system == 'Windows'",
]

[project.urls]
Homepage = "https://github.com/majiidd/persiantools"
Source = "https://github.com/majiidd/persiantools"
Issues = "https://github.com/majiidd/persiantools/issues"

[tool.setuptools.dynamic]
version = { attr = "persiantools.__version__" }

[tool.setuptools.packages.find]
include = ["persiantools*"]

[tool.black]
line-length = 120
target-version = ["py39", "py310", "py311", "py312", "py313"]
target-version = ["py39", "py310", "py311", "py312", "py313", "py314"]
include = '\.pyi?$'
exclude = '''
(
Expand Down
60 changes: 0 additions & 60 deletions setup.py

This file was deleted.

Loading