Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(developer): kmc-package support remote fonts and files 🐻 #12667

Conversation

mcdurdin
Copy link
Member

@mcdurdin mcdurdin commented Nov 13, 2024

Note: epic/shared-fonts is brought to you by the Share Bear 🐻

The concept here is that the 'Name' property for a file can now be a remote stable reference to a file stored on GitHub, rather than a local file. Additionally, we add a 'Source' property that may change over time, so we can be notified of changes to files.

File.Source

The Name element may be either a local filename, or a GitHub raw permanent
URL, conforming to the pattern:

<Name>https://github.com/{owner}/{repo}/raw/{commit}/[path/]{filename}</Name>

We will add a Source element to .kps Package.Files.File. We would support
several sources:

  • local - local file system
  • flo - fonts.languagetechnology.org
  • github
  • noto - in the future

local: <Source> element is omitted

This is the same as previous versions. <Name> references a file on the local
filesystem. Note that <Source> is optional for other sources as well; the
absence of the element does not mean local -- that is determined by pattern
match.

<File>
  <Name>[relative-or-absolute-path/]{filename}</Name>
</File>

Example

<File>
  <Name>Alkalami-Regular.ttf</Name>
</File>
<File>
  <Name>../../shared/fonts/Alkalami-Regular.ttf</Name>
</File>

fonts.languagetechnology.org flo:<id>

References a font by ID from fonts.languagetechnology.org. The resource must be
resolved to a permanent URL. If the <Name> field is omitted, the compiler will
return an error, reporting the URL it discovers, so the author should be able to
paste that URL in.

If the URL in the <Name> field differs from the resolved URL given by FLO,
then a hint will be issued by the compiler, allowing the author to easily update
to a new version of the resource at their convenience. It is anticipated that
this may be scripted on the keyboards repository in the future.

<File>
  <Name>https://github.com/{owner}/{repo}/raw/{commit}/[path/]{filename}</Name>
  <Source>flo:{id}</Source>
</File>

Example

<File>
  <Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
  <Source>flo:alkalami</Source>
</File>

GitHub

A reference to a resource located on GitHub. The resource must be resolved to a
permanent URL. If the <Name> field is omitted, the compiler will return an
error, reporting the URL it discovers, so the author should be able to paste
that URL in.

If the URL in the <Name> field differs from the resolved URL from the
<Source> element, then a hint will be issued by the compiler, allowing the
author to easily update to a new version of the resource at their convenience.
It is anticipated that this may be scripted on the keyboards repository in the
future.

<File>
  <Name>https://github.com/{owner}/{repo}/raw/{commit}/[path/]{filename}</Name>
  <Source>https://github.com/{owner}/{repo}/(raw|blob)/([refs/heads/]{branch}|[refs/tags/]{tag}|{commit})/[path/]{filename}</Source>
</File>

Examples

A GitHub raw reference, from a commit, resolves identically.

<File>
  <Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
  <Source>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>

A GitHub raw reference, from a branch, with refs/heads/ excluded:

<File>
  <Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
  <Source>https://github.com/silnrsi/fonts/raw/main/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>

A GitHub raw reference, from a branch, with refs/heads/ included:

<File>
  <Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
  <Source>https://github.com/silnrsi/fonts/raw/refs/heads/main/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>

A GitHub blob reference, as copied from a URL in GitHub:

<File>
  <Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
  <Source>https://github.com/silnrsi/fonts/blob/main/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>

noto

In a future version.

Additional file format notes

At the same time CopyLocation, Description, and FileType elements should
be deprecated and totally ignored by the compiler. They are busydata.

Future sources could be considered, e.g. noto. We don't want to allow arbitrary URLs, both for stability and for security reasons.


This change is entirely compiler-side, so we don't need to make any changes to apps, and so packages will be backwardly compatible.

Things to do

--> moved to #11236

Replaces: #12336
Fixes: #11236

@keymanapp-test-bot skip

The concept here is that the 'Name' property for a file can now be a
remote reference, rather than a local file. There are two supported
formats in this commit:

* GitHub: This is a cutdown version of a plain github.com URL, and must
  match this exact format:

  ```
  github:<owner>/<repo>/raw/<hash>/<filepath/filename>
  ```

  This format is mandated in order to ensure that we always have a
  hashed version of a file from the origin. This gives us reproducible
  builds, which avoids churn issues when font files change.

  Example: `github:silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf`
  gets https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf

  An alternative could be to just have `https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf`
  which could be matched with a regex in the same way as the `github`
  prefix, and would avoid the need to munge the input URL. **Discuss!**

* fonts.languagetechnology.org: references just a font identifier. This
  is somewhat broken, because if the source file changes, we don't know
  about it and won't publish an updated version of the package. So this
  needs some more discussion (we could e.g. embed the version number in
  the request, e.g. `flo:[email protected]`). **Discuss!**

  ```
  flo:<family>
  ```

  e.g. `flo:andika` gets
  https://fonts.languagetechnology.org/fonts/sil/andika/Andika-Bold.ttf

Future sources could be considered, e.g. noto. We don't want to allow
arbitrary URLs, both for stability and for security reasons.

This change is entirely compiler-side, so we don't need to make any
changes to apps, and so packages will be backwardly compatible. A lot of
work will need to be done with the Package Editor in TIKE to support
this feature.

Fixes: #11236
@keymanapp-test-bot
Copy link

keymanapp-test-bot bot commented Nov 13, 2024

User Test Results

Test specification and instructions

User tests are not required

@mcdurdin
Copy link
Member Author

mcdurdin commented Nov 13, 2024

Capturing discussion on font versioning. We need to think this through with FLO, caching of fonts, versioning updates.

Marc Durdin
30 minutes ago
in fonts.languagetechnology.org, where is the version data sourced from in the .ttf? I see vast majority of fonts have version #.### format, but three fonts have semantic version patterns: Dukor dukor (1.0.7), suranna (1.0.5) and wakor (4.0.7).
27 replies

Marc Durdin
29 minutes ago
We want to see if we need to re-download a font based on the version metadata provided, and also verify font version

Victor Gaultney
20 minutes ago
Here’s a summary: https://silnrsi.github.io/FDBP/en-US/Versioning.html

Marc Durdin
20 minutes ago
So looks like you are using the name table
4:07
Thanks!

Victor Gaultney
19 minutes ago
Version info is in both head and name
4:08
decimal and string
4:09
The three fonts you mention are not our and are out of step with the common #.### industry pattern

Marc Durdin
17 minutes ago
Yeah
4:10
I just downloaded wakor, and its name record says Version 4.007 so something seems inconsistent there

Victor Gaultney
15 minutes ago
You can try to map the semantic-style to the industry standard using the M.mpp format
4:11
So 4.0.7 is really 4.007
4:12
By definition the head must be #.#

Marc Durdin
13 minutes ago
But that doesn't explain where 4.0.7 is coming from in FLO data -- given the .ttf itself has 4.007 (both tables), shouldn't FLO be using that?
4:14
Suranna-Regular.ttf has Version 1.0.5; .... and head.fontRevision 0x10000 which really doesn't help.

Victor Gaultney
12 minutes ago
That comes from the documentation from the source, not the ttf

Marc Durdin
12 minutes ago
I see. So we cannot reliably automatically check then. That's a shame
4:16
(We are actually not going to support the fonts in question anyway as none of them have a direct URL download, so it's kinda moot, but I just want to make sure we don't paint ourselves into a corner in our use of FLO)
4:16
Wakor has fontRevision of 0x401CB which also doesn't really help!
4:17
I am keen to avoid downloading hundreds of fonts in each build of the keyboards repository, so I want to cache, but if the version metadata is not reliable, that makes it tricky

Victor Gaultney
9 minutes ago
head.fontRevision is not carefully handled by font providers and can be downriught odd

Marc Durdin
8 minutes ago
So name.Version is a safer bet, /^Version (\d+.\d+)/i?

