Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4c37932
chore: create readme, roadmap, and license files
hasanzakeri Apr 6, 2026
3bdc21a
chore: create readme, roadmap, and license files (#1)
hasanzakeri Apr 6, 2026
a868ebf
chore: define project structure
hasanzakeri Apr 6, 2026
7042dd3
chore: define project structure (#2)
hasanzakeri Apr 6, 2026
8849a90
feat: implement Phase 1 project skeleton
hasanzakeri Apr 6, 2026
2740bbd
feat: implement Phase 1 Rust/Python binding scaffold (#3)
hasanzakeri Apr 6, 2026
030ecbf
feat: add value types — Direction, Script, Language, Feature, Variation
hasanzakeri Apr 7, 2026
78f9d88
Merge branch 'main' into feat/phase2-value-types
hasanzakeri Apr 7, 2026
fca2576
fix(types) Polish error handling in script from_str
hasanzakeri Apr 9, 2026
ffb134c
Merge branch 'main' into fix/script-err-handling
hasanzakeri Apr 9, 2026
990b899
Fix/script err handling (#5)
hasanzakeri Apr 9, 2026
cef5e04
Merge branch 'main' into feat/phase2-value-types
hasanzakeri Apr 9, 2026
96fc481
feat: Phase 2 — value types for text shaping configuration (#4)
hasanzakeri Apr 9, 2026
d00fa64
Add high-level shape() and run_from_args() Python bindings (Phase 3)
hasanzakeri Apr 16, 2026
3992935
Fix clippy warnings and add external test marker
hasanzakeri Apr 16, 2026
e1927d0
feat: High-level shaping API + .tests regression harness (#6)
hasanzakeri Apr 16, 2026
fc142e0
Add Tier 2 regression suite against external harfrust corpus
hasanzakeri Apr 16, 2026
bdaa43f
fix: exclude .tests source files from external test collection
hasanzakeri Apr 16, 2026
e670a6b
Add Tier 2 shaping regression against external harfrust corpus (#7)
hasanzakeri Apr 16, 2026
5a387ec
docs: fix stale package name and plan link
hasanzakeri Apr 22, 2026
7a2410e
docs: fix stale package name and plan link (#8)
hasanzakeri Apr 22, 2026
4af6756
feat: Buffer class wrapping UnicodeBuffer (Phase 4)
hasanzakeri Apr 22, 2026
d75419d
Test set_not_found_variation_selector_glyph and flag repr duplication
hasanzakeri Apr 22, 2026
5c97674
docs: update development plan link in README.md
hasanzakeri Apr 25, 2026
7d65c9f
Buffer class for text input (Phase 4) (#9)
hasanzakeri Apr 25, 2026
3d721e1
feat: Introduce Font, GlyphBuffer, GlyphInfo, and GlyphPosition classes
hasanzakeri Apr 25, 2026
970305b
Enhance glyph information tests and add byte comparison test
hasanzakeri Apr 26, 2026
ffef3f7
Font API and object-level shaping (#16)
hasanzakeri Apr 26, 2026
bebc634
Add PEP 561 type stubs and expand README
hasanzakeri Apr 30, 2026
8f2f7ce
Update README.md to enhance CLI usage documentation and clarify Glyph…
hasanzakeri Apr 30, 2026
839279d
Add PEP 561 type stubs and expand README (#17)
hasanzakeri Apr 30, 2026
a70555b
Add release pipeline gated on tip-of-release
hasanzakeri Apr 30, 2026
7bdd46e
Implement gated release pipeline with CI enforcement (#18)
hasanzakeri Apr 30, 2026
00a22c5
Update pre-commit configuration and CI workflow
hasanzakeri Apr 30, 2026
5eb955d
Update pre-commit configuration and CI workflow (#20)
hasanzakeri Apr 30, 2026
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
108 changes: 108 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: CI

on:
push:
branches: [main, release]
pull_request:
branches: [main, release]
workflow_dispatch:

env:
CARGO_TERM_COLOR: always

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
release-source-gate:
# PRs targeting `release` are only allowed from `main`. GitHub branch
# protection has no "restrict source branch" setting, so we enforce it
# here as a required check. Make this a required status check on the
# `release` branch so the PR can't merge without it.
if: github.event_name == 'pull_request' && github.base_ref == 'release'
runs-on: ubuntu-latest
steps:
- name: PRs to release must come from main
run: |
if [ "$GITHUB_HEAD_REF" != "main" ]; then
echo "::error::PRs to release must come from main, got '$GITHUB_HEAD_REF'."
exit 1
fi

ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2

- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Rust formatting
run: cargo fmt --all --check

- name: Clippy
run: cargo clippy --all -- -D warnings

- name: Rust tests
run: cargo test --all

- name: Install Python deps
run: uv sync --locked

- name: Build extension
run: uv run maturin develop

- name: Ruff check
run: uv run ruff check

- name: Ruff format
run: uv run ruff format --check

- name: Python tests
run: uv run pytest

tier2:
# Tier 2: external regression against the full harfrust shaping corpus.
# Only runs on the release branch, where we care about version-drift signal.
if: github.ref == 'refs/heads/release' || github.base_ref == 'release'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Check out harfrust at 0.5.2
uses: actions/checkout@v6
with:
repository: harfbuzz/harfrust
# Pinned to the 0.5.2 commit so the test corpus matches the hr-shape
# version declared in Cargo.toml. Bump this alongside the dep.
ref: efdae3142ab76a2f1524d72cff9e3dfdc5afd7ca
path: harfrust-external

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Install Python deps
run: uv sync --locked

- name: Build extension
run: uv run maturin develop

- name: Tier 2 shaping regression
env:
HARFRUST_SOURCE: ${{ github.workspace }}/harfrust-external
run: uv run pytest -m external
143 changes: 143 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
name: Release

on:
push:
tags: ['v*']
workflow_dispatch:

permissions:
contents: read

jobs:
gate:
# Enforce that the tag points to the current tip of `release`. The
# release-branch CI (ci.yml) runs Tier 1 + Tier 2 on every push to
# release, so tip-of-release is the SHA on which full CI passed.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Tag must point to the tip of release
run: |
git fetch origin release --depth=1
expected=$(git rev-parse FETCH_HEAD)
if [ "$GITHUB_SHA" != "$expected" ]; then
echo "::error::Tag $GITHUB_REF_NAME ($GITHUB_SHA) is not the tip of release ($expected). See RELEASING.md."
exit 1
fi

linux:
runs-on: ubuntu-latest
needs: gate
strategy:
fail-fast: false
matrix:
target: [x86_64, aarch64]
steps:
- uses: actions/checkout@v6

- uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i python3.11 -i python3.12 -i python3.13

- uses: actions/upload-artifact@v4
with:
name: wheels-linux-${{ matrix.target }}
path: dist

macos:
needs: gate
strategy:
fail-fast: false
matrix:
target: [x86_64, aarch64]
python-version: ['3.11', '3.12', '3.13']
include:
- target: x86_64
runner: macos-13
- target: aarch64
runner: macos-14
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6

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

- uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python${{ matrix.python-version }}

- uses: actions/upload-artifact@v4
with:
name: wheels-macos-${{ matrix.target }}-py${{ matrix.python-version }}
path: dist

windows:
runs-on: windows-latest
needs: gate
strategy:
fail-fast: false
matrix:
target: [x64]
python-version: ['3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.target }}

- uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist

- uses: actions/upload-artifact@v4
with:
name: wheels-windows-${{ matrix.target }}-py${{ matrix.python-version }}
path: dist

sdist:
runs-on: ubuntu-latest
needs: gate
steps:
- uses: actions/checkout@v6

- uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist

- uses: actions/upload-artifact@v4
with:
name: sdist
path: dist

publish:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: [linux, macos, windows, sdist]
if: startsWith(github.ref, 'refs/tags/v')
environment:
name: pypi
url: https://pypi.org/p/pyharfrust
permissions:
# Required for PyPI trusted publishing (OIDC). Configure the publisher
# at https://pypi.org/manage/account/publishing/ before tagging a release.
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true

- uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
skip-existing: true
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Local Cargo config (dev overrides for sibling checkouts)
.cargo/config.toml

# Rust
target/
**/*.rs.bk

# Python
__pycache__/
*.py[cod]
*.so
*.pyd
*.egg-info/

# Environments
.venv/

# Testing
.pytest_cache/

**/*.dSYM
65 changes: 65 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
default_install_hook_types: [pre-commit, pre-push]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- id: no-commit-to-branch
args: [--branch, main, --branch, release]

- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.11.3
hooks:
- id: uv-lock
args: [--locked]

# pre-commit: fast formatting and lint checks only
- repo: local
hooks:
- id: cargo-fmt
name: cargo fmt
entry: cargo fmt --all --check
language: system
types: [rust]
pass_filenames: false

- id: ruff-check
name: ruff check
entry: uv run ruff check --fix
language: system
types: [python]

- id: ruff-format
name: ruff format
entry: uv run ruff format --force-exclude
language: system
types: [python]

# pre-push: compilation, linting, and tests
- id: cargo-clippy
name: cargo clippy
entry: cargo clippy --all -- -D warnings
language: system
types: [rust]
pass_filenames: false
stages: [pre-push]

- id: cargo-test
name: cargo test
entry: cargo test --all
language: system
types: [rust]
pass_filenames: false
stages: [pre-push]

- id: pytest
name: pytest
entry: uv run pytest
language: system
types_or: [python, rust]
pass_filenames: false
stages: [pre-push]
Loading
Loading