release and custom build in CI

This commit is contained in:
subframe7536 2024-12-03 18:02:41 +08:00
parent c1d1d49f8a
commit dc3fcdae9e
7 changed files with 241 additions and 35 deletions

70
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,70 @@
name: Release Font
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
options:
description: 'Options for build.py'
required: false
default: ''
cn:
description: 'Include Chinese version'
required: false
default: 'false'
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Checkout branch
run: git checkout variable
- name: Install ftcli
run: python -m pip install foundrytools-cli
- name: Run release script
run: |
if [ "${{ github..event_name }}" == "push" ]; then
python release.py
else
options="${{ github.event.inputs.options }}"
if [ "${{ github.event.inputs.cn }}" == "true" ]; then
options="$options --cn"
fi
python build.py $options
fi
- name: Generate release notes
id: release_notes
run: |
if [ "${{ github.event_name }}" == "push" ]; then
echo "notes=$(git log --pretty=%B $(git describe --tags --abbrev=0)..HEAD)" >> $GITHUB_ENV
else
notes=$(python release.py ${{ github.event.inputs.options }} --dry)
echo "notes=### Configuration:\n\n```\n$notes\n```" >> $GITHUB_ENV
- name: Generate tag name
id: generate_timestamp
run: echo "::set-output name=tag_name::v$(date +%s)"
- name: Create release
id: create_release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name || steps.generate_timestamp.outputs.tag_name }}
draft: ${{ github.event_name == 'push' }}
body: ${{ env.notes }}
token: ${{ secrets.GITHUB_TOKEN }}
file: |
release/*

5
.gitignore vendored
View file

@ -4,4 +4,7 @@ dist
/FontPatcher
/fonts
/source/cn/static
__pycache__
__pycache__
.DS_store
release
source/cn

View file

@ -13,7 +13,9 @@ from fontTools.ttLib import TTFont, newTable
from fontTools.merge import Merger
from source.py.utils import (
check_font_patcher,
download_cn_static_fonts,
get_font_forge_bin,
is_ci,
run,
set_font_name,
joinPaths,
@ -341,7 +343,8 @@ class BuildOption:
self.output_dir, "TTF-AutoHint" if config.use_hinted else "TTF"
)
self.cn_static_path = f"{self.src_dir}/cn/static"
self.cn_variable_dir = f"{self.src_dir}/cn"
self.cn_static_dir = f"{self.cn_variable_dir}/static"
self.cn_base_font_dir = None
self.cn_suffix = None
@ -373,6 +376,22 @@ class BuildOption:
self.cn_suffix = self.cn_suffix_compact = "CN"
self.output_cn = joinPaths(self.output_dir, self.cn_suffix_compact)
def should_build_cn(self, config: FontConfig) -> bool:
if not config.cn["enable"] and not config.use_cn_both:
return False
if path.exists(self.cn_static_dir) and listdir(self.cn_static_dir).__len__() == 16:
return True
if not path.exists(self.cn_variable_dir) and listdir(self.cn_variable_dir).__len__() < 1:
if is_ci():
return download_cn_static_fonts(
tag="cn_static",
target_dir=self.cn_static_dir,
github_mirror=self.nerd_font["github_mirror"],
)
print("CN varaible fonts does not exist. Skip CN build.")
return False
return True
def has_cache(self) -> bool:
return (
self.__check_cache_dir(self.output_variable, count=2)
@ -660,7 +679,7 @@ def build_cn(f: str, font_config: FontConfig, build_option: BuildOption):
[
joinPaths(build_option.cn_base_font_dir, f),
joinPaths(
build_option.cn_static_path, f"MapleMonoCN-{style_compact_cn}.ttf"
build_option.cn_static_dir, f"MapleMonoCN-{style_compact_cn}.ttf"
),
]
)
@ -743,6 +762,7 @@ def run_build(pool_size: int, fn: Callable, dir: str):
for f in listdir(dir):
fn(f)
def main():
check_ftcli()
parsed_args = parse_args()
@ -847,20 +867,17 @@ def main():
# ==================================== build CN =======================================
# =========================================================================================
if font_config.cn["enable"] and path.exists(f"{build_option.src_dir}/cn"):
if (
not path.exists(build_option.cn_static_path)
or font_config.cn["clean_cache"]
):
if build_option.should_build_cn(font_config):
if not path.exists(build_option.cn_static_dir) or font_config.cn["clean_cache"]:
print("=========================================")
print("Instantiating CN Base font, be patient...")
print("=========================================")
run(
f"ftcli converter vf2i {build_option.src_dir}/cn -out {build_option.cn_static_path}"
f"ftcli converter vf2i {build_option.cn_variable_dir} -out {build_option.cn_static_dir}"
)
run(f"ftcli ttf fix-contours {build_option.cn_static_path}")
run(f"ftcli ttf remove-overlaps {build_option.cn_static_path}")
run(f"ftcli utils del-table -t kern -t GPOS {build_option.cn_static_path}")
run(f"ftcli ttf fix-contours {build_option.cn_static_dir}")
run(f"ftcli ttf remove-overlaps {build_option.cn_static_dir}")
run(f"ftcli utils del-table -t kern -t GPOS {build_option.cn_static_dir}")
def _build_cn():
print(
@ -969,7 +986,9 @@ def main():
target_parent_dir_path=archieve_dir,
)
with open(
joinPaths(archieve_dir, f"{font_config.family_name_compact}-{f}.sha256"),
joinPaths(
archieve_dir, f"{font_config.family_name_compact}-{f}.sha256"
),
"w",
encoding="utf-8",
) as hash_file:

View file

@ -38,7 +38,7 @@
"extra_args": []
},
"cn": {
"enable": true,
"enable": false,
"with_nerd_font": true,
"fix_meta_table": true,
"clean_cache": false,

70
release.py Normal file
View file

@ -0,0 +1,70 @@
from os import listdir, mkdir, path
from shutil import copytree, move, rmtree
import subprocess
from source.py.utils import joinPaths
output_base = "fonts"
output_release = "release"
def move_and_log(file_path: str, target_path: str):
print(f"Move {file_path} -> {target_path}")
move(file_path, target_path)
def build(normal: bool, hinted: bool, cache: bool = False):
args = [
"python",
"build.py",
"--archieve",
"--cn-both",
]
if cache:
args.append("--cache")
if normal:
args.append("--normal")
if hinted:
args.append("--hinted")
else:
args.append("--no-hinted")
print(" ".join(args))
subprocess.run(args)
build_archieve_dir = f"{output_base}/archieve"
for file_name in listdir(build_archieve_dir):
file_path = joinPaths(build_archieve_dir, file_name)
if path.isfile(file_path):
if not hinted:
name, ext = path.splitext(file_name)
file_name = f"{name}-unhinted{ext}"
move_and_log(file_path, joinPaths(output_release, file_name))
# clear old releases
rmtree(output_base, ignore_errors=True)
mkdir(output_base)
rmtree(output_release, ignore_errors=True)
mkdir(output_release)
# build all formats
build(normal=True, hinted=True)
build(normal=True, hinted=False, cache=True)
build(normal=False, hinted=True)
build(normal=False, hinted=False, cache=True)
# copy woff2 to root
rmtree("woff2", ignore_errors=True)
copytree(f"{output_base}/woff2", "woff2")
print("Copy woff2 to root")
subprocess.run(f"ftcli converter ft2wf -out woff2/var -f woff2 {output_base}/variable")
target_dir = "website/public-dev/fonts"
rmtree(target_dir, ignore_errors=True)
copytree("woff2/var", target_dir)

View file

@ -38,7 +38,7 @@
"extra_args": []
},
"cn": {
"enable": true,
"enable": false,
"with_nerd_font": true,
"fix_meta_table": true,
"clean_cache": false,

View file

@ -50,9 +50,58 @@ def get_font_forge_bin():
return LINUX_FONTFORGE_PATH
def is_ci():
ci_envs = [
"JENKINS_HOME",
"TRAVIS",
"CIRCLECI",
"GITHUB_ACTIONS",
"GITLAB_CI",
"TF_BUILD",
]
for env in ci_envs:
if environ.get(env):
return True
return False
def parse_github_mirror(github_mirror: str) -> str:
github = environ.get("GITHUB") # custom github mirror, for CN users
if not github:
github = github_mirror
return f"https://{github}"
def download_zip_and_extract(
name: str, url: str, zip_path: str, output_dir: str, remove_zip: bool = True
) -> bool:
try:
if not path.exists(zip_path):
try:
print(f"NerdFont Patcher does not exist, download from {url}")
with urlopen(url) as response, open(zip_path, "wb") as out_file:
shutil.copyfileobj(response, out_file)
except Exception as e:
print(
f"\nFail to download {name}. Please download it manually from {url}, then put downloaded file into project's root and run this script again. \n Error: {e}"
)
return False
with ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall(output_dir)
if remove_zip:
remove(zip_path)
return True
except Exception as e:
print(f"Download zip and extract failed, error: {e}")
return False
def check_font_patcher(version: str, github_mirror: str = "github.com") -> bool:
if path.exists("FontPatcher"):
with open("FontPatcher/font-patcher", "r", encoding="utf-8") as f:
target_dir = "FontPatcher"
if path.exists(target_dir):
with open(f"{target_dir}/font-patcher", "r", encoding="utf-8") as f:
if f"# Nerd Fonts Version: {version}" in f.read():
return True
else:
@ -60,22 +109,17 @@ def check_font_patcher(version: str, github_mirror: str = "github.com") -> bool:
shutil.rmtree("FontPatcher", ignore_errors=True)
zip_path = "FontPatcher.zip"
if not path.exists(zip_path):
github = environ.get("GITHUB") # custom github mirror, for CN users
if not github:
github = github_mirror
url = f"https://{github}/ryanoasis/nerd-fonts/releases/download/v{version}/FontPatcher.zip"
try:
print(f"NerdFont Patcher does not exist, download from {url}")
with urlopen(url) as response, open(zip_path, "wb") as out_file:
shutil.copyfileobj(response, out_file)
except Exception as e:
print(
f"\nFail to download NerdFont Patcher. Please download it manually from {url}, then put downloaded 'FontPatcher.zip' into project's root and run this script again. \n Error: {e}"
)
exit(1)
url = f"{parse_github_mirror(github_mirror)}/ryanoasis/nerd-fonts/releases/download/v{version}/{zip_path}"
return download_zip_and_extract(
name="Nerd Font Patcher", url=url, zip_path=zip_path, output_dir=target_dir
)
with ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall("FontPatcher")
remove(zip_path)
return True
def download_cn_static_fonts(
tag: str, target_dir: str, github_mirror: str = "github.com"
) -> bool:
url = f"{parse_github_mirror(github_mirror)}/subframe7536/maple-font/releases/download/{tag}/cn-base.zip"
zip_path = "cn-base-static.zip"
return download_zip_and_extract(
name="Nerd Font Patcher", url=url, zip_path=zip_path, output_dir=target_dir
)