fontist 3.0.36 → 3.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9740ba780da1a789c85954976ffa0d49227705ee62a24dc266dbc4448e63d84
4
- data.tar.gz: 05dd6af4bb8303c4ee4807c6b0a1b2f24607dffbd448352ba9dc8dd145d924c2
3
+ metadata.gz: 85b1409d941968bcf69c2058ab3e3ffac4138279bf6c262d670206590dd8e557
4
+ data.tar.gz: f025af9d9214504168b1612f7f42f3142afdadb9112e9283022fe92378665000
5
5
  SHA512:
6
- metadata.gz: 8663ae799c949874b9b61a23c382812f6654a379702f93907530ed8602158725ebf83dd9320cde2539b6e757e51c209833efdc167f820f7d4d695983ad0c27f0
7
- data.tar.gz: 6f01160c9dfc7c11ace56ebac1b92c332be258716b64bd0eb8a95d8271913efc14c1499897c962e170cee914385f1ccbd79b9157f2060582f3ace220b7f3ccde
6
+ metadata.gz: 6c13e6cca540bd6f5e2ed3bd203bad3b4dad7db56824d34e36b511331b6a9bd5b5178ecf7fc6717318d54981950c33536ae3dfc6eafc82e4ceaf771f3b08fbdb
7
+ data.tar.gz: f1a3542fcda412f62e763cb38d9484028ddc65585eba3d6c9cb982eea8ef80656672769fc898a5b4266ef7419adadcf7e592200f4d7c8c9845505ddf13ec4f58
@@ -28,3 +28,13 @@ jobs:
28
28
  next_version: ${{ github.event.inputs.next_version }}
29
29
  secrets:
30
30
  rubygems-api-key: ${{ secrets.FONTIST_CI_RUBYGEMS_API_KEY }}
31
+
32
+ # Self-contained executables and .deb/.rpm packages for the new version,
33
+ # attached to its GitHub Release (fontist/fontist#463).
34
+ packages:
35
+ needs: release
36
+ permissions:
37
+ contents: write
38
+ uses: ./.github/workflows/tebako-pack.yml
39
+ with:
40
+ publish: true
@@ -1,65 +1,239 @@
1
1
  name: tebako-pack
2
2
 
3
+ # Packs fontist into one self-contained executable per platform with
4
+ # tebako v2, wraps the Linux ones into .deb/.rpm packages, checks both on
5
+ # machines with no Ruby, and attaches them to the GitHub Release when given
6
+ # a tag.
7
+
3
8
  on:
4
- repository_dispatch:
5
- types: [ do-release ]
9
+ # Called by release.yml once the gem is out. Its releases are created with
10
+ # GITHUB_TOKEN, and GitHub starts no workflow from such an event, so a
11
+ # `release: published` trigger here would never fire.
12
+ workflow_call:
13
+ inputs:
14
+ publish:
15
+ type: boolean
16
+ default: true
6
17
  workflow_dispatch:
18
+ inputs:
19
+ tag:
20
+ description: Release tag to build and attach to (empty = build only)
21
+ required: false
22
+ default: ''
23
+ pull_request:
24
+ paths:
25
+ - .github/workflows/tebako-pack.yml
26
+ - script/tebako/**
27
+ - packaging/**
7
28
 
8
29
  permissions:
9
30
  contents: read
10
- packages: write
11
31
 
12
32
  concurrency:
13
- group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}'
33
+ group: '${{ github.workflow }}-${{ github.head_ref || github.ref_name }}'
14
34
  cancel-in-progress: true
15
35
 
16
36
  env:
17
- # This is Ruby version that will be as a base for tebako package
18
- # I.e.: fontist binary will be executed by this version of Ruby
19
- PACKAGE_RUBY_VER: 3.2.3
20
- # This is Ruby version that will be used to execute tebako itself
21
- # It can be the same as PACKAGE_RUBY_VER but for whatever reason 3.1.4
22
- # has better performance
23
- TEBAKO_RUBY_VER: 3.1.4
37
+ # Ruby the packed fontist runs on: a version tebako ships a runtime for
38
+ # (tebako press rejects the rest).
39
+ PACKAGE_RUBY_VERSION: 3.3.12
40
+ TEBAKO_VERSION: 2.8.68
41
+ NFPM_VERSION: 2.48.0
24
42
 
25
43
  jobs:
44
+ resolve:
45
+ name: resolve tag
46
+ runs-on: ubuntu-24.04
47
+ outputs:
48
+ tag: ${{ steps.tag.outputs.tag }}
49
+ steps:
50
+ # After release.yml the default branch carries the released version.
51
+ - uses: actions/checkout@v4
52
+ with:
53
+ ref: ${{ github.event.repository.default_branch }}
54
+
55
+ - id: tag
56
+ env:
57
+ GH_TOKEN: ${{ github.token }}
58
+ CALLED: ${{ github.event_name != 'workflow_dispatch' && github.event_name != 'pull_request' && inputs.publish }}
59
+ INPUT_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || '' }}
60
+ run: |
61
+ if [ "$CALLED" = true ]; then
62
+ tag="v$(sed -n 's/.*VERSION = "\(.*\)".*/\1/p' lib/fontist/version.rb)"
63
+ else
64
+ tag="$INPUT_TAG"
65
+ fi
66
+ if [ -n "$tag" ]; then
67
+ # Only a version tag that is a published release of this repo.
68
+ if ! printf '%s' "$tag" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
69
+ echo "not a release tag: $tag" >&2
70
+ exit 1
71
+ fi
72
+ gh release view "$tag" --repo "$GITHUB_REPOSITORY" --json tagName >/dev/null
73
+ fi
74
+ echo "tag=$tag" >> "$GITHUB_OUTPUT"
75
+ echo "building ${tag:-$GITHUB_SHA}"
76
+
26
77
  pack:
27
- name: Pack fontist
78
+ name: pack ${{ matrix.name }}
79
+ needs: resolve
28
80
  runs-on: ${{ matrix.os }}
29
81
  strategy:
30
82
  fail-fast: false
31
- # macos-11 runner is deadly slow because brew is not supported on this version anymore
32
- # so it is practically impossible to use it for tests anymore
33
83
  matrix:
34
- os: [ubuntu-20.04, macos-12]
84
+ include:
85
+ # Linux is packed on the oldest runner so the result runs on older
86
+ # glibc distros too.
87
+ - { os: ubuntu-22.04, triplet: linux-gnu-x86_64, name: linux-x86_64 }
88
+ - { os: ubuntu-22.04-arm, triplet: linux-gnu-arm64, name: linux-arm64 }
89
+ - { os: macos-15, triplet: macos-arm64, name: macos-arm64 }
90
+ - { os: macos-15-intel, triplet: macos-x86_64, name: macos-x86_64 }
91
+ steps:
92
+ - uses: actions/checkout@v4
93
+ with:
94
+ fetch-depth: 0
95
+
96
+ # A release build packs the validated release tag, not the dispatching
97
+ # commit (release.yml bumps the version after this run's commit).
98
+ - name: Check out the release tag
99
+ if: needs.resolve.outputs.tag != ''
100
+ env:
101
+ TAG: ${{ needs.resolve.outputs.tag }}
102
+ run: git checkout --detach "refs/tags/$TAG"
103
+
104
+ - uses: ruby/setup-ruby@v1
105
+ with:
106
+ ruby-version: '3.3'
107
+
108
+ - name: Press
109
+ env:
110
+ GH_TOKEN: ${{ github.token }}
111
+ run: script/tebako/press.sh "${{ matrix.triplet }}" "out/fontist-${{ matrix.name }}"
112
+
113
+ # Uploaded before the smoke test so a failing binary can be fetched
114
+ # and inspected.
115
+ - uses: actions/upload-artifact@v4
116
+ with:
117
+ name: fontist-${{ matrix.name }}
118
+ path: out/fontist-${{ matrix.name }}
119
+ if-no-files-found: error
120
+
121
+ # Only the system directories and git stay on PATH, so the Ruby set up
122
+ # above is out of reach. (macOS keeps a system ruby in /usr/bin; the
123
+ # fresh distro containers in package-smoke are the no-Ruby-at-all
124
+ # check.) fontist's git gem needs git >= 2.43, newer than Apple's, so
125
+ # the runner's own git is kept.
126
+ - name: Smoke test without the build Ruby
127
+ run: |
128
+ git_dir="$(dirname "$(command -v git)")"
129
+ env -i HOME="$HOME" PATH="$git_dir:/usr/bin:/bin:/usr/sbin:/sbin" \
130
+ script/tebako/smoke.sh "out/fontist-${{ matrix.name }}"
131
+
132
+ packages:
133
+ name: deb and rpm
134
+ needs: [ resolve, pack ]
135
+ runs-on: ubuntu-24.04
35
136
  steps:
36
- - uses: actions/checkout@v4
37
- with:
38
- fetch-depth: 1
39
-
40
- - name: Setup Tebako
41
- uses: tamatebako/tebako/.github/actions/setup-tebako@main
42
- with:
43
- cache: build
44
- ruby_version: ${{ env.TEBAKO_RUBY_VER }}
45
-
46
- - name: Pack Fontist
47
- run: |
48
- tebako press -R "$PACKAGE_RUBY_VER" --root="." --entry-point="fontist" --output="fontist"
49
- if [ ${{ matrix.os }} -ne "macos-12" ]; then
50
- strip fontist
51
- fi
52
- ./fontist help || echo "fontist help exits with status 1"
53
- cp ./fontist ./fontist-${{ runner.os }}
54
-
55
- - if: github.event_name != 'workflow_dispatch'
56
- uses: softprops/action-gh-release@v1
57
- with:
58
- files: |
59
- fontist-${{ runner.os }}
60
-
61
- - if: github.event_name == 'workflow_dispatch'
62
- uses: actions/upload-artifact@v4
63
- with:
64
- name: fontist-${{ matrix.os }}
65
- path: fontist-${{ runner.os }}
137
+ - uses: actions/checkout@v4
138
+ with:
139
+ fetch-depth: 0
140
+
141
+ # A release build packs the validated release tag, not the dispatching
142
+ # commit (release.yml bumps the version after this run's commit).
143
+ - name: Check out the release tag
144
+ if: needs.resolve.outputs.tag != ''
145
+ env:
146
+ TAG: ${{ needs.resolve.outputs.tag }}
147
+ run: git checkout --detach "refs/tags/$TAG"
148
+
149
+ - uses: actions/download-artifact@v4
150
+ with:
151
+ pattern: fontist-linux-*
152
+ path: out
153
+ merge-multiple: true
154
+
155
+ - name: Install nfpm
156
+ env:
157
+ GH_TOKEN: ${{ github.token }}
158
+ run: |
159
+ gh release download "v${NFPM_VERSION}" --repo goreleaser/nfpm --dir /tmp/nfpm \
160
+ --pattern "nfpm_${NFPM_VERSION}_Linux_x86_64.tar.gz" --pattern checksums.txt
161
+ (cd /tmp/nfpm && sha256sum --ignore-missing -c checksums.txt)
162
+ tar -xzf "/tmp/nfpm/nfpm_${NFPM_VERSION}_Linux_x86_64.tar.gz" -C /tmp/nfpm nfpm
163
+ sudo install /tmp/nfpm/nfpm /usr/local/bin/nfpm
164
+
165
+ - name: Build packages
166
+ run: |
167
+ script/tebako/package.sh amd64 out/fontist-linux-x86_64 pkg
168
+ script/tebako/package.sh arm64 out/fontist-linux-arm64 pkg
169
+
170
+ - uses: actions/upload-artifact@v4
171
+ with:
172
+ name: fontist-linux-packages
173
+ path: pkg/*
174
+ if-no-files-found: error
175
+
176
+ package-smoke:
177
+ name: install on ${{ matrix.image }} ${{ matrix.arch }}
178
+ needs: [ resolve, packages ]
179
+ runs-on: ${{ matrix.runner }}
180
+ container: ${{ matrix.image }}
181
+ strategy:
182
+ fail-fast: false
183
+ matrix:
184
+ # Distros whose git meets the git gem's >= 2.43; older ones refuse
185
+ # the package on its git dependency.
186
+ image: [ 'ubuntu:24.04', 'debian:13', 'fedora:latest', 'rockylinux:9' ]
187
+ arch: [ amd64 ]
188
+ runner: [ ubuntu-24.04 ]
189
+ include:
190
+ - { image: 'ubuntu:24.04', arch: arm64, runner: ubuntu-24.04-arm }
191
+ steps:
192
+ # Only the smoke scripts are used here, so the dispatching commit is
193
+ # enough (these containers have no git to check out a tag with).
194
+ - uses: actions/checkout@v4
195
+
196
+ - uses: actions/download-artifact@v4
197
+ with:
198
+ name: fontist-linux-packages
199
+ path: pkg
200
+
201
+ - name: Keep only this architecture
202
+ run: |
203
+ if [ "${{ matrix.arch }}" = amd64 ]; then
204
+ find pkg \( -name '*arm64*' -o -name '*aarch64*' \) -delete
205
+ else
206
+ find pkg \( -name '*amd64*' -o -name '*x86_64*' \) -delete
207
+ fi
208
+ ls -l pkg
209
+
210
+ - name: Install, use, remove
211
+ run: script/tebako/package-smoke.sh pkg
212
+
213
+ publish:
214
+ name: attach to release
215
+ if: needs.resolve.outputs.tag != ''
216
+ needs: [ resolve, pack, package-smoke ]
217
+ runs-on: ubuntu-24.04
218
+ permissions:
219
+ contents: write
220
+ steps:
221
+ - uses: actions/download-artifact@v4
222
+ with:
223
+ path: dist
224
+ merge-multiple: true
225
+
226
+ - name: Name executables by version and upload
227
+ env:
228
+ GH_TOKEN: ${{ github.token }}
229
+ TAG: ${{ needs.resolve.outputs.tag }}
230
+ run: |
231
+ version="${TAG#v}"
232
+ cd dist
233
+ for name in linux-x86_64 linux-arm64 macos-arm64 macos-x86_64; do
234
+ mv "fontist-$name" "fontist-$version-$name"
235
+ done
236
+ # Executables and .rpm files are fontist-*, .deb files fontist_*.
237
+ sha256sum fontist-* fontist_* > SHA256SUMS
238
+ gh release upload "$TAG" --repo "$GITHUB_REPOSITORY" --clobber \
239
+ fontist-* fontist_* SHA256SUMS
data/README.adoc CHANGED
@@ -42,6 +42,56 @@ And then execute:
42
42
  bundle install
43
43
  ----
44
44
 
45
+ === Install without Ruby
46
+
47
+ Each release on the
48
+ https://github.com/fontist/fontist/releases[GitHub Releases] page has a
49
+ self-contained `fontist` executable. It does not need a Ruby installation.
50
+ Linux and macOS are supported.
51
+
52
+ Download the file for your system, check it, and make it executable. The
53
+ names are `fontist-<version>-linux-x86_64`, `-linux-arm64`, `-macos-arm64`
54
+ and `-macos-x86_64`.
55
+
56
+ [source,sh]
57
+ ----
58
+ curl -LO https://github.com/fontist/fontist/releases/download/v<version>/fontist-<version>-linux-x86_64
59
+ curl -LO https://github.com/fontist/fontist/releases/download/v<version>/SHA256SUMS
60
+ sha256sum --ignore-missing -c SHA256SUMS # on macOS: shasum -a 256 -c --ignore-missing SHA256SUMS
61
+ chmod +x fontist-<version>-linux-x86_64
62
+ ./fontist-<version>-linux-x86_64 version
63
+ ----
64
+
65
+ The executables are not signed. On macOS, remove the quarantine flag before
66
+ the first run:
67
+
68
+ [source,sh]
69
+ ----
70
+ xattr -d com.apple.quarantine ./fontist-<version>-macos-arm64
71
+ ----
72
+
73
+ Each run prints a notice from the packager that the executable is unsigned.
74
+ It is harmless.
75
+
76
+ fontist needs git 2.43 or newer to fetch formulas. The git that ships with
77
+ macOS is older, so install a current one first (for example
78
+ `brew install git`).
79
+
80
+ On Debian and Ubuntu, install the `.deb` package. On Fedora and RHEL, install
81
+ the `.rpm` package. Both put `fontist` in `/usr/bin` and depend on git 2.43 or
82
+ newer, which Ubuntu 24.04, Debian 13, Fedora and RHEL 9 provide.
83
+
84
+ [source,sh]
85
+ ----
86
+ sudo apt install ./fontist_<version>_amd64.deb
87
+ sudo dnf install ./fontist-<version>-1.x86_64.rpm
88
+ ----
89
+
90
+ For ARM64, use `_arm64.deb` and `.aarch64.rpm`.
91
+
92
+ This build uses the same home directory as the gem (`~/.fontist`, or
93
+ `FONTIST_PATH` if set), so the two can be used side by side.
94
+
45
95
  === Fetch formulas
46
96
 
47
97
  After installation, please fetch formulas via the `fontist` command:
@@ -248,7 +248,7 @@ export default defineConfig({
248
248
  ],
249
249
 
250
250
  footer: {
251
- message: `Fontist is <a href="https://open.ribose.com/">riboseopen</a>`,
251
+ message: `Fontist is <a href="https://www.ribose.com/">riboseopen</a>`,
252
252
  copyright: `Copyright &copy; 2026 Ribose Group Inc. All rights reserved.`,
253
253
  },
254
254
  },
data/lib/fontist/cli.rb CHANGED
@@ -564,7 +564,7 @@ module Fontist
564
564
  styles.each do |style, installed|
565
565
  opts = []
566
566
  opts << "manual" if formula.manual?
567
- opts << (installed ? "installed" : "not installed")
567
+ opts << install_label(installed)
568
568
  msg = " #{style.type} (#{opts.join(', ')})"
569
569
 
570
570
  if installed
@@ -578,6 +578,14 @@ module Fontist
578
578
  end
579
579
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
580
580
 
581
+ def install_label(status)
582
+ case status
583
+ when true then "installed"
584
+ when false then "not installed"
585
+ else "installed (system)"
586
+ end
587
+ end
588
+
581
589
  def print_find_results(results)
582
590
  if results.empty?
583
591
  Fontist.ui.say("No fonts found matching criteria")
data/lib/fontist/font.rb CHANGED
@@ -446,8 +446,9 @@ module Fontist
446
446
  def list_styles(formulas)
447
447
  map_to_hash(formulas) do |formula|
448
448
  map_to_hash(requested_fonts(formula.all_fonts)) do |font|
449
+ external = external_paths_by_type(font)
449
450
  map_to_hash(font.styles) do |style|
450
- installed(style)
451
+ installed(style, external)
451
452
  end
452
453
  end
453
454
  end
@@ -465,8 +466,25 @@ module Fontist
465
466
  end
466
467
  end
467
468
 
468
- def installed(style)
469
- path(style) ? true : false
469
+ # A font found only outside the fontist directory reports its paths
470
+ # instead of true, so callers can tell the copies fontist cannot
471
+ # update or remove from the managed ones.
472
+ def installed(style, external)
473
+ return true if path(style)
474
+
475
+ external.fetch(style.type.to_s.downcase, false)
476
+ end
477
+
478
+ # One index lookup per font name, grouped by lower-cased style type.
479
+ def external_paths_by_type(font)
480
+ entries = SystemFont.find_styles(font.name) || []
481
+ entries.reject { |entry| in_fontist_dir?(entry.path) }
482
+ .group_by { |entry| entry.type.to_s.downcase }
483
+ .transform_values { |group| group.map(&:path) }
484
+ end
485
+
486
+ def in_fontist_dir?(path)
487
+ path.start_with?(Fontist.fonts_path.to_s)
470
488
  end
471
489
 
472
490
  def raise_non_supported_font
@@ -403,8 +403,11 @@ module Fontist
403
403
  end
404
404
 
405
405
  def index_changed?
406
+ # Verified first: a location that holds no fonts leaves the index
407
+ # empty after its rebuild, and treating empty as unbuilt would rescan
408
+ # the disk on every lookup. Installs reach the index via add_font.
409
+ return false if @index_check_done # already verified in this session
406
410
  return true if fonts.nil? || fonts.empty?
407
- return false if @index_check_done # Skip if already verified in this session
408
411
 
409
412
  # Quick check: if index was scanned recently, trust it
410
413
  if recently_scanned?
@@ -93,7 +93,7 @@ module Fontist
93
93
 
94
94
  # Build lookup hash for fast O(1) access by path (non-shared state)
95
95
  def to_lookup
96
- entries.index_by(&:path)
96
+ entries.to_h { |entry| [entry.path, entry] }
97
97
  end
98
98
 
99
99
  # Get validation result for a path, checking if file has changed
@@ -78,8 +78,7 @@ module Fontist
78
78
  end
79
79
 
80
80
  # Validate the font
81
- font_file = FontFile.from_path(path, validate: true,
82
- check_extension: false)
81
+ font_file = FontFile.from_path(path)
83
82
 
84
83
  time_taken = Time.now - start_time
85
84
 
@@ -1,3 +1,3 @@
1
1
  module Fontist
2
- VERSION = "3.0.36".freeze
2
+ VERSION = "3.0.37".freeze
3
3
  end
@@ -0,0 +1,33 @@
1
+ # Linux packages that wrap the tebako-packed fontist executable.
2
+ # Built by script/tebako/package.sh, which sets ARCH, VERSION and BINARY.
3
+ name: fontist
4
+ arch: ${ARCH}
5
+ platform: linux
6
+ version: ${VERSION}
7
+ section: utils
8
+ priority: optional
9
+ maintainer: Ribose Inc. <open.source@ribose.com>
10
+ description: |
11
+ Install openly-licensed fonts on Windows, Linux and Mac.
12
+ Self-contained build: no Ruby installation is needed.
13
+ vendor: Ribose Inc.
14
+ homepage: https://www.fontist.org
15
+ license: BSD-2-Clause
16
+
17
+ # fontist clones its formula repository with git, and its git gem (~> 6.0)
18
+ # refuses a host git older than 2.43. Debian and Ubuntu version git with
19
+ # epoch 1 (1:2.43.0-1), RPM distros do not, hence one dependency per packager.
20
+ overrides:
21
+ deb:
22
+ depends:
23
+ - git (>= 1:2.43)
24
+ rpm:
25
+ depends:
26
+ - git >= 2.43
27
+
28
+ contents:
29
+ - src: ${BINARY}
30
+ dst: /usr/bin/fontist
31
+ expand: true # nfpm leaves ${BINARY} in src alone without this
32
+ file_info:
33
+ mode: 0755
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Entry point of the tebako-packed fontist (copied to bin/fontist by
5
+ # script/tebako/stage.sh).
6
+ #
7
+ # tebako installs the locked dependencies as gems inside the package; only
8
+ # fontist itself sits at a path, so it goes on the load path directly.
9
+ # bundler/setup is not used: bundler chdirs into path gems, and the packed
10
+ # filesystem refuses that.
11
+ #
12
+ # Libraries loaded through FFI (yeptris, leptris, ...) are opened with the
13
+ # host's dlopen, which cannot see inside the package. Each one is copied
14
+ # once to a host cache keyed by its content, and FFI is handed that copy.
15
+
16
+ # The packed gems are the only gems this program may see. rubygems also
17
+ # searches the host's GEM_HOME, GEM_PATH and user gem dir (~/.gem/ruby/<abi>),
18
+ # so a Ruby manager such as rvm, or a `gem install --user-install`, would
19
+ # shadow the locked gems. Pin rubygems to the package's gem dir before
20
+ # anything is required.
21
+ packed_gem_dir = Gem.default_dir
22
+ ENV.delete("GEM_HOME")
23
+ ENV.delete("GEM_PATH")
24
+ Gem.use_paths(packed_gem_dir, [packed_gem_dir])
25
+
26
+ require "digest"
27
+ require "fileutils"
28
+ require "ffi"
29
+
30
+ module FontistPackedNativeLibraries
31
+ PACKAGE_ROOT = "/__tfs__/"
32
+
33
+ def self.cache_root
34
+ home = ENV["FONTIST_PATH"] || File.join(Dir.home, ".fontist")
35
+ File.join(home, "tebako-native")
36
+ end
37
+
38
+ def self.host_path(name)
39
+ return name unless name.is_a?(String) && name.start_with?(PACKAGE_ROOT)
40
+ return name unless File.file?(name)
41
+
42
+ bytes = File.binread(name)
43
+ dir = File.join(cache_root, Digest::SHA256.hexdigest(bytes)[0, 16])
44
+ target = File.join(dir, File.basename(name))
45
+ write_once(target, bytes) unless File.file?(target)
46
+ target
47
+ end
48
+
49
+ def self.write_once(target, bytes)
50
+ FileUtils.mkdir_p(File.dirname(target))
51
+ partial = "#{target}.#{Process.pid}.partial"
52
+ File.binwrite(partial, bytes)
53
+ File.chmod(0o755, partial)
54
+ File.rename(partial, target)
55
+ end
56
+
57
+ # An Array argument is a list of alternatives (the first that loads wins),
58
+ # so the nesting is kept: only the names inside are rewritten.
59
+ def self.rewrite(name)
60
+ name.is_a?(Array) ? name.map { |n| rewrite(n) } : host_path(name)
61
+ end
62
+
63
+ def ffi_lib(*names)
64
+ super(*names.map { |name| FontistPackedNativeLibraries.rewrite(name) })
65
+ end
66
+ end
67
+
68
+ FFI::Library.prepend(FontistPackedNativeLibraries)
69
+
70
+ # leptris also opens libleptris from its compiled extension, which consults
71
+ # LEPTRIS_LIB_PATH and then package paths the host cannot open. Two copies
72
+ # of the library loaded side by side crash it, so both loaders are pointed
73
+ # at the same host copy. yeptris reads YEPTRIS_LIB_PATH the same way.
74
+ {
75
+ "leptris" => %w[LEPTRIS_LIB_PATH libleptris],
76
+ "yeptris" => %w[YEPTRIS_LIB_PATH libyeptris],
77
+ }.each do |gem_name, (variable, basename)|
78
+ next if ENV[variable]
79
+
80
+ spec = Gem::Specification.find_all_by_name(gem_name).max_by(&:version)
81
+ next unless spec
82
+
83
+ library = Dir.glob(
84
+ File.join(spec.full_gem_path, "**", "#{basename}.{dylib,so,dll}"),
85
+ ).min
86
+ ENV[variable] = FontistPackedNativeLibraries.host_path(library) if library
87
+ end
88
+
89
+ $LOAD_PATH.unshift(File.expand_path("../vendor/fontist/lib", __dir__))
90
+ load File.expand_path("../vendor/fontist/exe/fontist", __dir__)
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ # Install a fontist .deb or .rpm on a fresh distro, use it, then remove it.
3
+ # Runs inside a distro container (see .github/workflows/tebako-pack.yml).
4
+ #
5
+ # script/tebako/package-smoke.sh <package-dir>
6
+ set -euo pipefail
7
+
8
+ dir="${1:?usage: package-smoke.sh <package-dir>}"
9
+ # apt reads a bare relative path as a package name; give it an absolute one.
10
+ dir="$(cd "$dir" && pwd)"
11
+ here="$(cd "$(dirname "$0")" && pwd)"
12
+
13
+ if command -v apt-get >/dev/null; then
14
+ export DEBIAN_FRONTEND=noninteractive
15
+ apt-get update -qq
16
+ apt-get install -y -qq ca-certificates "$dir"/fontist_*.deb
17
+ remove() { apt-get remove -y -qq fontist; }
18
+ else
19
+ dnf install -y -q "$dir"/fontist-*.rpm
20
+ remove() { dnf remove -y -q fontist; }
21
+ fi
22
+
23
+ if command -v ruby >/dev/null; then
24
+ echo "package-smoke: this image has no business carrying ruby" >&2
25
+ exit 1
26
+ fi
27
+ "$here/smoke.sh" "$(command -v fontist)"
28
+ remove
29
+ if command -v fontist >/dev/null; then
30
+ echo "package-smoke: fontist still on PATH after removal" >&2
31
+ exit 1
32
+ fi
33
+ echo "package-smoke: ok"
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ # Wrap a packed Linux fontist executable into .deb and .rpm packages.
3
+ #
4
+ # script/tebako/package.sh <amd64|arm64> <fontist-executable> <out-dir>
5
+ set -euo pipefail
6
+
7
+ usage="usage: package.sh <amd64|arm64> <fontist-executable> <out-dir>"
8
+ arch="${1:?$usage}"
9
+ binary="${2:?$usage}"
10
+ out="${3:?$usage}"
11
+
12
+ root="$(cd "$(dirname "$0")/../.." && pwd)"
13
+ version="$(sed -n 's/.*VERSION = "\(.*\)".*/\1/p' "$root/lib/fontist/version.rb")"
14
+
15
+ mkdir -p "$out"
16
+ BINARY="$(cd "$(dirname "$binary")" && pwd)/$(basename "$binary")"
17
+ export ARCH="$arch" VERSION="$version" BINARY
18
+
19
+ for packager in deb rpm; do
20
+ nfpm package --config "$root/packaging/nfpm.yaml" \
21
+ --packager "$packager" --target "$out"
22
+ done
23
+ ls -l "$out"
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Press fontist into one self-contained executable with tebako v2.
3
+ #
4
+ # script/tebako/press.sh <tebako-triplet> <output-path>
5
+ #
6
+ # <tebako-triplet> is the tebako release asset suffix: linux-gnu-x86_64,
7
+ # linux-gnu-arm64, macos-arm64 or macos-x86_64. Needs gh, ruby and bundler.
8
+ # GH_TOKEN (or GITHUB_TOKEN) keeps tebako's runtime lookups clear of the
9
+ # anonymous GitHub API limit.
10
+ set -euo pipefail
11
+
12
+ triplet="${1:?usage: press.sh <tebako-triplet> <output-path>}"
13
+ output="${2:?usage: press.sh <tebako-triplet> <output-path>}"
14
+
15
+ tebako_version="${TEBAKO_VERSION:-2.8.68}"
16
+ ruby_version="${PACKAGE_RUBY_VERSION:-3.3.12}"
17
+ here="$(cd "$(dirname "$0")" && pwd)"
18
+ work="$(mktemp -d)"
19
+ trap 'rm -rf "$work"' EXIT
20
+
21
+ export GITHUB_TOKEN="${GITHUB_TOKEN:-${GH_TOKEN:-}}"
22
+
23
+ asset="tebako-${tebako_version}-${triplet}"
24
+ gh release download "v${tebako_version}" --repo tamatebako/tebako \
25
+ --pattern "${asset}" --pattern "${asset}.sha256" --dir "$work"
26
+ (cd "$work" && shasum -a 256 -c "${asset}.sha256")
27
+ chmod +x "$work/$asset"
28
+
29
+ stage="$("$here/stage.sh" "$work/stage")"
30
+
31
+ mkdir -p "$(dirname "$output")"
32
+ output="$(cd "$(dirname "$output")" && pwd)/$(basename "$output")"
33
+
34
+ # tebako appends a trailer to the executable: never strip the result.
35
+ "$work/$asset" press --root "$stage" --entry-point bin/fontist \
36
+ --output "$output" --mode self-contained -R "$ruby_version"
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # Check that a packed (or installed) fontist works on its own.
3
+ #
4
+ # script/tebako/smoke.sh <fontist-executable>
5
+ #
6
+ # Uses a throwaway FONTIST_PATH so it never touches a real ~/.fontist.
7
+ set -euo pipefail
8
+
9
+ fontist="${1:?usage: smoke.sh <fontist-executable>}"
10
+ font="${SMOKE_FONT:-Overpass}"
11
+
12
+ home="$(mktemp -d)"
13
+ trap 'rm -rf "$home"' EXIT
14
+ export FONTIST_PATH="$home"
15
+
16
+ "$fontist" version
17
+ "$fontist" install --accept-all-licenses "$font"
18
+
19
+ if ! find "$home/fonts" -type f | grep -q .; then
20
+ echo "smoke: no font file landed under $home/fonts" >&2
21
+ exit 1
22
+ fi
23
+ "$fontist" list "$font" | tee "$home/list.txt"
24
+ grep -qi "installed" "$home/list.txt"
25
+ echo "smoke: ok"
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ # Build a Gemfile-rooted app around the fontist gem, the layout tebako v2
3
+ # presses (it refuses a root that is itself a gem source tree).
4
+ #
5
+ # script/tebako/stage.sh <stage-dir>
6
+ #
7
+ # The gem is built from this checkout, unpacked with a static gemspec (the
8
+ # repository gemspec lists files with `git ls-files`, which needs .git), and
9
+ # referenced by path, so the packed fontist is exactly this checkout's code.
10
+ set -euo pipefail
11
+
12
+ stage="${1:?usage: stage.sh <stage-dir>}"
13
+ root="$(cd "$(dirname "$0")/../.." && pwd)"
14
+
15
+ rm -rf "$stage"
16
+ mkdir -p "$stage/vendor" "$stage/bin"
17
+ stage="$(cd "$stage" && pwd)"
18
+
19
+ (cd "$root" && gem build fontist.gemspec --output "$stage/fontist.gem" >/dev/null)
20
+ # Unpacked directory naming differs across rubygems versions; take the one
21
+ # child of a fresh directory instead of guessing it.
22
+ unpacked="$(mktemp -d)"
23
+ gem unpack "$stage/fontist.gem" --target "$unpacked" >/dev/null
24
+ mv "$unpacked"/*/ "$stage/vendor/fontist"
25
+ rmdir "$unpacked"
26
+ gem spec "$stage/fontist.gem" --ruby > "$stage/vendor/fontist/fontist.gemspec"
27
+ rm "$stage/fontist.gem"
28
+
29
+ cat > "$stage/Gemfile" <<'GEMFILE'
30
+ source "https://rubygems.org"
31
+
32
+ gem "fontist", path: "vendor/fontist"
33
+ GEMFILE
34
+
35
+ cp "$root/script/tebako/entry.rb" "$stage/bin/fontist"
36
+ chmod +x "$stage/bin/fontist"
37
+
38
+ (cd "$stage" && bundle lock >/dev/null)
39
+ echo "$stage"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontist
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.36
4
+ version: 3.0.37
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-11-05 00:00:00.000000000 Z
11
+ date: 2026-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: down
@@ -517,9 +517,16 @@ files:
517
517
  - lib/fontist/yaml/yeptris_adapter.rb
518
518
  - lib/fontist/yaml_backend.rb
519
519
  - lib/tasks/system_fonts.rake
520
+ - packaging/nfpm.yaml
520
521
  - script/capture_system_fonts
521
522
  - script/download_system_indexes
522
523
  - script/generate_windows_formulas.rb
524
+ - script/tebako/entry.rb
525
+ - script/tebako/package-smoke.sh
526
+ - script/tebako/package.sh
527
+ - script/tebako/press.sh
528
+ - script/tebako/smoke.sh
529
+ - script/tebako/stage.sh
523
530
  - script/validate_windows_fod_ci.rb
524
531
  homepage: https://github.com/fontist/fontist
525
532
  licenses: