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
37 changes: 34 additions & 3 deletions .github/scripts/package_release.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/usr/bin/env bash
# Make the release files for one family or for the site.
# Usage: .github/scripts/package_release.sh weft|sans|site VERSION OUT_DIR
# Usage: .github/scripts/package_release.sh weft|sans|heddle|site VERSION OUT_DIR
# VERSION is the version from the tag, for example 0.1.0.
# OUT_DIR receives the zip file and the single files to attach.
# Run from the repository root, after the build and the checks.
# For weft, OUT_DIR/readability.json must exist already (the release
# workflow writes it with weft/tools/readability.py --gate).
set -euo pipefail

fam="${1:?family: weft, sans or site}"
fam="${1:?family: weft, sans, heddle or site}"
version="${2:?version, for example 0.1.0}"
out="${3:?output folder}"
mkdir -p "$out"
Expand Down Expand Up @@ -104,6 +104,37 @@ case "$fam" in
"fonts/sans/variable/MachineMadeSans[wght].ttf" \
"fonts/sans/variable/MachineMadeSans-Italic[wght].ttf" "$out/"
;;
heddle)
name="machine-made-heddle-${version}"
need "fonts/heddle/variable/MachineMadeHeddle[wdth,wght].ttf" \
"fonts/heddle/webfonts/MachineMadeHeddle[wdth,wght].woff2" \
"fonts/heddle/webfonts/MachineMadeHeddle-Regular.woff2" \
"fonts/heddle/webfonts/MachineMadeHeddle-Bold.woff2" \
heddle/OFL.txt "heddle/RELEASE-NOTES-${notes_version}.md"
mkdir -p "$stage/$name"
cp -r fonts/heddle/variable fonts/heddle/ttf fonts/heddle/webfonts "$stage/$name/"
cp heddle/OFL.txt "$stage/$name/OFL.txt"
cp "heddle/RELEASE-NOTES-${notes_version}.md" "$stage/$name/RELEASE-NOTES.md"
{
echo "# machine-made Heddle ${version}"
echo
echo "A Swiss display grotesk made by code. Axes: wght 100 to 900, wdth 70 to 140."
echo
echo "- variable/: the variable font (TTF)"
echo "- ttf/: nine static weights at width 100, Compressed Bold and Extended Bold"
echo "- webfonts/: WOFF2 for the web (variable, Regular, Bold)"
echo "- RELEASE-NOTES.md: what is in this release"
echo
echo "Licence: SIL Open Font License 1.1, Reserved Font Name \"machine-made\" (OFL.txt)."
echo "Source: https://github.com/machine-made/type"
echo "Specimen: https://machine-made.github.io/type/heddle/"
} > "$stage/$name/README.md"
make_zip "$stage" "$out/$name.zip"
cp "fonts/heddle/webfonts/MachineMadeHeddle[wdth,wght].woff2" \
fonts/heddle/webfonts/MachineMadeHeddle-Regular.woff2 \
fonts/heddle/webfonts/MachineMadeHeddle-Bold.woff2 \
"fonts/heddle/variable/MachineMadeHeddle[wdth,wght].ttf" "$out/"
;;
site)
name="machine-made-type-site-${version}"
need site/_site/index.html site/_site/data/readability.json
Expand All @@ -112,7 +143,7 @@ case "$fam" in
make_zip "$stage" "$out/$name.zip"
;;
*)
echo "error: unknown family $fam (use weft, sans or site)." >&2
echo "error: unknown family $fam (use weft, sans, heddle or site)." >&2
exit 1
;;
esac
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/heddle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: heddle

on:
push:
branches: [main]
paths: ["heddle/**", "fonts/heddle/**", "proofs/heddle/**", ".github/workflows/heddle.yml"]
pull_request:
paths: ["heddle/**", "fonts/heddle/**", "proofs/heddle/**", ".github/workflows/heddle.yml"]
workflow_dispatch:

permissions:
contents: read

env:
FONTSPECTOR_VERSION: "1.9.0"
# SHA-256 of fontspector-1.9.0-x86_64-unknown-linux-gnu.tar.gz.
FONTSPECTOR_SHA256: "0d4a7fbc577f7f99a084dec2a7acc13f64f923cbe44c0e3c5e3d76469786b8e2"

jobs:
build:
runs-on: ubuntu-24.04
timeout-minutes: 30
defaults:
run:
working-directory: heddle
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.14"

- name: Install dependencies
run: |
sudo apt-get update
# Raqm shapes the kerning proof (tools/proof.py).
sudo apt-get install -y --no-install-recommends libraqm0 libfribidi0
python -m pip install --upgrade pip
python -m pip install -e ".[test]"
# One test runs the sketch engine (reference/heddle-sketch-0.1.js).
node --version

- name: Run tests
run: python -m pytest -q

- name: Build fonts
run: scripts/build.sh

- name: Check interpolation (fontTools varLib.interpolatable)
run: |
fonttools varLib.interpolatable build/Heddle.designspace
fonttools varLib.interpolatable build/master/*.ufo
fonttools varLib.interpolatable "../fonts/heddle/variable/MachineMadeHeddle[wdth,wght].ttf"

- name: Check that the committed fonts are current
run: |
if [ -n "$(git -C .. status --porcelain -- fonts/heddle)" ]; then
git -C .. status --short -- fonts/heddle
echo "::error::fonts/heddle differs from a fresh build. Run heddle/scripts/build.sh and commit the fonts."
exit 1
fi

- name: Check that the drift report is current
run: |
python tools/drift.py
if [ -n "$(git -C .. status --porcelain -- proofs/heddle/drift.json)" ]; then
git -C .. diff -- proofs/heddle/drift.json | head -40
echo "::error::proofs/heddle/drift.json differs. Run heddle/tools/drift.py and commit the report."
exit 1
fi

- name: Install Fontspector
run: |
name="fontspector-${FONTSPECTOR_VERSION}-x86_64-unknown-linux-gnu"
curl -sSfL -o fontspector.tar.gz \
"https://github.com/fonttools/fontspector/releases/download/fontspector-v${FONTSPECTOR_VERSION}/${name}.tar.gz"
echo "${FONTSPECTOR_SHA256} fontspector.tar.gz" | sha256sum --check
tar xzf fontspector.tar.gz
sudo install -m 755 "${name}/fontspector" /usr/local/bin/fontspector
fontspector --version

- name: Check fonts (Fontspector, universal profile)
run: scripts/check.sh fontspector

- name: Add check reports to the summary
if: always()
run: cat build/reports/*.md >> "$GITHUB_STEP_SUMMARY" || true

- name: Render proof sheets
run: python tools/proof.py

- name: Upload fonts, proofs and reports
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: heddle-0.1
path: |
fonts/heddle/
proofs/heddle/
heddle/build/reports/
if-no-files-found: warn
131 changes: 127 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ name: release
# - run the workflow from the Actions tab ("Run workflow") with the tag name;
# the job then makes the tag on the commit it built and pushes it.
# Tag names:
# weft-vX.Y.Z machine-made Weft
# sans-vX.Y.Z machine-made Sans
# site-vX.Y.Z the specimen site (zip of site/_site; the live deploy is site.yml)
# weft-vX.Y.Z machine-made Weft
# sans-vX.Y.Z machine-made Sans
# heddle-vX.Y.Z machine-made Heddle
# site-vX.Y.Z the specimen site (zip of site/_site; the live deploy is site.yml)
# Each job builds again from the tagged commit, fails if the build differs
# from the committed fonts, runs the checks, makes the zip and the single
# files, attests their build provenance and creates the release.
Expand All @@ -17,11 +18,12 @@ on:
tags:
- "weft-v*"
- "sans-v*"
- "heddle-v*"
- "site-v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to make and release: weft-vX.Y.Z, sans-vX.Y.Z or site-vX.Y.Z"
description: "Tag to make and release: weft-vX.Y.Z, sans-vX.Y.Z, heddle-vX.Y.Z or site-vX.Y.Z"
required: true
type: string

Expand Down Expand Up @@ -301,6 +303,127 @@ jobs:
"$OUT/MachineMadeSans-Italic\[wght\].ttf" \
"$OUT/SHA256SUMS"

heddle:
if: startsWith(inputs.tag || github.ref_name, 'heddle-v')
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write
id-token: write
attestations: write
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Get the version from the tag
run: |
version="${RELEASE_TAG#heddle-v}"
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Tag ${RELEASE_TAG} is not heddle-vX.Y.Z."
exit 1
fi
echo "VERSION=$version" >> "$GITHUB_ENV"

- name: Set up Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.14"

- name: Install dependencies
working-directory: heddle
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[test]"
node --version

- name: Run tests
working-directory: heddle
run: python -m pytest -q

- name: Build fonts
working-directory: heddle
run: scripts/build.sh

- name: Check that the build is the same as the committed fonts
run: |
if [ -n "$(git status --porcelain -- fonts/heddle)" ]; then
git status --short -- fonts/heddle
echo "::error::The build differs from the committed fonts/heddle. Do not release these files."
exit 1
fi
echo "The build has the same bytes as the committed fonts/heddle."

- name: Check interpolation (fontTools varLib.interpolatable)
working-directory: heddle
run: |
fonttools varLib.interpolatable build/Heddle.designspace
fonttools varLib.interpolatable build/master/*.ufo
fonttools varLib.interpolatable "../fonts/heddle/variable/MachineMadeHeddle[wdth,wght].ttf"

- name: Install Fontspector
run: |
name="fontspector-${FONTSPECTOR_VERSION}-x86_64-unknown-linux-gnu"
curl -sSfL -o "$RUNNER_TEMP/fontspector.tar.gz" \
"https://github.com/fonttools/fontspector/releases/download/fontspector-v${FONTSPECTOR_VERSION}/${name}.tar.gz"
echo "${FONTSPECTOR_SHA256} $RUNNER_TEMP/fontspector.tar.gz" | sha256sum --check
tar xzf "$RUNNER_TEMP/fontspector.tar.gz" -C "$RUNNER_TEMP"
sudo install -m 755 "$RUNNER_TEMP/${name}/fontspector" /usr/local/bin/fontspector
fontspector --version

- name: Check fonts (Fontspector, universal profile)
working-directory: heddle
run: scripts/check.sh fontspector

- name: Make the release files
run: .github/scripts/package_release.sh heddle "$VERSION" "$OUT"

- name: Attest build provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: |
${{ env.OUT }}/machine-made-heddle-*.zip
${{ env.OUT }}/*.woff2
${{ env.OUT }}/*.ttf

- name: Make and push the tag (manual run only)
if: github.event_name == 'workflow_dispatch'
run: |
if [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo "::error::Run the release from main, not ${GITHUB_REF}."
exit 1
fi
# A tag that exists already is fine when its commit has the same release files
# (fonts/heddle heddle) as this commit: that is a re-run after a failed upload, and the
# release goes onto that tag. Any other change needs a new version.
existing="$(git ls-remote --tags origin "refs/tags/${RELEASE_TAG}^{}" "refs/tags/${RELEASE_TAG}" | awk '{print $1}' | tail -n 1)"
if [ -n "$existing" ]; then
git fetch --quiet --depth=1 origin "$existing"
if git diff --quiet "$existing" "$GITHUB_SHA" -- fonts/heddle heddle; then
echo "Tag ${RELEASE_TAG} exists already on ${existing} with the same release files. Re-running the release."
exit 0
fi
echo "::error::Tag ${RELEASE_TAG} exists already on ${existing}, and the release files differ. Use a new version."
exit 1
fi
git tag "$RELEASE_TAG" "$GITHUB_SHA"
git push origin "refs/tags/${RELEASE_TAG}"

- name: Create the release
# gh reads each file argument as a glob, so the brackets of the variable
# font names are escaped with a backslash.
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$RELEASE_TAG" --verify-tag \
--title "machine-made Heddle ${VERSION}" \
--notes-file "heddle/RELEASE-NOTES-${VERSION%.*}.md" \
"$OUT/machine-made-heddle-${VERSION}.zip" \
"$OUT/MachineMadeHeddle\[wdth,wght\].woff2" \
"$OUT/MachineMadeHeddle-Regular.woff2" \
"$OUT/MachineMadeHeddle-Bold.woff2" \
"$OUT/MachineMadeHeddle\[wdth,wght\].ttf" \
"$OUT/SHA256SUMS"

site:
if: startsWith(inputs.tag || github.ref_name, 'site-v')
runs-on: ubuntu-24.04
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ jobs:
- name: Check the built site
run: |
for f in "fonts/weft/MachineMadeWeft[FRAG,wght].woff2" "fonts/sans/MachineMadeSans[wght].woff2" \
"fonts/sans/MachineMadeSans-Italic[wght].woff2" data/readability.json index.html weft/index.html; do
"fonts/sans/MachineMadeSans-Italic[wght].woff2" "fonts/heddle/MachineMadeHeddle[wdth,wght].woff2" \
data/readability.json data/heddle.json index.html weft/index.html heddle/index.html; do
test -s "site/_site/$f" || { echo "::error::site/_site/$f is missing."; exit 1; }
done
python -c 'import json,sys; r=json.load(open("site/_site/data/readability.json")); sys.exit(0 if r.get("rows") else 1)'
Expand Down
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@ Each family keeps its own changelog:

- machine-made Weft: [`weft/CHANGELOG.md`](weft/CHANGELOG.md)
- machine-made Sans: [`sans/CHANGELOG.md`](sans/CHANGELOG.md)
- machine-made Heddle: [`heddle/CHANGELOG.md`](heddle/CHANGELOG.md)

Release tags: `weft-vX.Y.Z`, `sans-vX.Y.Z`, `site-vX.Y.Z`.
Release tags: `weft-vX.Y.Z`, `sans-vX.Y.Z`, `heddle-vX.Y.Z`, `site-vX.Y.Z`.

## 2026-10-27: Heddle 0.1

- machine-made Heddle 0.1.0 (font version 0.100). See
`heddle/CHANGELOG.md`.
Sketched as "Shuttle"; renamed before the release (name clash).
- Add `.github/workflows/heddle.yml` (tests, build, byte check,
interpolation, drift report, Fontspector, proofs) and a `heddle` job
in `release.yml` for `heddle-v*` tags. `package_release.sh` packs
Heddle.
- Site: add the Heddle page (playground with weight and width, width
rows, weight rows, glyph map), Heddle on the home and download
pages, and Heddle in the nav.

## 2026-10-26: release 0.1

Expand Down
7 changes: 5 additions & 2 deletions LICENSING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ code.
| --- | --- | --- |
| `fonts/weft/**` | SIL Open Font License 1.1, Reserved Font Name "machine-made" | `weft/OFL.txt` |
| `weft/loom/glyphs.py`, `weft/loom/extended.py` (the stroke tables that define the outlines; each file says so in its header) | SIL Open Font License 1.1, Reserved Font Name "machine-made" | `weft/OFL.txt` |
| `fonts/heddle/**` | SIL Open Font License 1.1, Reserved Font Name "machine-made" | `heddle/OFL.txt` |
| `heddle/loom/glyphs.py`, `heddle/loom/kerning.py` (the glyph and kerning tables that define the outlines and spacing; each file says so in its header) | SIL Open Font License 1.1, Reserved Font Name "machine-made" | `heddle/OFL.txt` |
| `heddle/reference/**` | The approved sketch (engine, page, screenshot): same licence as the font it defines | `heddle/OFL.txt` |
| `fonts/sans/**` | SIL Open Font License 1.1, Reserved Font Name "machine-made" (copyright: Plus Jakarta Sans Project Authors and machine-made Sans Project Authors) | `sans/OFL.txt` |
| `sans/sources/**` | SIL Open Font License 1.1, as for `fonts/sans/` | `sans/OFL.txt` |
| `sans/upstream/**` | Upstream Plus Jakarta Sans credit files, unchanged (SIL OFL 1.1) | `sans/upstream/OFL.txt` |
| `proofs/**` | Images and reports made from the fonts: same licence as the font they show | `weft/OFL.txt` or `sans/OFL.txt` |
| Every other file, for example the rest of `weft/loom/`, `weft/tools/`, `weft/scripts/`, `weft/tests/`, `sans/scripts/`, `sans/tests/`, `site/`, `.github/` | Apache License 2.0 | `LICENSE` |
| `proofs/**` | Images and reports made from the fonts: same licence as the font they show | `weft/OFL.txt`, `sans/OFL.txt` or `heddle/OFL.txt` |
| Every other file, for example the rest of `weft/loom/` and `heddle/loom/`, `weft/tools/`, `weft/scripts/`, `weft/tests/`, `heddle/tools/`, `heddle/scripts/`, `heddle/tests/`, `sans/scripts/`, `sans/tests/`, `site/`, `.github/` | Apache License 2.0 | `LICENSE` |

The Reserved Font Name "machine-made" means: if you change a font and
distribute it, give it a name that does not contain "machine-made".
Expand Down
Loading