To preserve the synchronization of source files, the google/fonts repository doesn’t host font sources —only font binaries.
“Upstream” is the further we can go to the original files; since google/fonts is only hosting a copy of the font files —pulled from that original designer’s repository— we can say that google/fonts is somewhere down the stream. The API processes that file and serves a subsetted version, but the original font file can be found in the downloadable zip file from the platform.
None of these three instances are directly linked, though. At least three persons, at each instance, will interact with that font file before it is available on fonts.google.com. Let’s go briefly through the process step by step:
The font doesn’t go directly to the live API; it has to travel through intermediate servers, where it will be reviewed and validated again to ensure the API is showing the font correctly.
This graphic shows how a project travels from the google/fonts GitHub repository to fonts.google.com.
dev-sandbox
server. Each merged PR will appear a few or more minutes later on the dev-sandbox
(if it works correctly).dev-sandbox
, we can add them to the to_sandbox.txt list. The API engineers then push the list of families in sandbox
.sandbox
, and if that is the case, they can add them to the to_production.txt list. The API engineers can then push the families onto the fonts.google.com, available to the public.This process takes 2 to 4 weeks, because the push to sandbox or production only happens in alternance once a week — if not less.
You see the importance of the issues and the pull requests (PR) in the graphic. We try to keep connected the issue and the PR that fixes it, but we do track them on different boards. The Google Fonts board tracks the issues (that are our link to the upstream repos), and the Traffic Jam board tracks the PRs (that are our link to the servers and the API).
Finally, it is important to note that while the API is the primary space for the users, the google/fonts repo is the primary space of the onboarders, and the upstream font repo is the primary space of the author. Since it is impossible to satisfy everyone in all spaces, these priorities are to be taken seriously into consideration when facing a problem:
googlefonts
org.The project board is a way to sort all the font project issues that are contained in it. There is usually no pull request in this project board (they are tracked in the Traffic Jam board). Each tab has specific filters to be able to focus on one aspect of the issue.
Each issue that we intend to care for should be added to the Google Fonts project board, but also to a milestone. This allows us to divide and anticipate the workload in time.
To preserve a clean workflow, the Milestone needs to be assessed at the end of the quarter so we can move the still-open issues to the next Milestone to be able to close this one.
To have a proper overview of the milestones, the Milestones
tab in the GF board allows to see all open issues (triaged by quarter). We can see all the necessary information linked to an issue: the onboarder assigned, the priority level, the progress status, and the labels.
Issues in the google/fonts
repository are used to define the Google Fonts production pipeline for team members, collaborating designers, and for users to report bugs or suggest new submission ideas.
For the project manager and the onboarder, the bridge between what happens upstream and what is reported downstream is made through the issues. In order to track the project appropriately, the issue must contain a certain number of information, and for that, we use the GitHub API extensively.
What do we mean by font project? “Adding a new family”, and “upgrading a family” are font projects. We consider the project completed once that version of the font hits production. Upgrading a font after its release on fonts.google.com would be considered as a new project and therefore a new issue for it is required.
Issues can be used to communicate with the designer and should be updated regularly with a comment about the progress status. It is recommended to use the template issues in order to have all the information we need to start a project.
We want one issue per font project because we allow a PR to change one font directory only. Therefore, the issue can be closed by the PR that makes these changes. As we say above, it doesn’t mean that the project is completed, though. It happens that we see a problem in sandbox; in this case, we re-open the issue to fix it with another PR. Therefore several PRs can be linked to the same issue.
All issues should be labeled at least with a primary category label (start with I
). These labels are grey and inform about the type of project, typically: I New font, I Font upgrade, I Font bug.
The secondary category labels (start with II
) are green and allow to add mandatory additional information.
Every new fonts issues should be labelled with a light-green label:
When the primary language supported is a complex script (usually not Latin-Cyrillic-Greek), a dark-green label must inform us about it. Indeed, the world languages need special care for the API to display the script appropriately, they also need onboarders with expertise to take care of them. We divided them into script categories:
We also use dark-green labels to precise certain conditions/technologies that require special attention such as:
The teritary category labels (start with III
) are orange and allow to know more about the type of upgrade. Every Font upgrade issue should have an orange label:
When in doubt, call on someone else with these blue labels:
The progress status exists through the Project Board. Like the Priorities, they are not labels but a proper custom field displayed as a column in the project boards. It is possible to add more custom fields (and also modify them) in one project board’s settings.
Manual status:
Automatic status:
The progress status from In progress to PR GF is set manually by the onboarder managing that project. The ones from In Dev to Live should be automated thanks to gftools push module
(see below for more information).
This is how to set the progress status manually:
The original designer should be tagged when possible.
The issue becomes then the place for the designer to update us about their progress and notify onboarders that the font is ready for review. If they have a problem, they can also report it there and link to a specific issue; this way we have a good track of the different events that block us from onboarding a font.
The upstream repo must be linked to the issue.
Sometimes, multiple sources of a font can exist on GitHub, or a source may be difficult to find. To make life easier for onboarders, a link to the source repo must be included. We try to ensure that the upstream is the original source of the font (typically, the repo was made by the font’s designer), and not using some fork as a source. When an author is unresponding after giving consent to collaborate with Google Font, we may need to fork the project in the googlefonts repository so we can maintain it ourselves.
The Closed issues
tab allows to follow the status of each project once they have been merged and send to the dev-sandbox
. Their status are: In Dev, In Sanbox or Live. If an issue occurs before it gets live, then the issue should be re-open and amended with a new PR.
The Submissions to review
tab is filtered by the submission label. The submission issues are typically divided into two milestones: the “submission to review” and the “icebox” milestones.
When an issue is placed in the “icebox” milestone, it is the responsibility of the manager to close this issue “as not planned” or keep it open indefinitely.
Maintaining the repository means answering issues opened by users. Most of them are submissions to review or font bugs. Anything related to te API that seems important should be transferred to the on-call team through the dedicated group chat.
This is how we proceed once a new font is proposed through the issue tracker:
Sometimes commercial fonts are submitted, or some user are spaming the repo: in this case we simply close the issue “as not planned” with a small cordial comment explaining why we consider this as spam.
Sometimes the font is clearly not professional and needs several month of practice and work by the designer: in that case we close the issue “as not planned” with a small paragraph with suggestion on how to improve the quality of the font —in addition of links to tutorial or some relevant chapters sof the GF guide.
Such as sources, description in the README.md, etc., we simply ask the designer to provide those elements so we can review the font properly.
An onboarder should be assigned to give a review. That review is then submitted at the “Pipeline meeting” to a collective vote. The final answer should be given in comment of the submission issue.
If the font is rejected: we close it “as not planned” with an explanation, or we move it the “icebox” milestone for later. In case of rejection we must change the status of the issue from to do as won't happen or blocked.
If the font is accepted: we assign an onboarder, a milestone, change the Submission label to the Accepted label. We also notify the designer in comment of the issue with a proposed timeline such as: “depending on the pipeline, we may be able to onboard this font next quarter, please make sure you receive notifications etc.”.
Some labels have been created to quickly see what’s blocking the issue, they are red and starts with --
:
Keeping issues unecessarily opened have several impacts:
We can therefore consider that leaving issues unecessarily opened is a proper mistreatment of the users, the team members and the pipeline.
So when to close an issue?
If an issue gets labelled won't happen, it is more likely that it is for the best to close it to relieve the issue tracker from open issues. It can also be removed from the GF Project Board.
If the issue is labelled blocked, it must be decided if an effort will be made to unblock it or not. If not, closing this issue is also for the best.
The Onboarders
tabs are the special project management spaces for each onboarder individually. They can see what is assigned to them, and update the status of each issue by dragging and dropping the issue from a column to another. They are also asked to update regularly the status of project in comment of the related issue.
The traffic Jam project board is the space where we track, sort and filter the Pull Requests. We use it to check in which server and in which push list a certain version of the family stands.
The first tab would group the PRs by Servers
and sort them by Lists
. It is particularly useful at the moment of preparing/generating the push lists. You can see everything that is in the dev-sandbox and decide to block and add to a list.
The second tab would group the PRs by Lists
and sort them by Labels
. It is particularly useful at the moment of checking the fonts in the servers. You can see everything that belonged to a list by type of project and decide to block or to upgrade list.
The status
column is updated thanks to the gftools push module
. It checks the servers against the font families and output which server displays which version. The lists
column on the other hand is updated manually.
no list tag
: these PRs were merged recently and not added to a list yet. You can add them the tag to_sandbox at the moment of preparing the lists, or blocked if something is wrong after merging.whatever
/ blocked: something is wrong and the PR should be amended to be able to get on a list. A comment should be written in the PR or the linked issue, and the issue re-open. The list of blocked project should be assessed regularly with a will of unblocking them.Every new PRs will be automatically added to the Traffic Jam board, except for the ones with the label Tool/Workflow/Repo.
Font files should always be packaged with Packager. The first comment and the title are generated by the tool and it ensures the font has been packaged by the Packager and not manually.
Additional information can be added in the comment or the next to help the review. For example if something is expected to appear in the diff, it can spare time to the reviewer to know about it in advance. Eg: https://github.com/google/fonts/pull/6820#issuecomment-1741983529.
Like issues, the PR must have labels to help understand the project and communicate within the team. These labels should be set by the onboarder onboarding the font.
All PRs should be labelled at least with a primary category label (startwith I
). These labels are grey and inform about the type of project, typically:
The secondary category labels (start with II
) are dark green and allow to add mandatory additional informations.
When the primary language supported is a complex script (usually not Latin-Cyrillic-Greek), a dark-green label must inform us about it. This way the person checking the font in sandbox can make sure Latin is not displayed, but the actual primary script of the font:
We also use a dark-green labels to precise certain conditions/technologies that requires special attention such as:
The teritary category labels (start with III
) are orange and allow to know more about the type of upgrade. Every Font upgrade issue should have an orange label:
The policy is that no one should merge their own PRs. Therefore a reviewer should be assigned to approve the PR before merging. They will make sure the PR is conform to the requirements described in this Guide and will report any issue with the PR.
They either approve and merge or ask for changes —and signal that with labels.
If something is wrong with the PR, the red labels are used:
lang
and glyphsets
below).glyphsets
subtree), or because the sample text displays a glyph it shouldn’t (textproto in lang
subtree).When in doubt, the reviewer can call on someone else or ask for more details to the onboarder with these blue labels:
This is the section of the PR panel where we link an issue. Sometimes it doesn’t work, in that case “closing” words can be used in the first comment. For example, “Resolves #465”.
A Pull Request linked to an issue will automatically close the issue when the PR gets merged. Indeed we consider that when a project is part of the main branch of the gitub repo, the issue is not actively worked on anymore by the onboarder. Except if there is a problem in sandbox (in which case we would re-open the issue), the family will end up in the API and this is not something to track along with the issues to do and in progress. As a reminder, this repository is first and foremost the workspace of the onboarders, it is not meant to give hint on releases to users.
A new PR will trigger the CI and output a fontbakery report in comment of the PR. Ideally, there should not be any FAILs, and each WARN should be also checked to decide if they are unrelevant.
It is also required to justify any fontbakery FAILS that should be ignored.
Fontbakery has already some exception lists when it comes to RFN and abbreviated font names and camel-cased font name. If the family name of the font is added there, then the FAIL should not yeld anymore (once next version of fontbakery released).
You can find the dowloadable artifacts under checks > Google Fonts QA > qa
. This zip file consist in proof and diff images to ensure also a QA by a human eyes.
Check new fonts with the proof reports:
Check updated thanks to the diff reports:
If an important regression is introduced, we should decide to either accept it and merge, or not accept it. For the latter the designer can be asked to roll out the change, or we can eventually onboard the font under a new family name.
Check even if there is no FAILs reported by font bakery.
display_name
fieldopsz
axis)ARTICLE.en_us.html or DESCRIPTION.en_us.html
To check even if no FAILs reported by font bakery.
upstream.yaml
Check the copyright string looks correct.
As said above, google/fonts repository content is directly linked to the dev-sandbox replicating fonts.google.com. Every week an engineer from the Fonts team, on call for two weeks, will be responsible to push the changes introduced into the dev-sandbox towards the sandbox server, and the changes introduced into the sandbox towards the actual live server.
An onboarder on call is in charge of notifying the engineer on call about the changed paths in the directory using what we call a “push list”. These are text files directly used by an internal script to push the changes.
The to_sandbox.txt file lists the path that are ready to go to the sandbox servers.
The to_production.txt file lists the path that are ready to go to the live server after being validating in sandbox.
The to_delists.txt file lists all the files that we want to revome from the repository and that should not be displayed on fonts.google.com anymore. This file is not linked to any script though, completing it demands a manual intervention from the on call engineer.
A list looks like that:
# New
ofl/notosanskawi # https://github.com/google/fonts/pull/6469
ofl/playpensans # https://github.com/google/fonts/pull/6746
# Upgrade
ofl/biorhyme # https://github.com/google/fonts/pull/6737
ofl/notosanscaucasianalbanian # https://github.com/google/fonts/pull/5281
ofl/notosanselymaic # https://github.com/google/fonts/pull/5283
# Metadata / Description / License
ofl/balsamiqsans # https://github.com/google/fonts/pull/6805
ofl/redrose # https://github.com/google/fonts/pull/6805
# Axis Registry
axisregistry/ar_retinal_resolution.textproto # https://github.com/google/fonts/pull/6663
# Lang
lang/languages/ae_Avst.textproto # https://github.com/google/fonts/pull/6573
lang/languages/af_Latn.textproto # https://github.com/google/fonts/pull/6468
Two tools are needed to prepare these lists.
The push module is used to organise the Traffic Jam board, ie. update the PR status and tag the proper list in order to prepare the generation of the push lists text files. Everything that the push module does, you can do it manually from the Traffic Jam board —but it take ages.
gftools gen-push-lists
scriptA script from gftool is used to generate the push lists text files. Make sure your main branch is in sync and run gftools gen-push-lists path/to/fonts/repo
.
The script reads the Traffic Jam board to collect informations:
to_delist.txt
file.Once the list are created, it should be pushed to google/fonts thanks to a PR on another branch with a label Tool / Workflow / Repo. Chris should be assigned to review the PR and he will merge it.
Once the PR updating the list is merged, the oncall team should be notified by message in the group chat.
Most of the time the oncall engineer will push the text list without reading it, and they will push the entire font directory wether the change concern only one file or not.
For example pushing a change to a article, description, metadata or license file will result in re-pushing the font family. This can become tricky in the following situations:
METADATA.pb
from these families were manually made and are containing errors such as an inconsistent family name with the font name key in METADATA.pb
(for eg. an inconsistency such as FontName
and Font Name
would result in pushing a new family into the API). So even when only modifying a non-font file, the fontbakery report should be read carefully.It is needed to check each pushed family to make sure that indeed the change was pushed, that it is displayed correctly, and to double-check check previous QA before sending to prod. Most problems could be avoided with a thorough QA before merging, but errors can always happen and the API has the advantage to reflect data errors visually.
The live server can be checked very briefly after a prod push. It is a good opportunity to select the new fonts and the important upgrades to create a “share” link with the stakeholders and the user community on tweeter.
Example:
🐔 Google font news!
🐣 New fonts this month
Borel, Dai Banna SIL, Handjet, Lisu Bosa, Narnoor, Noto Sans Vithkuqi, Noto Serif Vithkuqi, REM, Noto Sans Cypro Minoan, Noto Serif Khitan Small Script, Tektur, Victor Mono
https://fonts.google.com/share?selection.family=Borel|Dai+Banna+SIL|Handjet|Lisu+Bosa|Narnoor|Noto+Sans+Vithkuqi|Noto+Serif+Vithkuqi|REM|Noto+Sans+Cypro+Minoan|Noto+Serif+Khitan+Small+Script|Tektur|Victor+Mono
🐥 Upgraded fonts this month
DM Sans, Joan, Sen, Figtree, Signika, Nuosu SIL, Bangers
https://fonts.google.com/share?selection.family=DM+Sans|Joan|Sen|Figtree|Signika|Nuosu+SIL|Bangers
Above we noted that the API could display the font incorrectly. This is because the Google Fonts API doesn’t rely uniquely on the google/fonts repository.
These are the thee subtrees on which an onboarder can interact and what they allow the API to do:
The lang repo contains textprotos to define script, regions and languages.
The scripts directories contains textprotos which two keys, a script name to a script ID.
Eg. scripts/Arab.textproto
id: "Arab"
name: "Arabic"
This ID can be reflected in the font’s METADATA.pb
, to indicate the API to display the font’s “primary script” over all the other available scripts contained in the font.
Eg. primary_script: "Arab"
The languages directory contains textprotos which define several key entries for a specific language, but also the sample texts displayed in the specimen page. Each defined language uses the script ID to know to which script this language is linked to.
Eg. languages/arz_Arab.textproto
id: "arz_Arab"
language: "arz"
script: "Arab"
name: "Egyptian Arabic"
population: 66639360
region: "EG"
sample_text {
masthead_full: "الإع"
masthead_partial: "نم"
styles: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية"
tester: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم"
poster_sm: "الإعلان العالمي"
poster_md: "الإعلان"
poster_lg: "الإعلان"
specimen_48: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني"
specimen_36: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية."
specimen_32: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية."
specimen_21: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية.الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية."
specimen_16: "الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية.الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية.الإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية.\nالإعلان العالمي لحقوق الإنسان، المادة الأولانية البني أدمين كلهم مولودين حرين ومتساويين في الكرامة والحقوق. إتوهبلهم العقل والضمير، والمفروض يعاملوا بعض بروح الأخوية."
}
Therefore if a font’s METADATA.pb
has a defined primary script key, it should be possible to access all the language specific to that script in the specimen page of the API.
It happens that a font contains only glyphs specific to a certain language of a script, and not all the languages of that script. It is the case for the Noto collection and some fonts from SIL. In this case, the languages are also defined in the font’s METADATA.pb
to avoid displaying tofu on the specimen page and prevent users from selecting a language that is not supported by the font.
Eg. languages: "arz_Arab" # Egyptian Arabic
The important keys in the textprotos of the regions directory are the region ID
—which links the region to a language— and the region_group
which is mirrored in the continent selection menu on the specimen page.
Eg. regions/EG.textproto
id: "EG"
name: "Egypt"
population: 104124000
region_group: "Africa"
It happens that some languages don’t have any sample text defined. We can either update the language textproto, or add a sample_text
entry in METADATA.pb
which would override any existing sample text. This absence of sample text for a script without the addition of a custom one in METADATA.pb
would result in an empty specimen page.
Subsets definition for the API
The subsets
keys in a font’s METADATA.pb
refers to the nam
files in Lib/encoding/glyphsets. These files determine how a font will be subsetted, meaning that if a glyph is not in a defined subset, it won’t be accessible in the API. In addition, if a certain subset is missing from METADATA.pb
, all the glyphs of that subset will be removed from the font served by the API.
These subsets are visually reflected on the UI through the filters, where we select “languages” (which are actually scripts).
The problem is that this database is a copy of the internal one used by the API. Therefore it can be easy to not be in sync anymore. So when we notice an important glyph is missing from a nam
file, we follow this process:
Glyphsets for designers
The glyphsets in GF_Glyphsets
are meant for the designers and specific glyphset expectation from Google Fonts, but it has no influence on the API, nor METADATA.pb
.
We need to be careful when upgrading them though, cause we don’t want to have a inconsistent requirement. Also the Noto fonts (non-LCGD) include automatically GF_Latin_Core
; if a glyphs gets removed from the character set definition, it will be removed from the next update of the Noto families.
The glyphsets
(both the API subset and the designers minimal sets) is used as a module in different QA tools. Among other checks, Fontbakery checks that all LCG fonts support at least GF_Latin_Core
glyphset, and all complex scripts support at least GF_Latin_Kernel
.
The Axis Registry defines:
The supported axes by Google Fonts API.
The snippet-information about each axis.
The instances served by API.
The static instances that will be provided in the zip file:
You know if an axis was correctly implemented by checking fonts.google.com/variablefonts.
Find everything you need to know about the Axis Registry and the registration protocol in this chapter.
We need regular releases because these subtrees are also modules used by gftools and fontbakery. For example, if Fontbakery fails a font because it doesn’t display all glyphs of the language’s sample text (although the codepoint is part of a nam file) then it can mean that there is no recent release of the glyphsets package.
Changes to the Lang
and Axis Registry
repo need to be pulled into the google/fonts
repo. The ideal would make a pull subtree at each release of the module to make the sync easier to verify.
From your local google/fonts
clone, run:
git subtree pull --prefix=axisregistry https://github.com/googlefonts/axisregistry.git main
or
git subtree pull --prefix=lang https://github.com/googlefonts/lang.git main
Once the main branch from the submodule is pulled, you will need to commit and push in a new branch upstream. Label the PR appropriately Axis Registry or Lang so it gets sorted correclty in the Traffic Jam board.
Important: When merging a pull request in the google/fonts repo which contains a subtree pull, merge it using the “Create a merge commit” button and not the “Squash and merge” button. If we squash and merge, the subtree’s history is squashed into a single commit which will cause merge conflicts for the next person who has to do a subtree pull.