release and custom build in CI
This commit is contained in:
parent
c1d1d49f8a
commit
dc3fcdae9e
7 changed files with 241 additions and 35 deletions
70
.github/workflows/release.yml
vendored
Normal file
70
.github/workflows/release.yml
vendored
Normal 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/*
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,3 +5,6 @@ dist
|
||||||
/fonts
|
/fonts
|
||||||
/source/cn/static
|
/source/cn/static
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.DS_store
|
||||||
|
release
|
||||||
|
source/cn
|
43
build.py
43
build.py
|
@ -13,7 +13,9 @@ from fontTools.ttLib import TTFont, newTable
|
||||||
from fontTools.merge import Merger
|
from fontTools.merge import Merger
|
||||||
from source.py.utils import (
|
from source.py.utils import (
|
||||||
check_font_patcher,
|
check_font_patcher,
|
||||||
|
download_cn_static_fonts,
|
||||||
get_font_forge_bin,
|
get_font_forge_bin,
|
||||||
|
is_ci,
|
||||||
run,
|
run,
|
||||||
set_font_name,
|
set_font_name,
|
||||||
joinPaths,
|
joinPaths,
|
||||||
|
@ -341,7 +343,8 @@ class BuildOption:
|
||||||
self.output_dir, "TTF-AutoHint" if config.use_hinted else "TTF"
|
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_base_font_dir = None
|
||||||
self.cn_suffix = None
|
self.cn_suffix = None
|
||||||
|
@ -373,6 +376,22 @@ class BuildOption:
|
||||||
self.cn_suffix = self.cn_suffix_compact = "CN"
|
self.cn_suffix = self.cn_suffix_compact = "CN"
|
||||||
self.output_cn = joinPaths(self.output_dir, self.cn_suffix_compact)
|
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:
|
def has_cache(self) -> bool:
|
||||||
return (
|
return (
|
||||||
self.__check_cache_dir(self.output_variable, count=2)
|
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_base_font_dir, f),
|
||||||
joinPaths(
|
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):
|
for f in listdir(dir):
|
||||||
fn(f)
|
fn(f)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
check_ftcli()
|
check_ftcli()
|
||||||
parsed_args = parse_args()
|
parsed_args = parse_args()
|
||||||
|
@ -847,20 +867,17 @@ def main():
|
||||||
# ==================================== build CN =======================================
|
# ==================================== build CN =======================================
|
||||||
# =========================================================================================
|
# =========================================================================================
|
||||||
|
|
||||||
if font_config.cn["enable"] and path.exists(f"{build_option.src_dir}/cn"):
|
if build_option.should_build_cn(font_config):
|
||||||
if (
|
if not path.exists(build_option.cn_static_dir) or font_config.cn["clean_cache"]:
|
||||||
not path.exists(build_option.cn_static_path)
|
|
||||||
or font_config.cn["clean_cache"]
|
|
||||||
):
|
|
||||||
print("=========================================")
|
print("=========================================")
|
||||||
print("Instantiating CN Base font, be patient...")
|
print("Instantiating CN Base font, be patient...")
|
||||||
print("=========================================")
|
print("=========================================")
|
||||||
run(
|
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 fix-contours {build_option.cn_static_dir}")
|
||||||
run(f"ftcli ttf remove-overlaps {build_option.cn_static_path}")
|
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_path}")
|
run(f"ftcli utils del-table -t kern -t GPOS {build_option.cn_static_dir}")
|
||||||
|
|
||||||
def _build_cn():
|
def _build_cn():
|
||||||
print(
|
print(
|
||||||
|
@ -969,7 +986,9 @@ def main():
|
||||||
target_parent_dir_path=archieve_dir,
|
target_parent_dir_path=archieve_dir,
|
||||||
)
|
)
|
||||||
with open(
|
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",
|
"w",
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as hash_file:
|
) as hash_file:
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
"extra_args": []
|
"extra_args": []
|
||||||
},
|
},
|
||||||
"cn": {
|
"cn": {
|
||||||
"enable": true,
|
"enable": false,
|
||||||
"with_nerd_font": true,
|
"with_nerd_font": true,
|
||||||
"fix_meta_table": true,
|
"fix_meta_table": true,
|
||||||
"clean_cache": false,
|
"clean_cache": false,
|
||||||
|
|
70
release.py
Normal file
70
release.py
Normal 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)
|
|
@ -38,7 +38,7 @@
|
||||||
"extra_args": []
|
"extra_args": []
|
||||||
},
|
},
|
||||||
"cn": {
|
"cn": {
|
||||||
"enable": true,
|
"enable": false,
|
||||||
"with_nerd_font": true,
|
"with_nerd_font": true,
|
||||||
"fix_meta_table": true,
|
"fix_meta_table": true,
|
||||||
"clean_cache": false,
|
"clean_cache": false,
|
||||||
|
|
|
@ -50,9 +50,58 @@ def get_font_forge_bin():
|
||||||
return LINUX_FONTFORGE_PATH
|
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:
|
def check_font_patcher(version: str, github_mirror: str = "github.com") -> bool:
|
||||||
if path.exists("FontPatcher"):
|
target_dir = "FontPatcher"
|
||||||
with open("FontPatcher/font-patcher", "r", encoding="utf-8") as f:
|
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():
|
if f"# Nerd Fonts Version: {version}" in f.read():
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
@ -60,22 +109,17 @@ def check_font_patcher(version: str, github_mirror: str = "github.com") -> bool:
|
||||||
shutil.rmtree("FontPatcher", ignore_errors=True)
|
shutil.rmtree("FontPatcher", ignore_errors=True)
|
||||||
|
|
||||||
zip_path = "FontPatcher.zip"
|
zip_path = "FontPatcher.zip"
|
||||||
if not path.exists(zip_path):
|
url = f"{parse_github_mirror(github_mirror)}/ryanoasis/nerd-fonts/releases/download/v{version}/{zip_path}"
|
||||||
github = environ.get("GITHUB") # custom github mirror, for CN users
|
return download_zip_and_extract(
|
||||||
if not github:
|
name="Nerd Font Patcher", url=url, zip_path=zip_path, output_dir=target_dir
|
||||||
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)
|
|
||||||
|
|
||||||
with ZipFile(zip_path, "r") as zip_ref:
|
|
||||||
zip_ref.extractall("FontPatcher")
|
def download_cn_static_fonts(
|
||||||
remove(zip_path)
|
tag: str, target_dir: str, github_mirror: str = "github.com"
|
||||||
return True
|
) -> 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
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue