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
114 changes: 114 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: ttx-diff CI/CD

on:
push:
branches:
- main
paths:
- 'ttx_diff/**'
- '.github/workflows/python.yml'
tags:
- 'ttx-diff-v*'
pull_request:
paths:
- 'ttx_diff/**'
- '.github/workflows/python.yml'
workflow_dispatch:

jobs:
test:
name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ['3.10', '3.13']
# TODO: Re-enable 3.14 once gftools updates pygit2 pin from 1.16.0
# https://github.com/googlefonts/gftools/blob/d4c06f5d88e0a849fabf7089d80835a96dc42f30/pyproject.toml#L47-L49

steps:
- uses: actions/checkout@v5

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

- name: Install package with test dependencies
working-directory: ttx_diff
run: pip install -e ".[test]"

- name: Run tests
working-directory: ttx_diff
run: pytest -v

lint:
name: Lint and format check
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"

- name: Check package builds
working-directory: ttx_diff
run: pipx run build

- name: Install package with dev dependencies
working-directory: ttx_diff
run: pip install --find-links=dist 'ttx-diff[dev]'

- name: Check formatting with ruff
working-directory: ttx_diff
run: ruff format --check src/ tests/

- name: Lint with ruff
working-directory: ttx_diff
run: ruff check src/ tests/

build:
name: Build distribution
runs-on: ubuntu-latest
needs: [test, lint]
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/ttx-diff-v')

steps:
- uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.x"

- name: Build package
working-directory: ttx_diff
run: pipx run build

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
path: ttx_diff/dist/

publish-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: [build]
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/ttx-diff-v')
permissions:
id-token: write # Required for trusted publishing

steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
10 changes: 10 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
on:
pull_request:
paths-ignore:
- 'ttx_diff/**'
- '.github/workflows/python.yml'
- 'resources/scripts/requirements.in'
- 'resources/scripts/requirements.txt'
merge_group:
push:
branches:
- main
tags:
- "*"
paths-ignore:
- 'ttx_diff/**'
- '.github/workflows/python.yml'
- 'resources/scripts/requirements.in'
- 'resources/scripts/requirements.txt'

name: Continuous integration

Expand Down
2 changes: 1 addition & 1 deletion fontc_crater/src/ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ fn ttx_diff_has_changes(last_run_sha: &str) -> bool {
.unwrap();
std::str::from_utf8(&output.stdout)
.unwrap()
.contains("ttx_diff.py")
.contains("ttx_diff/")
}

#[derive(Debug, Default)]
Expand Down
4 changes: 2 additions & 2 deletions fontc_crater/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl Target {
Default::default()
};
let mut cmd = format!(
"python3 resources/scripts/ttx_diff.py '{repo_url}{sha_part}#{}'",
"python3 -m ttx_diff '{repo_url}{sha_part}#{}'",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just ttx-diff, people should install it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wanted to avoid fontc_crater falling into a trap of invoking a random ttx-diff on PATH. The previous code was calling python3 with the script path. This is the equivalent, to ensure it runs the ttx_diff installed within the same environment as python3 itself.

rel_source_path.display()
);
if self.build == BuildType::GfTools {
Expand Down Expand Up @@ -371,7 +371,7 @@ mod tests {
let hmm = target.repro_command("example.com");
assert_eq!(
hmm,
"python3 resources/scripts/ttx_diff.py 'example.com?123456789a#sources/hi.glyphs'"
"python3 -m ttx_diff 'example.com?123456789a#sources/hi.glyphs'"
);
}
}
47 changes: 21 additions & 26 deletions fontc_crater/src/ttx_diff_runner.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
process::Command,
};
use std::{collections::BTreeMap, path::PathBuf, process::Command};

use crate::{BuildType, Results, RunResult, Target, ci::ResultsCache};

static SCRIPT_PATH: &str = "./resources/scripts/ttx_diff.py";
// Run ttx-diff via python -m to ensure we use the venv's installed version
static TTX_DIFF_MODULE: &str = "ttx_diff";

pub(super) struct TtxContext {
pub fontc_path: PathBuf,
Expand All @@ -24,13 +21,20 @@ pub(super) fn run_ttx_diff(ctx: &TtxContext, target: &Target) -> RunResult<DiffO
ctx.results_cache
.copy_cached_files_to_build_dir(target, &build_dir);
let mut cmd = Command::new("python3");
cmd.args([SCRIPT_PATH, "--json", "--compare", compare, "--outdir"])
.arg(outdir)
.arg("--fontc_path")
.arg(&ctx.fontc_path)
.arg("--normalizer_path")
.arg(&ctx.normalizer_path)
.args(["--rebuild", "fontc"]);
cmd.args([
"-m",
TTX_DIFF_MODULE,
"--json",
"--compare",
compare,
"--outdir",
])
.arg(outdir)
.arg("--fontc_path")
.arg(&ctx.fontc_path)
.arg("--normalizer_path")
.arg(&ctx.normalizer_path)
.args(["--rebuild", "fontc"]);
if target.build == BuildType::GfTools {
cmd.arg("--config")
.arg(target.config_path(&ctx.source_cache));
Expand Down Expand Up @@ -192,30 +196,21 @@ fn non_nan(val: f32) -> f32 {
if val.is_nan() { 0.0 } else { val }
}

/// make sure we can find and execute ttx_diff script
/// make sure we can find and execute ttx-diff module
pub(super) fn assert_can_run_script() {
// first check that we can find timeout(1) (not present on macOS by default,
// install via homebrew)
assert_has_timeout_coreutil();
// then check that we can run the ttx_diff script itself
let path = Path::new(SCRIPT_PATH);
if !path.exists() {
eprintln!(
"cannot find script at {}",
path.canonicalize().as_deref().unwrap_or(path).display()
);
std::process::exit(1);
}
// then check that we can run ttx-diff via python -m
match Command::new("python3")
.arg(SCRIPT_PATH)
.arg("--only_check_args")
.args(["-m", TTX_DIFF_MODULE, "--only_check_args"])
.output()
{
Ok(output) if output.status.success() => return,
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("could not run ttx_diff.py. Have you setup your venv?");
eprintln!("could not run ttx-diff. Have you setup your venv?");
if !stdout.is_empty() {
eprintln!("stdout: {stdout}");
}
Expand Down
15 changes: 6 additions & 9 deletions resources/scripts/requirements.in
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
absl-py
# keep fontmake version pinned to ensure output from ttx_diff.py is stable;
# ttx-diff package with all its dependencies (single source of truth)
-e file:./ttx_diff

# Pin versions for CI stability (overrides ttx-diff's minimum versions)
# keep fontmake version pinned to ensure output from ttx-diff is stable;
# the 'repacker' option enables faster GSUB/GPOS serialization via uharfbuzz
fontmake[repacker]==3.11.0
# technically fonttools is in turn a dependency of fontmake but a few of
# our scripts import it directly, so we list it among the top-level requirements.
fonttools
lxml
cdifflib
glyphsLib
# keep gftools pinned as well to ensure ttx_diff.py output is stable.
# keep gftools pinned as well to ensure ttx-diff output is stable.
# 0.9.74 is when experimental support for fontc was added to gftools.
gftools==0.9.93
22 changes: 14 additions & 8 deletions resources/scripts/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
#
# pip-compile resources/scripts/requirements.in
#
-e file:./ttx_diff
# via -r resources/scripts/requirements.in
absl-py==2.3.1
# via
# -r resources/scripts/requirements.in
# gftools
# nanoemoji
# picosvg
# ttx-diff
afdko==4.0.2
# via gftools
attrs==25.4.0
Expand Down Expand Up @@ -41,7 +43,7 @@ cattrs==25.3.0
# statmake
# ufolib2
cdifflib==1.2.9
# via -r resources/scripts/requirements.in
# via ttx-diff
certifi==2025.10.5
# via requests
cffi==2.0.0
Expand Down Expand Up @@ -76,6 +78,7 @@ fontmake[json,repacker]==3.11.0
# via
# -r resources/scripts/requirements.in
# gftools
# ttx-diff
fontmath==0.9.4
# via
# afdko
Expand All @@ -90,7 +93,6 @@ fontpens==0.2.4
# via defcon
fonttools[lxml,repacker,ufo,unicode,woff]==4.60.1
# via
# -r resources/scripts/requirements.in
# afdko
# axisregistry
# babelfont
Expand All @@ -111,6 +113,7 @@ fonttools[lxml,repacker,ufo,unicode,woff]==4.60.1
# mutatormath
# nanoemoji
# statmake
# ttx-diff
# ufo2ft
# ufolib2
# ufomerge
Expand All @@ -126,7 +129,9 @@ gflanguages==0.7.7
gfsubsets==2024.9.25
# via gftools
gftools==0.9.93
# via -r resources/scripts/requirements.in
# via
# -r resources/scripts/requirements.in
# ttx-diff
gitdb==4.0.12
# via gitpython
gitpython==3.1.45
Expand All @@ -135,11 +140,11 @@ glyphsets==1.1.0
# via gftools
glyphslib==6.12.1
# via
# -r resources/scripts/requirements.in
# bumpfontversion
# fontmake
# gftools
# glyphsets
# ttx-diff
idna==3.11
# via requests
importlib-resources==6.5.2
Expand All @@ -148,12 +153,12 @@ jinja2==3.1.6
# via gftools
lxml==6.0.2
# via
# -r resources/scripts/requirements.in
# afdko
# fontfeatures
# fonttools
# nanoemoji
# picosvg
# ttx-diff
markdown-it-py==4.0.0
# via rich
markupsafe==3.0.3
Expand All @@ -177,7 +182,7 @@ openstep-plist==0.5.1
# glyphslib
opentype-sanitizer==9.2.0
# via gftools
orjson==3.11.3
orjson==3.11.4
# via
# babelfont
# ufolib2
Expand Down Expand Up @@ -220,6 +225,7 @@ pyyaml==6.0.3
# via
# gftools
# glyphsets
# ttx-diff
regex==2025.10.23
# via nanoemoji
requests==2.32.5
Expand All @@ -232,7 +238,7 @@ resvg-cli==0.44.0
# via nanoemoji
rich==14.2.0
# via gftools
ruamel-yaml==0.18.15
ruamel-yaml==0.18.16
# via gftools
ruamel-yaml-clib==0.2.14
# via ruamel-yaml
Expand Down
Loading
Loading