diff --git a/.github/actions/cmark-gfm-patch/action.yml b/.github/actions/cmark-gfm-patch/action.yml index c6de7fa..cc64475 100644 --- a/.github/actions/cmark-gfm-patch/action.yml +++ b/.github/actions/cmark-gfm-patch/action.yml @@ -1,7 +1,7 @@ name: Patch cmark-gfm description: Apply local modifications to cmark-gfm runs: - using: "composite" + using: composite steps: - run: git -C third_party/cmark apply $PWD/tasklist-id.patch shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..6fffb52 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,73 @@ +name: Package and upload to PyPi +on: + release: + types: [published, edited] +jobs: + build: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04, windows-2022, macos-12] + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Patch cmark-gfm + uses: ./.github/actions/cmark-gfm-patch + + - uses: actions/setup-python@v3 + name: Install Python + with: + python-version: '3.11' + + - name: Install build deps + run: | + python -m pip --disable-pip-version-check install cibuildwheel==2.15.0 twine==4.0.2 + + - uses: docker/setup-qemu-action@v1 + if: runner.os == 'Linux' + name: Set up QEMU + + - name: Build wheels + run: | + python -m cibuildwheel --output-dir dist + twine check ./dist/*.whl + + - name: Build sdist + if: runner.os == 'Linux' + run: | + python setup.py sdist + twine check ./dist/*.tar.gz + + - uses: actions/upload-artifact@v3 + name: Upload wheels and sdist + with: + name: dist-${{ matrix.os }} + path: |- + ./dist/*.whl + ./dist/*.tar.gz + + upload-pypi: + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + needs: [build] + + steps: + - name: Retrieve dists + uses: actions/download-artifact@v3 + with: + # No name to download from all matrix runs. + path: all_dists + + - name: Combine dists into one folder + run: |- + mkdir -p dist + find all_dists/ -type f -exec cp -v '{}' dist/ ';' + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..4ca2ef1 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,59 @@ +name: Check tests & typing +on: + pull_request: + push: + release: +jobs: + test: + name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Patch cmark-gfm + uses: ./.github/actions/cmark-gfm-patch + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install -U pip pytest + python -m pip install -e . + + - name: Test with pytest + run: pytest tests + + - name: Test installing from sdist + run: | + python setup.py sdist + python -m venv /tmp/sdist-test + /tmp/sdist-test/bin/pip install ./dist/*.tar.gz + /tmp/sdist-test/bin/python -c 'import pycmarkgfm; print(pycmarkgfm.gfm_to_html("it *works*"))' | grep '

it works

' + + check-typing: + name: Check typing with mypy + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v3 + with: + python-version: '3.11' + + - name: Install dependencies + run: python -m pip install mypy + + - name: Check types + run: mypy pycmarkgfm/ tests/ diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml new file mode 100644 index 0000000..2b3e68b --- /dev/null +++ b/.github/workflows/generate.yml @@ -0,0 +1,58 @@ +name: Generate cmark header files +on: + push: + branches: [doesnotexit] + workflow_dispatch: +jobs: + generate: + name: Generate cmake header files for ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Patch cmark-gfm + uses: ./.github/actions/cmark-gfm-patch + + - name: Build + run: | + mkdir build + cd build + cmake ../third_party/cmark + + - name: Copy (Windows) + if: runner.os == 'Windows' + working-directory: ./build + run: | + Get-ChildItem -Recurse + New-Item -ItemType Directory -Force -Path ${{ github.workspace }}/generated/windows + Copy-Item -Force -Path extensions/cmark-gfm-extensions_export.h -Destination ${{ github.workspace }}/generated/windows + Copy-Item -Force -Path src/cmark-gfm_export.h -Destination ${{ github.workspace}}/generated/windows + Copy-Item -Force -Path src/cmark-gfm_version.h -Destination ${{ github.workspace}}/generated/windows + Copy-Item -Force -Path src/config.h -Destination ${{ github.workspace }}/generated/windows + + - name: Copy (Unix) + if: runner.os == 'Linux' + working-directory: ./build + run: | + ls -lRa + mkdir -p ${{ github.workspace }}/generated/unix + cp extensions/cmark-gfm-extensions_export.h src/cmark-gfm_export.h src/cmark-gfm_version.h src/config.h ${{ github.workspace }}/generated/unix + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + add-paths: generated/ + title: Update generated headers for ${{ runner.os }} + commit-message: Update generated headers for ${{ runner.os }}. + body: '' + reviewers: zopieux + branch: gen-headers + branch-suffix: short-commit-hash + delete-branch: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index aa82ea1..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: "Test and package" - -on: - push: - pull_request: - -jobs: - test: - name: "Test Python ${{ matrix.python-version }} on ${{ matrix.os }}" - runs-on: "${{ matrix.os }}" - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] - - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - - name: Patch cmark-gfm - uses: ./.github/actions/cmark-gfm-patch - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install -U pip pytest - python -m pip install -e . - - - name: Test with pytest - run: pytest tests - - - name: Test installing from sdist - run: | - python setup.py sdist - python -m venv /tmp/sdist-test - /tmp/sdist-test/bin/pip install ./dist/*.tar.gz - /tmp/sdist-test/bin/python -c 'import pycmarkgfm; print(pycmarkgfm.gfm_to_html("it *works*"))' | grep '

it works

' - - check-typing: - name: "Check typing with mypy" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: python -m pip install mypy - - - name: Check types - run: mypy pycmarkgfm/ tests/ - - check-generated: - name: "Check generated files are up-to-date" - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - - name: Generate files - run: ./generated.sh - - - name: Check for diffs - run: git diff --exit-code - - package-wheel: - name: "Build manylinux wheels" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - - name: Patch cmark-gfm - uses: ./.github/actions/cmark-gfm-patch - - - name: Build manylinux wheels and test installing from them - uses: docker://quay.io/pypa/manylinux_2_28_x86_64 - with: - entrypoint: /bin/bash - args: > - -c "set -ex; cd /github/workspace && for py in 37 38 39 310 311; do /opt/python/cp${py}-cp*/bin/python setup.py bdist_wheel && auditwheel repair dist/*${py}*.whl && /opt/python/cp${py}-cp*/bin/python -m venv /tmp/test && /tmp/test/bin/pip install wheelhouse/*cp${py}*.whl && pushd /tmp && echo 'it *works*' | /tmp/test/bin/python -c 'import sys, pycmarkgfm; print(pycmarkgfm.gfm_to_html(sys.stdin.read()))' | grep '

it works

'; popd; rm -rf /tmp/test; done" - - - uses: actions/upload-artifact@v2 - # On tags only. - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - with: - path: wheelhouse/*manylinux*.whl - if-no-files-found: error diff --git a/CHANGELOG.md b/CHANGELOG.md index 59018cf..9c6ec22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 1.2.1 + +* No new feature nor bugfix. +* Now compatible with both Linux, MacOS and Windows, with precompiled wheels. +* The process of publishing package updates, including wheels, is now automatic via GitHub actions. + ### 1.2.0 * No new feature nor bugfix. diff --git a/README.md b/README.md index 0bb345c..4cd6082 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ with enhanced support for task lists. * As opposed to most cmark-gfm bindings out there, this package supports [parsing and toggling task lists](#dealing-with-task-lists). * Compatibility: - [![](https://github.com/Zopieux/pycmarkgfm/workflows/Test%20and%20package/badge.svg)](https://github.com/Zopieux/pycmarkgfm/actions?query=workflow%3A%22Test+and+package%22) - with Python 3.5, 3.6, 3.7, 3.8, 3.9 on Linux-like platforms. If you need Windows support, please contribute a PR. + [![](https://github.com/zopieux/pycmarkgfm/actions/workflows/check.yml/badge.svg?event=release)](https://github.com/zopieux/pycmarkgfm/actions/workflows/check.yml) + with Python 3.7 & newer, on Linux, MacOS and Windows. ## Installation -This packages is [available on PyPi](https://pypi.org/project/pycmarkgfm/). +This packages is [available on PyPi](https://pypi.org/project/pycmarkgfm/) along with precompiled wheels for most architectures. $ pip install pycmarkgfm @@ -107,7 +107,7 @@ You can also use `checked=True/False` instead of `TOGGLE` to force a particular [cmarkgfm](https://pypi.org/project/cmarkgfm/) is similar to this package, in fact cmarkgfm's cffi build script is partially re-used in this project – in compliance with its MIT license. -As of October 2020, cmarkgfm is still a well-maintained project and I recommend using it if you don't need the extra +As of October 2023, cmarkgfm is still a well-maintained project and I recommend using it if you don't need the extra features provided by pycmarkgfm, most notably the support for task lists. ## License diff --git a/generated.sh b/generated.sh deleted file mode 100755 index 870047d..0000000 --- a/generated.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -ex - -rm -rf ./build -mkdir -p build - -( cd build && cmake ../third_party/cmark ) - -cp -f \ - build/extensions/cmark-gfm-extensions_export.h \ - build/src/cmark-gfm_export.h \ - build/src/cmark-gfm_version.h \ - build/src/config.h \ - generated/ - -rm -rf ./build diff --git a/generated/cmark-gfm-extensions_export.h b/generated/unix/cmark-gfm-extensions_export.h similarity index 100% rename from generated/cmark-gfm-extensions_export.h rename to generated/unix/cmark-gfm-extensions_export.h diff --git a/generated/cmark-gfm_export.h b/generated/unix/cmark-gfm_export.h similarity index 100% rename from generated/cmark-gfm_export.h rename to generated/unix/cmark-gfm_export.h diff --git a/generated/cmark-gfm_version.h b/generated/unix/cmark-gfm_version.h similarity index 100% rename from generated/cmark-gfm_version.h rename to generated/unix/cmark-gfm_version.h diff --git a/generated/config.h b/generated/unix/config.h similarity index 100% rename from generated/config.h rename to generated/unix/config.h diff --git a/pycmarkgfm/build_cmark.py b/pycmarkgfm/build_cmark.py index 72fa4a1..64c8919 100644 --- a/pycmarkgfm/build_cmark.py +++ b/pycmarkgfm/build_cmark.py @@ -15,7 +15,8 @@ BUILD = os.path.join(PACKAGE_ROOT, "build") SRC_DIR = os.path.join(PACKAGE_ROOT, "third_party/cmark/src") EXTENSIONS_SRC_DIR = os.path.join(PACKAGE_ROOT, "third_party/cmark/extensions") -UNIX_GENERATED_SRC_DIR = os.path.join(PACKAGE_ROOT, "generated") +UNIX_GENERATED_SRC_DIR = os.path.join(PACKAGE_ROOT, "generated", "unix") +WIN_GENERATED_SRC_DIR = os.path.join(PACKAGE_ROOT, "generated", "windows") with open(os.path.join(CWD, "cmark.cffi.h"), "r", encoding="utf-8") as fh: CMARK_DEF_H = fh.read() @@ -56,11 +57,12 @@ def _compiler_type(): COMPILER_TYPE = _compiler_type() -PY2 = sys.version_info[0] < 3 -PY34 = sys.version_info[:2] == (3, 4) -if COMPILER_TYPE in {"unix", "mingw32"} or (PY2 or PY34): +if COMPILER_TYPE in {"unix", "mingw32"}: EXTRA_COMPILE_ARGS = ["-std=c99"] GENERATED_SRC_DIR = UNIX_GENERATED_SRC_DIR +elif COMPILER_TYPE == "msvc": + EXTRA_COMPILE_ARGS = ["/TP"] + GENERATED_SRC_DIR = WIN_GENERATED_SRC_DIR else: raise ValueError("unsupported compiler: %s" % COMPILER_TYPE) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bf2e8b8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[tool.cibuildwheel] +build = "" +skip = "cp36-* pp*" +test-skip = "" +archs = ["auto"] + +[tool.cibuildwheel.macos] +archs = ["auto", "arm64"] + +[tool.cibuildwheel.linux] +archs = ["auto", "aarch64"] +before-all = "yum -y update && yum install -y libffi-devel" + +[[tool.cibuildwheel.overrides]] +select = "*-musllinux*" +before-all = "apk add libffi-dev" + +[tool.yamlfix] +quote_representation = "'" +line_length = 120 +explicit_start = false +whitelines = 1 diff --git a/setup.py b/setup.py index 52ef220..6039c25 100644 --- a/setup.py +++ b/setup.py @@ -2,37 +2,40 @@ from setuptools import setup, find_packages -with open(os.path.join(os.path.dirname(__file__), 'README.md'), encoding='utf-8') as f: +with open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8") as f: long_description = f.read() setup( - name='pycmarkgfm', - version='1.2.0', + name="pycmarkgfm", + version="1.2.1", description="Bindings to GitHub's Flavored Markdown (cmark-gfm), with enhanced support for task lists.", long_description=long_description, long_description_content_type="text/markdown", - url='https://github.com/zopieux/pycmarkgfm', - author='Alexandre Macabies', - author_email='web@zopieux.com', + url="https://github.com/zopieux/pycmarkgfm", + author="Alexandre Macabies", + author_email="web@zopieux.com", classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ], packages=find_packages(), cffi_modules=["pycmarkgfm/build_cmark.py:ffibuilder"], - setup_requires=["cffi>=1.0.0"], - install_requires=["cffi>=1.0.0"], + setup_requires=["cffi>=1.15.0"], + install_requires=["cffi>=1.15.0"], project_urls={ - 'Bug Reports': 'https://github.com/zopieux/pycmarkgfm/issues', - 'Source': 'https://github.com/zopieux/pycmarkgfm', + "Bug Reports": "https://github.com/zopieux/pycmarkgfm/issues", + "Source": "https://github.com/zopieux/pycmarkgfm", }, zip_safe=False, include_package_data=True,