Victor Gaultney
8 minutes ago
That too is not handled consistently - it’s just a string whose format cannot be guaranteed
4:19
There is no ‘safe’ bet here

Marc Durdin
5 minutes ago
I see. I might have to maintain a map of ttf:name.Version:FLO-version -- that would allow me to invalidate stale fonts.
The other half is knowing when a font is updated in FLO data. For that, we are proposing to have a reference such as flo:[email protected] in our data files (we also support other references such as GitHub raw URLs)

Victor Gaultney
2 minutes ago
I think you could use the name string regex as a first step, and if the result is a #.### pattern use it. If it’s not then you can’t compare.

Marc Durdin
1 minute ago
Yep, that's my thinking. And perhaps disallow use of fonts via FLO which don't meet that req. I'm okay with that (fallback is they can download the font and embed it the legacy way, or hassle the font author to up their game)
4:26
... then if the FLO version doesn't match, we'd flag a version update for the containing keyboard package. And that way, we can (semi-)automatically update packages which have updates to fonts (which will make David and Lorna very happy).
Hard part is what to do when we do a build: we may not have all the fonts in cache, which could cause the build to fall over if a version is updated. I need to think this through
Stability is really important for us as we have to check over 1,000 packages in repo which may need updates when a font is updated, plus hundreds of keyboard authors who would also be impacted.
Reproducible keyboard package builds is an essential feature. Versioning of fonts is tricky in this respect (edited)

Victor Gaultney
5 minutes ago
Yes, esp. given that FLO is a snapshot, not a live update, and doesn’t provide non-current versions.

4:33
IOW on a Monday FLO might say Noto 3.400 is current. On Tuesday Google might update Noto to 3.500. But FLO won’t immediately change. So you could have a user who has 3.500, but FLO says 3.400.

@mcdurdin
Copy link
Member Author

Another capture

marc
Monday at 10:28 AM
I have a remote referencing conundrum. kmc-copy and kmc-package are both now able to reference github URIs for content, but they have different patterns for referencing:
kmc-copy has: github:owner/repo:[branch:]path/to/file
kmc-package has: github:owner/repo/raw/git-hash/path/to/file
The purposes are slightly different. kmc-copy is a once off action, so we allow branch references. kmc-package is for data in a .kps file, so should be referencing a permanent URI (hence use of the commit hash).
Options:
We could use a valid https url and match valid patterns, e.g. https://github.com/owner/repo/tree/branch/path/to/file and https://github.com/owner/repo/raw/git-hash/path/to/file , which makes copypasta easier but may make it less obvious why we don't allow certain patterns.
We could use kmc-copy pattern for kmc-package : github:owner/repo:git-hash:path/to/file
We could use kmc-package pattern for kmc-copy : github:owner/repo/branch/path/to/file
Leave them divergent (ugh)
What do y'all think?
4 replies

ross
Monday at 1:47 PM
I think I like option 2. Iike the / used for directory : for different "named" identifiers. Although I realise owner/repo still has a slash
Saved for later • Due 1 hour ago

eberhard
Today at 2:58 PM
I agree that option 1 is probably not the best since it suggests that you can use any URL.
Option 3 allows the user to make fewer changes to the copy/pasted URL, so I'd go with that.

marc
1 hour ago
Opt 3 should probably be: github://raw//<path/to/file> for kmc-package, for consistency (and so we can allow other terms in place of 'raw' in future. This allows a right-click copy URL and just a very minor edit, for kmc-package.
For kmc-copy, I wonder if allowing a paste from GH is actually fine? Which then makes me wonder if a https URL is actually better for kmc-package as well, if we have clear messaging and UI around it. It means users can just open the URL to see what they get, as well.
So I am coming around to option 1.
Which now means we have 3 different answers to my question! Oops.
3:44
The compiler can provide error messages with precise URL formatting. We can even translate githubusercontent.com urls to the original GH url in the IDE

@mcdurdin
Copy link
Member Author

marc
Monday at 10:44 AM
Another remote conundrum. We want to be able to support fonts.languagetechnology.org (FLO) for sourcing fonts for keyboard packages. However, we need to have stability, and FLO font versions and references can change without warning. Two issues:
Reproducible build requirement means we want a static font version -- how can we guarantee this?
We need to bump keyboard version when font version changes, so users get the new font. How can we learn of version changes to fonts? (And can we automate?)
4 replies

davidrowe
Monday at 1:31 PM
Would the commit hash scheme work to get a fixed reference?

marc
Monday at 1:32 PM
yes, commit hash is guaranteed fixed reference
Saved for later • Due 1 hour ago

eberhard
Today at 3:02 PM
Looking at the source of FLO (https://github.com/silnrsi/fonts) I see that the fonts seem to have a fontmanifest.json file that contains a version number, so we could query that repo for changes to fonts.

silnrsi/fonts
Collection of webfonts for internal use
Stars
5
Language
HTML
Added by GitHub

marc
43 minutes ago
Yeah, the FLO data includes a version number, e.g. "version": "1.490", for this font below.
"nokyung": {
"defaults": {
"ttf": "Nokyung-Regular.ttf",
"woff": "Nokyung-Regular.woff"
},
"distributable": true,
"family": "Nokyung",
"familyid": "nokyung",
"files": {
"Nokyung-Bold.ttf": { "axes": { "ital": 0, "wght": 700.0 }, "flourl": "https://fonts.languagetechnology.org/fonts/sil/nokyung/Nokyung-Bold.ttf", "packagepath": "Nokyung-Bold.ttf", "url": "https://github.com/silnrsi/fonts/raw/main/fonts/sil/nokyung/Nokyung-Bold.ttf", "zippath": "Nokyung-1.490-dev-e7e323M/Nokyung-Bold.ttf" },
"Nokyung-Bold.woff": { "axes": { "ital": 0, "wght": 700.0 }, "flourl": "https://fonts.languagetechnology.org/fonts/sil/nokyung/web/Nokyung-Bold.woff", "packagepath": "web/Nokyung-Bold.woff", "url": "https://github.com/silnrsi/fonts/raw/main/fonts/sil/nokyung/web/Nokyung-Bold.woff", "zippath": "Nokyung-1.490-dev-e7e323M/web/Nokyung-Bold.woff" },
"Nokyung-Regular.ttf": { "axes": { "ital": 0, "wght": 400.0 }, "flourl": "https://fonts.languagetechnology.org/fonts/sil/nokyung/Nokyung-Regular.ttf", "packagepath": "Nokyung-Regular.ttf", "url": "https://github.com/silnrsi/fonts/raw/main/fonts/sil/nokyung/Nokyung-Regular.ttf", "zippath": "Nokyung-1.490-dev-e7e323M/Nokyung-Regular.ttf" },
"Nokyung-Regular.woff": { "axes": { "ital": 0, "wght": 400.0 }, "flourl": "https://fonts.languagetechnology.org/fonts/sil/nokyung/web/Nokyung-Regular.woff", "packagepath": "web/Nokyung-Regular.woff", "url": "https://github.com/silnrsi/fonts/raw/main/fonts/sil/nokyung/web/Nokyung-Regular.woff", "zippath": "Nokyung-1.490-dev-e7e323M/web/Nokyung-Regular.woff" }
},
"license": "OFL",
"packageurl": "https://software.sil.org/downloads/r/nokyung/Nokyung-1.490-dev-e7e323M.zip",
"siteurl": "https://software.sil.org/Nokyung/",
"source": "SIL",
"status": "current",
"version": "1.490",
"ziproot": "Nokyung-1.490-dev-e7e323M"
},
Then the only pattern we need is the version, so do we have flo:[email protected] or flo:nokyung/1.490 or something else?

@mcdurdin
Copy link
Member Author

My current thinking on flo: references. Perhaps FLO needs to be a tool in Developer which resolves to a GH stable commit reference, as fonts with a url are all GitHub references in FLO.

If we always use GH, then it will be exceptionally rare to have a stable uri disappear -- only if repos are removed or nasty actions like force pushes are done -- in which case we probably want to deal with it anyway.

Additional feature: we can keep the original flo references as well, and use that to provide a hint message to flag when font updates are available, but do not attempt to use it directly.

…at/developer/kmc-package-source-files-from-remotes-2
@darcywong00 darcywong00 modified the milestones: A18S15, A18S16 Nov 24, 2024
Base automatically changed from fix/developer/package-compiler-box-info-fields to master November 25, 2024 01:06
…es-from-remotes-2'

of https://github.com/keymanapp/keyman into feat/developer/kmc-package-source-files-from-remotes-2
After the reorganization of the package compiler build, we now need to
make sure all files referenced in the .kps exist in order for the unit
tests to pass.
…matches

As our data files often come from Windows, we need to test if paths are
Windows-style absolute paths, with either path separator (e.g. `C:\...`,
`\path\...`, `\\server\path\...`, `/path/...`). Technically, this means
a posix path that starts with an escaped character would be regarded as
absolute, but IMHO those kinds of shenanigans deserve to result in a
failing build anyway. ;-)
Adds error for unsupported versions, hint for low versions when using
remote references.
@mcdurdin mcdurdin marked this pull request as ready for review December 2, 2024 05:22
@darcywong00 darcywong00 self-assigned this Dec 2, 2024
Copy link
Contributor

@darcywong00 darcywong00 left a comment

Choose a reason for hiding this comment

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

1/3 of the way through. Some questions


## local: `<Source>` element is omitted

This is the same as previous versions. `<Name>` references a file on the local
Copy link
Contributor

Choose a reason for hiding this comment

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

What does "... same as previous versions." refer to?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, this means v7, v12 of the format. I can update.

* github
* noto - in the future

## local: `<Source>` element is omitted
Copy link
Contributor

Choose a reason for hiding this comment

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

This took me a few re-reads of the part about "<Source> element is omitted."
That phrase doesn't appear in the other source headings (flo, github, noto).

Is this conveying for local sources, the <Source> element will always be omitted?
vs other sources the <Source> element can be omitted?


## local: `<Source>` element is omitted

This is the same as previous versions. `<Name>` references a file on the local
Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, this means v7, v12 of the format. I can update.

Comment on lines 54 to 57
This is the same as previous versions. `<Name>` references a file on the local
filesystem. Note that `<Source>` is optional for other sources as well; the
absence of the element does not mean local -- that is determined by pattern
match.
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
This is the same as previous versions. `<Name>` references a file on the local
filesystem. Note that `<Source>` is optional for other sources as well; the
absence of the element does not mean local -- that is determined by pattern
match.
If `<Name>` references a file on the local filesystem, then the
`<Source>` element must be omitted. This matches versions 7.0 and 12.0
of the .kps format for backward compatibility.
Note that `<Source>` is optional for other sources as well; the
absence of the element does not mean local -- that is determined by pattern
match.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note that <Source> is optional for other sources as well; the

See above. Maybe better: "Note that <Source> is optional for some of the other sources as well; the" ?

Copy link
Member

Choose a reason for hiding this comment

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

I'd split out the compatibility part, as it's confusing when mentioning local files (and becomes less important over time by nature, though it's in a prominent location). Using a local filesystem matches 7.0 and 12.0? Or omitting <Source>? Or both?

Perhaps move that sentence to the end:

For backwards compatibility with versions 7.0 and 12.0 of the .kps format, omit the <Source> element.

Comment on lines 45 to 50
We will add a `Source` element to .kps `Package.Files.File`. We would support
several sources:
* local - local file system
* flo - fonts.languagetechnology.org
* github
* noto - in the future
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
We will add a `Source` element to .kps `Package.Files.File`. We would support
several sources:
* local - local file system
* flo - fonts.languagetechnology.org
* github
* noto - in the future
There may a separate `<Source>` element as well, representing the following possible original sources for a file:
* Local file system -- omit the `<Source>` element
* fonts.languagetechnology.org -- `<Source>` should have the pattern `flo:...`
* `https://github.com/...` -- `<Source>` should have a URL to a file stored in GitHub
* `noto:...` - to be supported in the future
The `<Source>` element is always optional; if it is not present, then kmc-package will not check for updated versions of the source file.

Copy link
Contributor

Choose a reason for hiding this comment

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

The <Source> element is always optional; if it is not present, then kmc-package will not check for updated versions of the source file.

The first half of the sentence doesn't make sense to me after looking at the examples. If I want to specify a font from flo it seems I have to use the <Source> element, so it's not optional in that case.

There may a separate <Source> element as well,

I think there's a "be" missing...

Copy link
Contributor

@darcywong00 darcywong00 left a comment

Choose a reason for hiding this comment

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

A beefy PR!

`Keyboard file ${def(o.filename)} was not found. Has it been compiled?`);
static ERROR_KeyboardFileNotFound = SevError | 0x0011;
static Error_KeyboardFileNotFound = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotFound,
`Keyboard file ${def(o.filename)} was not found. Has it been compiled?`);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit to revert whitespace?

<FileVersion>18.0</FileVersion>
</System>
<Info>
<Name URL="">error_font_file_could_not_be_downloaded</Name>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<Name URL="">error_font_file_could_not_be_downloaded</Name>
<Name URL="">hint_source_file_has_changed</Name>

<Description>File splash.gif</Description>
<CopyLocation>0</CopyLocation>
<FileType>.gif</FileType>
</File>
</Files>
Copy link
Contributor

Choose a reason for hiding this comment

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

what keyboard content file is not found for this test fixture?

Copy link
Member Author

Choose a reason for hiding this comment

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

khmer_angkor.kmx is missing, have added a comment

</File>
<!-- warn_font_in_flo_does_not_have_default_ttf: this uses a mismatching font, but
we won't get that far. This handles the scenario where FLO previously lists a font
but no ttf is available -->
Copy link
Contributor

Choose a reason for hiding this comment

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

In the future when FLO makes updates, are there scenarios that could that impact some text fixture assumptions?

For example, a listed font that previously doesn't have ttf changes to include one?

Copy link
Member Author

Choose a reason for hiding this comment

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

The fixtures are all stored offline to avoid this arising (and have performant and consistent tests), see https://github.com/keymanapp/keyman/blob/c6baddad1237e09a6cdc14ccc64dd6d63253d63a/developer/src/kmc-package/test/fixtures/online/fonts.languagetechnology.org/%23families.json for example

@mcdurdin mcdurdin changed the base branch from master to epic/shared-fonts December 4, 2024 07:41
@mcdurdin mcdurdin requested a review from ermshiperete December 4, 2024 07:42
@mcdurdin
Copy link
Member Author

mcdurdin commented Dec 4, 2024

@ermshiperete and @srl295 would love your feedback on this before merge, particularly considering systemic issues (docs are included in the PR; most of the files are fixtures for tests). See also the TODO list in #11236 for some things I am already thinking about.

@mcdurdin mcdurdin changed the title feat(developer): kmc-package support remote fonts and files feat(developer): kmc-package support remote fonts and files 🐻 Dec 4, 2024
@ermshiperete ermshiperete self-assigned this Dec 4, 2024
Comment on lines 54 to 57
This is the same as previous versions. `<Name>` references a file on the local
filesystem. Note that `<Source>` is optional for other sources as well; the
absence of the element does not mean local -- that is determined by pattern
match.
Copy link
Member

Choose a reason for hiding this comment

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

I'd split out the compatibility part, as it's confusing when mentioning local files (and becomes less important over time by nature, though it's in a prominent location). Using a local filesystem matches 7.0 and 12.0? Or omitting <Source>? Or both?

Perhaps move that sentence to the end:

For backwards compatibility with versions 7.0 and 12.0 of the .kps format, omit the <Source> element.

</File>
```

### fonts.languagetechnology.org `flo:<id>`
Copy link
Member

Choose a reason for hiding this comment

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

I think I am not going to bikeshed the URI and schema structure here. These are reasonable, a small set, and unlikely to collide with anything.

```xml
<File>
<Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
<Source>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
Copy link
Member

Choose a reason for hiding this comment

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

is this then a redundant <Source> just for documentation? What's the benefit of the <Source> element here?

Copy link
Member Author

Choose a reason for hiding this comment

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

No benefit here -- it'd just be for documentation. Possibly misleading. Will consider removing this one.

Comment on lines 138 to 148
A GitHub raw reference, from a branch, with refs/heads/ excluded:

```xml
<File>
<Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
<Source>https://github.com/silnrsi/fonts/raw/main/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>
```

A GitHub raw reference, from a branch, with refs/heads/ included:

Copy link
Member

Choose a reason for hiding this comment

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

Not sure refs/head really needs to be called out. High bit for users is that both of these work.

Suggested change
A GitHub raw reference, from a branch, with refs/heads/ excluded:
```xml
<File>
<Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
<Source>https://github.com/silnrsi/fonts/raw/main/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>
```
A GitHub raw reference, from a branch, with refs/heads/ included:
Two examples of GitHub raw references, from a branch, which reference the same file:
```xml
<File>
<Name>https://github.com/silnrsi/fonts/raw/b88c7af5d16681bd137156929ff8baec82526560/fonts/sil/alkalami/Alkalami-Regular.ttf</Name>
<Source>https://github.com/silnrsi/fonts/raw/main/fonts/sil/alkalami/Alkalami-Regular.ttf</Source>
</File>

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I will simplify it but will include example because refs/heads/ is often found in UX in GitHub so helps users to know they can use either.

</File>
```

A GitHub blob reference, as copied from a URL in GitHub:
Copy link
Member

Choose a reason for hiding this comment

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

this could even be merged in with the above too - three examples… 

unless we are really wanting to tutor users on how to construct these?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm trying to be comprehensive on the different supported formats both for our reference as well as users. There's enough ambiguity in the URL formats that I think having all the examples helps. We'll probably only have the basic example for keyboard authors in the upcoming documentation associated with the package editor, but this is the reference doc.

Comment on lines 45 to 50
We will add a `Source` element to .kps `Package.Files.File`. We would support
several sources:
* local - local file system
* flo - fonts.languagetechnology.org
* github
* noto - in the future
Copy link
Contributor

Choose a reason for hiding this comment

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

The <Source> element is always optional; if it is not present, then kmc-package will not check for updated versions of the source file.

The first half of the sentence doesn't make sense to me after looking at the examples. If I want to specify a font from flo it seems I have to use the <Source> element, so it's not optional in that case.

There may a separate <Source> element as well,

I think there's a "be" missing...

Comment on lines 54 to 57
This is the same as previous versions. `<Name>` references a file on the local
filesystem. Note that `<Source>` is optional for other sources as well; the
absence of the element does not mean local -- that is determined by pattern
match.
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that <Source> is optional for other sources as well; the

See above. Maybe better: "Note that <Source> is optional for some of the other sources as well; the" ?


```xml
<File>
<Name>[relative-or-absolute-path/]{filename}</Name>
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's mentioned elsewhere, but would it be good to repeat that absolute paths are not recommended?

### fonts.languagetechnology.org `flo:<id>`

References a font by ID from fonts.languagetechnology.org. The resource must be
resolved to a permanent URL. If the `<Name>` field is omitted, the compiler will
Copy link
Contributor

Choose a reason for hiding this comment

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

"Omitted": To me this sounds like the <Name> field is optional which is confusing since above it talks about <Source> being optional. Would it be better to phrase as:

Suggested change
resolved to a permanent URL. If the `<Name>` field is omitted, the compiler will
resolved to a permanent URL. If the `<Name>` field is missing, the compiler will

?

### GitHub

A reference to a resource located on GitHub. The resource must be resolved to a
permanent URL. If the `<Name>` field is omitted, the compiler will return an
Copy link
Contributor

Choose a reason for hiding this comment

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

see above

return `https://github.com/${matches.groups.name}/${matches.groups.repo}/raw/${commit.sha}/${matches.groups.path}`;
}

async function getFileDataFromGitHub(callbacks: CompilerCallbacks, inputFilename: string, matches: RegExpExecArray): Promise<KmpCompilerFileDataResult> {
Copy link
Contributor

Choose a reason for hiding this comment

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

To keep the order the same as with the previous functions where we define the outer function first and then the function that gets called by the outer function, you could move this function after getFileDataFromRemote. Would make it easier to read when reading the whole file.

@@ -468,49 +478,25 @@ export class KmpCompiler implements KeymanCompiler {

const hasKmpInf = !!data.files.find(file => file.name == KMP_INF_FILENAME);

let failed = false;
data.files.forEach((value) => {
for(const value of data.files) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Out of curiosity: why did you change data.files.forEach to a for loop?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was so I could break out of the loop with return null on line 490 and not have the loop continue.

static HINT_RemoteReferencesShouldBeVersion18Plus = SevHint | 0x0031;
static Hint_RemoteReferencesShouldBeVersion18Plus = (o:{filename: string, kpsVersion: string}) => m(
this.HINT_RemoteReferencesShouldBeVersion18Plus,
`The source package includes a reference to URL '${def(o.filename)}', and the package source version is '${def(o.kpsVersion)}'; the package source version should be at least '18.0'`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe?

Suggested change
`The source package includes a reference to URL '${def(o.filename)}', and the package source version is '${def(o.kpsVersion)}'; the package source version should be at least '18.0'`,
`The source package includes a reference to URL '${def(o.filename)}' but the package source version is '${def(o.kpsVersion)}'; the package source version should be at least '18.0'`,

@@ -4,9 +4,13 @@ import { KeyboardMetadataCollection } from './package-metadata-collector.js';
import { KpsFile, CompilerCallbacks } from '@keymanapp/developer-utils';

export const DEFAULT_KEYBOARD_VERSION = '1.0';
export const MIN_LM_FILEVERSION_KMP_JSON = '12.0';

/** minimum FileVersion for keyboard packages */
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

assert.equal(matches.groups.repo, 'fonts');
assert.equal(matches.groups.hash, 'b88c7af5d16681bd137156929ff8baec82526560');
assert.equal(matches.groups.path, 'fonts/sil/alkalami/Alkalami-Regular.ttf');
const res = await getFileDataEndpoints.getFileDataFromGitHub(callbacks, '', matches);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this get the file from GitHub or is this mocked somehow?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is mocked in the TestCompilerCallbacks class:

class TestCompilerNetAsyncCallbacks implements CompilerNetAsyncCallbacks {
constructor(private basePath: string) {
}
urlToPath(url: string): string {
const p = new URL(url);
return path.join(this.basePath, p.hostname, p.pathname.replaceAll(/[^a-z0-9_.!@#$%() -]/ig, '#'));
}
async fetchBlob(url: string): Promise<Uint8Array> {

It fetches files from test/fixtures/online with munged up paths as can be seen in this PR, e.g. /developer/src/kmc-package/test/fixtures/online/fonts.languagetechnology.org/#fonts#sil#andika#Andika-Regular.ttf. There are some limits to this, e.g. it doesn't support # or ? components of a URL, so it would need more work to do those. It's a bit quick-and-dirty but fits the bill for now.

TestCompilerCallbacks has a mode where it runs online and retrieves and saves fixtures, which we can enable with the TEST_SAVE_FIXTURES env var. See the source for details; this mode makes it easy to build a coherent test that will work with online-sourced data and be reproducible.

@mcdurdin
Copy link
Member Author

mcdurdin commented Dec 5, 2024

I think I have addressed all the comments in the reviews, thanks all!

@mcdurdin mcdurdin merged commit 0faf77a into epic/shared-fonts Dec 5, 2024
5 of 9 checks passed
@mcdurdin mcdurdin deleted the feat/developer/kmc-package-source-files-from-remotes-2 branch December 5, 2024 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

feat(developer): manage shared fonts
4 participants