fontist 2.1.1 → 2.1.2
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 +4 -4
- data/.github/workflows/deploy-pages.yml +6 -0
- data/.github/workflows/formulas-auto-update-test.yml +287 -0
- data/.github/workflows/post-rake.yml +5 -1
- data/.github/workflows/rake-metanorma.yaml +3 -0
- data/.github/workflows/rake.yml +4 -1
- data/.github/workflows/release.yml +4 -0
- data/.github/workflows/tebako-pack.yml +4 -0
- data/fontist.gemspec +2 -0
- data/lib/fontist/cache/store.rb +31 -2
- data/lib/fontist/manifest.rb +4 -4
- data/lib/fontist/utils/cache.rb +1 -0
- data/lib/fontist/utils/downloader.rb +38 -4
- data/lib/fontist/utils/github_client.rb +43 -0
- data/lib/fontist/utils/github_url.rb +51 -0
- data/lib/fontist/version.rb +1 -1
- data/lib/fontist.rb +6 -3
- metadata +33 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0aad02a62b4c3f295e80229cbca11af23ccbad51850b3f87dc4e6652e548947
|
|
4
|
+
data.tar.gz: c97a29591a24ee8c84ecb3316a3f66dade12994c752196b1ff86f48c266b3df5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b6c0acf8fe7acd3a49770beaba2a5e6c1af8a742eb5db204ab8955be9ffaf013723d20170fe557d49983eb8234cfe7283ab2ba3a94c0231e7a0c10454219c3f
|
|
7
|
+
data.tar.gz: 6715acf2088a92c6d51e33d4fb07b88b48318fdb39a2f05084369109801149ecf57d9ed4f7cadef389cd39f9e26442548d7c121909013ce20909b6c6bc677609
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
name: deploy-pages
|
|
2
|
+
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
|
|
2
6
|
on:
|
|
3
7
|
push:
|
|
4
8
|
branches: "main"
|
|
@@ -10,9 +14,11 @@ on:
|
|
|
10
14
|
- .github/**
|
|
11
15
|
- "!.github/workflows/deploy-pages.yml"
|
|
12
16
|
workflow_dispatch:
|
|
17
|
+
|
|
13
18
|
concurrency:
|
|
14
19
|
group: ${{ github.workflow }}
|
|
15
20
|
cancel-in-progress: true
|
|
21
|
+
|
|
16
22
|
jobs:
|
|
17
23
|
deploy-pages:
|
|
18
24
|
environment:
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
name: formulas-auto-update-test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
# Cancel in-progress runs for the same workflow and branch
|
|
13
|
+
concurrency:
|
|
14
|
+
group: '${{ github.workflow }}-${{ github.head_ref || github.ref_name }}'
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
env:
|
|
18
|
+
BUNDLER_VER: latest
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
# This job tests that fonts can be installed WITHOUT explicit `fontist update`.
|
|
22
|
+
# This verifies the lazy initialization feature works correctly on all platforms.
|
|
23
|
+
#
|
|
24
|
+
# Lazy initialization ensures formulas are auto-downloaded on first access,
|
|
25
|
+
# fixing the issue where CI workflows failed because formulas weren't available.
|
|
26
|
+
test-formulas-auto-update:
|
|
27
|
+
name: Formulas Auto-Update Test (${{ matrix.os }})
|
|
28
|
+
runs-on: ${{ matrix.os }}
|
|
29
|
+
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: false
|
|
32
|
+
matrix:
|
|
33
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
34
|
+
# Test on all platforms to ensure lazy initialization works everywhere
|
|
35
|
+
# macOS: STIX Two Math is pre-installed, but we still test the mechanism
|
|
36
|
+
# Linux/Windows: No pre-installed fonts, tests full lazy initialization
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout
|
|
40
|
+
uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Setup Ruby
|
|
43
|
+
uses: ruby/setup-ruby@v1
|
|
44
|
+
with:
|
|
45
|
+
ruby-version: '3.3'
|
|
46
|
+
rubygems: latest
|
|
47
|
+
bundler: ${{ env.BUNDLER_VER }}
|
|
48
|
+
bundler-cache: true
|
|
49
|
+
|
|
50
|
+
- name: Verify fontist installation
|
|
51
|
+
shell: bash
|
|
52
|
+
run: |
|
|
53
|
+
echo "=== Fontist Version ==="
|
|
54
|
+
bundle exec fontist --version
|
|
55
|
+
echo ""
|
|
56
|
+
echo "=== Fontist Home Path ==="
|
|
57
|
+
echo "Fontist home: $HOME/.fontist"
|
|
58
|
+
echo "Formulas path: $HOME/.fontist/versions/v4/formulas"
|
|
59
|
+
|
|
60
|
+
# CRITICAL: Remove any existing formulas directory to simulate fresh CI environment
|
|
61
|
+
# This ensures we're testing lazy initialization from a clean state
|
|
62
|
+
- name: Remove existing formulas directory (simulate fresh CI)
|
|
63
|
+
shell: bash
|
|
64
|
+
run: |
|
|
65
|
+
echo "=== Simulating Fresh CI Environment ==="
|
|
66
|
+
echo "Removing any existing formulas directory..."
|
|
67
|
+
if [ -d "$HOME/.fontist/versions/v4/formulas" ]; then
|
|
68
|
+
echo " ✓ Found existing formulas directory, removing..."
|
|
69
|
+
rm -rf "$HOME/.fontist/versions/v4/formulas"
|
|
70
|
+
echo " ✓ Formulas directory removed"
|
|
71
|
+
else
|
|
72
|
+
echo " ✓ No existing formulas directory (clean state)"
|
|
73
|
+
fi
|
|
74
|
+
echo ""
|
|
75
|
+
echo "Verifying formulas directory does not exist:"
|
|
76
|
+
if [ -d "$HOME/.fontist/versions/v4/formulas" ]; then
|
|
77
|
+
echo " ✗ ERROR: Formulas directory still exists!"
|
|
78
|
+
exit 1
|
|
79
|
+
else
|
|
80
|
+
echo " ✓ Confirmed: Formulas directory does not exist"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
- name: Verify formulas directory doesn't exist before test
|
|
84
|
+
shell: bash
|
|
85
|
+
run: |
|
|
86
|
+
echo "=== Pre-test State Verification ==="
|
|
87
|
+
echo "Checking formulas directory status:"
|
|
88
|
+
FORMULAS_DIR="$HOME/.fontist/versions/v4/formulas/Formulas"
|
|
89
|
+
if [ -d "$FORMULAS_DIR" ]; then
|
|
90
|
+
echo " ✗ ERROR: Formulas directory exists at $FORMULAS_DIR"
|
|
91
|
+
echo " This will invalidate the test!"
|
|
92
|
+
exit 1
|
|
93
|
+
else
|
|
94
|
+
echo " ✓ Formulas directory does not exist (as expected)"
|
|
95
|
+
fi
|
|
96
|
+
echo ""
|
|
97
|
+
echo "Listing .fontist directory contents (if any):"
|
|
98
|
+
ls -la "$HOME/.fontist/" 2>/dev/null || echo " (No .fontist directory yet)"
|
|
99
|
+
echo ""
|
|
100
|
+
echo "Listing versions directory (if any):"
|
|
101
|
+
ls -la "$HOME/.fontist/versions/" 2>/dev/null || echo " (No versions directory yet)"
|
|
102
|
+
|
|
103
|
+
# CRITICAL: Remove formulas directory again right before install test
|
|
104
|
+
# This is necessary because bundler-cache may restore .fontist directory
|
|
105
|
+
- name: Force remove formulas directory before test
|
|
106
|
+
shell: bash
|
|
107
|
+
run: |
|
|
108
|
+
echo "=== Force removing formulas directory before test ==="
|
|
109
|
+
rm -rf "$HOME/.fontist/versions/v4/formulas"
|
|
110
|
+
echo "✓ Formulas directory removed"
|
|
111
|
+
echo ""
|
|
112
|
+
echo "Verifying removal:"
|
|
113
|
+
if [ -d "$HOME/.fontist/versions/v4/formulas" ]; then
|
|
114
|
+
echo " ✗ ERROR: Formulas directory still exists!"
|
|
115
|
+
exit 1
|
|
116
|
+
else
|
|
117
|
+
echo " ✓ Formulas directory successfully removed"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# CRITICAL TEST: Install Andale Mono WITHOUT running `fontist update` first
|
|
121
|
+
# Andale Mono is NOT pre-installed on any platform (macOS/Linux/Windows),
|
|
122
|
+
# so this properly tests lazy initialization on ALL platforms.
|
|
123
|
+
# Unlike STIX Two Math which is pre-installed on macOS.
|
|
124
|
+
- name: Install Andale Mono (WITHOUT fontist update)
|
|
125
|
+
shell: bash
|
|
126
|
+
run: |
|
|
127
|
+
echo "========================================================================="
|
|
128
|
+
echo " CRITICAL TEST: Lazy Initialization"
|
|
129
|
+
echo "========================================================================="
|
|
130
|
+
echo ""
|
|
131
|
+
echo "This test installs Andale Mono WITHOUT running 'fontist update' first."
|
|
132
|
+
echo "Andale Mono is NOT pre-installed on any platform, ensuring we test"
|
|
133
|
+
echo "lazy initialization on ALL platforms (macOS/Linux/Windows)."
|
|
134
|
+
echo ""
|
|
135
|
+
echo "Platform: ${{ matrix.os }}"
|
|
136
|
+
echo "Ruby: $(ruby --version)"
|
|
137
|
+
echo ""
|
|
138
|
+
echo "--- Step 1: Verify formulas don't exist yet ---"
|
|
139
|
+
FORMULAS_DIR="$HOME/.fontist/versions/v4/formulas/Formulas"
|
|
140
|
+
if [ -d "$FORMULAS_DIR" ]; then
|
|
141
|
+
echo " ✗ ERROR: Formulas directory already exists!"
|
|
142
|
+
echo " This invalidates the test - lazy initialization won't be tested."
|
|
143
|
+
exit 1
|
|
144
|
+
else
|
|
145
|
+
echo " ✓ Formulas directory does not exist (as expected)"
|
|
146
|
+
fi
|
|
147
|
+
echo ""
|
|
148
|
+
|
|
149
|
+
echo "--- Step 2: Install Andale Mono (should trigger lazy update) ---"
|
|
150
|
+
echo "Running: bundle exec fontist install 'Andale Mono' --accept-all-licenses --force"
|
|
151
|
+
echo ""
|
|
152
|
+
if bundle exec fontist install "Andale Mono" --accept-all-licenses --force 2>&1; then
|
|
153
|
+
echo ""
|
|
154
|
+
echo "✓ SUCCESS: Andale Mono installation completed"
|
|
155
|
+
INSTALL_SUCCESS=true
|
|
156
|
+
else
|
|
157
|
+
EXIT_CODE=$?
|
|
158
|
+
echo ""
|
|
159
|
+
echo "✗ FAILED: Andale Mono installation failed with exit code: $EXIT_CODE"
|
|
160
|
+
INSTALL_SUCCESS=false
|
|
161
|
+
fi
|
|
162
|
+
echo ""
|
|
163
|
+
|
|
164
|
+
echo "--- Step 3: Verify formulas were auto-downloaded ---"
|
|
165
|
+
if [ -d "$FORMULAS_DIR" ]; then
|
|
166
|
+
echo " ✓ Formulas directory was created (lazy initialization worked!)"
|
|
167
|
+
echo ""
|
|
168
|
+
echo " Formulas directory contents (first 20 entries):"
|
|
169
|
+
ls -1 "$FORMULAS_DIR" | head -20
|
|
170
|
+
else
|
|
171
|
+
echo " ✗ ERROR: Formulas directory was NOT created!"
|
|
172
|
+
echo " This indicates lazy initialization FAILED."
|
|
173
|
+
INSTALL_SUCCESS=false
|
|
174
|
+
fi
|
|
175
|
+
echo ""
|
|
176
|
+
|
|
177
|
+
echo "--- Step 4: Verify Andale Mono formula exists ---"
|
|
178
|
+
ANDALE_FORMULA="$FORMULAS_DIR/andale.yml"
|
|
179
|
+
if [ -f "$ANDALE_FORMULA" ]; then
|
|
180
|
+
echo " ✓ Andale Mono formula exists at andale.yml"
|
|
181
|
+
else
|
|
182
|
+
echo " ✗ ERROR: Andale Mono formula NOT found!"
|
|
183
|
+
echo " Expected: $ANDALE_FORMULA"
|
|
184
|
+
INSTALL_SUCCESS=false
|
|
185
|
+
fi
|
|
186
|
+
echo ""
|
|
187
|
+
|
|
188
|
+
echo "--- Step 5: Verify Andale Mono font was installed ---"
|
|
189
|
+
# Check if font file exists
|
|
190
|
+
FONT_PATH=$(find "$HOME/.fontist/fonts" -name "*Andale*" -type f 2>/dev/null | head -1)
|
|
191
|
+
if [ -n "$FONT_PATH" ]; then
|
|
192
|
+
echo " ✓ Andale Mono font file found:"
|
|
193
|
+
echo " $FONT_PATH"
|
|
194
|
+
else
|
|
195
|
+
echo " ⚠ WARNING: Andale Mono font file not found in .fontist/fonts"
|
|
196
|
+
echo " Checking with 'fontist list'..."
|
|
197
|
+
if bundle exec fontist list "Andale Mono" 2>&1 | grep -q "Andale"; then
|
|
198
|
+
echo " ✓ Font is discoverable via fontist list"
|
|
199
|
+
else
|
|
200
|
+
echo " ✗ ERROR: Font is NOT discoverable"
|
|
201
|
+
INSTALL_SUCCESS=false
|
|
202
|
+
fi
|
|
203
|
+
fi
|
|
204
|
+
echo ""
|
|
205
|
+
|
|
206
|
+
echo "--- Step 6: Final Result ---"
|
|
207
|
+
if [ "$INSTALL_SUCCESS" = true ]; then
|
|
208
|
+
echo "========================================================================="
|
|
209
|
+
echo " ✓✓✓ TEST PASSED: Lazy initialization works correctly ✓✓✓"
|
|
210
|
+
echo "========================================================================="
|
|
211
|
+
echo ""
|
|
212
|
+
echo "Andale Mono was successfully installed WITHOUT running 'fontist update'"
|
|
213
|
+
echo "This confirms that formulas are auto-downloaded on first access."
|
|
214
|
+
else
|
|
215
|
+
echo "========================================================================="
|
|
216
|
+
echo " ✗✗✗ TEST FAILED: Lazy initialization not working ✗✗✗"
|
|
217
|
+
echo "========================================================================="
|
|
218
|
+
echo ""
|
|
219
|
+
echo "Andale Mono installation failed or formulas were not auto-downloaded."
|
|
220
|
+
echo "This indicates a regression in the lazy initialization feature."
|
|
221
|
+
exit 1
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
- name: Debug output on failure
|
|
225
|
+
if: failure()
|
|
226
|
+
shell: bash
|
|
227
|
+
run: |
|
|
228
|
+
echo "=== Debug Information ==="
|
|
229
|
+
echo ""
|
|
230
|
+
echo "Fontist version:"
|
|
231
|
+
bundle exec fontist --version || true
|
|
232
|
+
echo ""
|
|
233
|
+
echo ".fontist directory structure:"
|
|
234
|
+
find "$HOME/.fontist" -type d 2>/dev/null || true
|
|
235
|
+
echo ""
|
|
236
|
+
echo "Formulas directory:"
|
|
237
|
+
ls -la "$HOME/.fontist/versions/v4/formulas/" 2>/dev/null || echo " (Does not exist)"
|
|
238
|
+
echo ""
|
|
239
|
+
echo "Formulas/Formulas directory:"
|
|
240
|
+
ls -la "$HOME/.fontist/versions/v4/formulas/Formulas/" 2>/dev/null || echo " (Does not exist)"
|
|
241
|
+
echo ""
|
|
242
|
+
echo "Fonts directory:"
|
|
243
|
+
ls -la "$HOME/.fontist/fonts/" 2>/dev/null || echo " (Does not exist)"
|
|
244
|
+
echo ""
|
|
245
|
+
echo "Andale Mono formula file:"
|
|
246
|
+
cat "$HOME/.fontist/versions/v4/formulas/Formulas/andale.yml" 2>/dev/null || echo " (Does not exist)"
|
|
247
|
+
|
|
248
|
+
# Additional test: Verify that explicit 'fontist update' still works
|
|
249
|
+
test-explicit-update-still-works:
|
|
250
|
+
name: Explicit fontist update still works
|
|
251
|
+
runs-on: ubuntu-latest
|
|
252
|
+
|
|
253
|
+
steps:
|
|
254
|
+
- name: Checkout
|
|
255
|
+
uses: actions/checkout@v4
|
|
256
|
+
|
|
257
|
+
- name: Setup Ruby
|
|
258
|
+
uses: ruby/setup-ruby@v1
|
|
259
|
+
with:
|
|
260
|
+
ruby-version: '3.3'
|
|
261
|
+
rubygems: latest
|
|
262
|
+
bundler: ${{ env.BUNDLER_VER }}
|
|
263
|
+
bundler-cache: true
|
|
264
|
+
|
|
265
|
+
- name: Remove existing formulas directory
|
|
266
|
+
shell: bash
|
|
267
|
+
run: rm -rf "$HOME/.fontist/versions/v4/formulas"
|
|
268
|
+
|
|
269
|
+
- name: Run explicit fontist update
|
|
270
|
+
shell: bash
|
|
271
|
+
run: |
|
|
272
|
+
echo "=== Testing explicit 'fontist update' ==="
|
|
273
|
+
echo ""
|
|
274
|
+
echo "Running: bundle exec fontist update"
|
|
275
|
+
bundle exec fontist update
|
|
276
|
+
echo ""
|
|
277
|
+
echo "✓ Explicit update completed"
|
|
278
|
+
echo ""
|
|
279
|
+
echo "Verifying formulas directory exists:"
|
|
280
|
+
FORMULAS_DIR="$HOME/.fontist/versions/v4/formulas/Formulas"
|
|
281
|
+
if [ -d "$FORMULAS_DIR" ]; then
|
|
282
|
+
echo " ✓ Formulas directory exists"
|
|
283
|
+
echo " Formula count: $(ls -1 "$FORMULAS_DIR" | wc -l)"
|
|
284
|
+
else
|
|
285
|
+
echo " ✗ ERROR: Formulas directory does not exist"
|
|
286
|
+
exit 1
|
|
287
|
+
fi
|
|
@@ -3,12 +3,16 @@ name: post-rake
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
5
|
workflow_run:
|
|
6
|
-
workflows:
|
|
6
|
+
workflows:
|
|
7
7
|
- rake
|
|
8
8
|
- rake-metanorma
|
|
9
9
|
types:
|
|
10
10
|
- completed
|
|
11
11
|
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
packages: write
|
|
15
|
+
|
|
12
16
|
jobs:
|
|
13
17
|
post-rake:
|
|
14
18
|
if: ${{ github.event.workflow_run.conclusion == 'success' && contains(github.ref, 'refs/tags/v') }}
|
data/.github/workflows/rake.yml
CHANGED
|
@@ -10,6 +10,9 @@ on:
|
|
|
10
10
|
paths-ignore:
|
|
11
11
|
- '**.adoc'
|
|
12
12
|
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
13
16
|
concurrency:
|
|
14
17
|
group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}'
|
|
15
18
|
cancel-in-progress: true
|
|
@@ -45,7 +48,7 @@ jobs:
|
|
|
45
48
|
run: |
|
|
46
49
|
git clone --branch v4 --depth 1 https://github.com/fontist/formulas.git C:\temp\fontist\versions\v4\formulas
|
|
47
50
|
shell: powershell
|
|
48
|
-
|
|
51
|
+
|
|
49
52
|
- uses: actions/checkout@v4
|
|
50
53
|
|
|
51
54
|
- uses: ruby/setup-ruby@v1
|
data/fontist.gemspec
CHANGED
|
@@ -36,8 +36,10 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.add_dependency "git", "> 1.0"
|
|
37
37
|
spec.add_dependency "json", "~> 2.0"
|
|
38
38
|
spec.add_dependency "lutaml-model", "~> 0.7"
|
|
39
|
+
spec.add_dependency "lutaml-xsd", "~> 1.0"
|
|
39
40
|
spec.add_dependency "marcel", "~> 1.0"
|
|
40
41
|
spec.add_dependency "nokogiri", "~> 1.0"
|
|
42
|
+
spec.add_dependency "octokit", "~> 4.0"
|
|
41
43
|
spec.add_dependency "paint", "~> 2.3"
|
|
42
44
|
spec.add_dependency "parallel", "~> 1.24"
|
|
43
45
|
spec.add_dependency "plist", "~> 3.0"
|
data/lib/fontist/cache/store.rb
CHANGED
|
@@ -42,6 +42,16 @@ module Fontist
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def cleanup_temp_files
|
|
46
|
+
# Clean up orphaned .tmp files from interrupted writes
|
|
47
|
+
# This can happen if the process crashes between File.write and File.rename
|
|
48
|
+
Dir.glob(File.join(@cache_dir, "*.tmp")).each do |tmp|
|
|
49
|
+
File.delete(tmp)
|
|
50
|
+
rescue StandardError
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
45
55
|
private
|
|
46
56
|
|
|
47
57
|
attr_reader :cache_dir
|
|
@@ -68,11 +78,30 @@ module Fontist
|
|
|
68
78
|
def read_entry(key)
|
|
69
79
|
return nil unless File.exist?(cache_path(key))
|
|
70
80
|
|
|
71
|
-
|
|
81
|
+
begin
|
|
82
|
+
Marshal.load(File.read(cache_path(key)))
|
|
83
|
+
rescue ArgumentError, TypeError => e
|
|
84
|
+
# Cache file is corrupted - delete it and return nil
|
|
85
|
+
# This can happen on Windows when file is read while being written,
|
|
86
|
+
# or when cache files from previous runs are corrupted
|
|
87
|
+
File.delete(cache_path(key)) rescue nil
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
72
90
|
end
|
|
73
91
|
|
|
74
92
|
def write_entry(key, entry)
|
|
75
|
-
|
|
93
|
+
# Use temp file + atomic rename to prevent race conditions
|
|
94
|
+
# This ensures readers never see partial writes, even on Windows
|
|
95
|
+
temp_path = cache_path(key) + ".tmp"
|
|
96
|
+
|
|
97
|
+
File.write(temp_path, Marshal.dump(entry))
|
|
98
|
+
# Atomic rename (overwrites target atomically)
|
|
99
|
+
# File.rename is atomic on all platforms for same filesystem
|
|
100
|
+
File.rename(temp_path, cache_path(key))
|
|
101
|
+
rescue => e
|
|
102
|
+
# Clean up temp file if rename fails
|
|
103
|
+
File.delete(temp_path) rescue nil
|
|
104
|
+
raise e
|
|
76
105
|
end
|
|
77
106
|
|
|
78
107
|
# Cache entry with TTL support
|
data/lib/fontist/manifest.rb
CHANGED
|
@@ -79,13 +79,13 @@ location: nil)
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def validate_platform_compatibility!
|
|
82
|
-
formula = Fontist::Formula.
|
|
83
|
-
return if formula.
|
|
84
|
-
return if formula.compatible_with_platform?
|
|
82
|
+
formula = Fontist::Formula.find_many(name)
|
|
83
|
+
return if formula.empty?
|
|
84
|
+
return if formula.any?(&:compatible_with_platform?)
|
|
85
85
|
|
|
86
86
|
raise Fontist::Errors::PlatformMismatchError.new(
|
|
87
87
|
name,
|
|
88
|
-
formula.platforms,
|
|
88
|
+
formula.map(&:platforms).flatten.uniq,
|
|
89
89
|
Fontist::Utils::System.user_os,
|
|
90
90
|
)
|
|
91
91
|
end
|
data/lib/fontist/utils/cache.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
require_relative "cache"
|
|
2
|
+
require_relative "github_url"
|
|
3
|
+
require_relative "github_client"
|
|
2
4
|
|
|
3
5
|
module Fontist
|
|
4
6
|
module Utils
|
|
@@ -59,16 +61,30 @@ module Fontist
|
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
def download_file
|
|
62
|
-
tries
|
|
64
|
+
@tries ||= 0
|
|
65
|
+
@tries += 1
|
|
63
66
|
print_download_start if @verbose
|
|
64
67
|
do_download_file
|
|
65
68
|
rescue Down::Error => e
|
|
66
|
-
|
|
69
|
+
if @tries < max_retries
|
|
70
|
+
sleep(backoff_time(@tries))
|
|
71
|
+
retry
|
|
72
|
+
end
|
|
67
73
|
|
|
68
74
|
raise Fontist::Errors::InvalidResourceError,
|
|
69
75
|
"Invalid URL: #{@file}. Error: #{e.inspect}."
|
|
70
76
|
end
|
|
71
77
|
|
|
78
|
+
def max_retries
|
|
79
|
+
@max_retries ||= 3
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def backoff_time(attempt)
|
|
83
|
+
# Exponential backoff: 2^attempt seconds, max 30 seconds
|
|
84
|
+
# 1st retry: 2s, 2nd: 4s, 3rd: 8s
|
|
85
|
+
[2**attempt, 30].min
|
|
86
|
+
end
|
|
87
|
+
|
|
72
88
|
def print_download_start
|
|
73
89
|
Fontist.ui.say("Downloading from: #{Paint[url, :cyan]}")
|
|
74
90
|
if @verbose
|
|
@@ -115,8 +131,10 @@ module Fontist
|
|
|
115
131
|
# rubocop:enable Metrics/MethodLength
|
|
116
132
|
|
|
117
133
|
def url
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
@url ||= begin
|
|
135
|
+
raw_url = extract_raw_url
|
|
136
|
+
github_aware_url(raw_url)
|
|
137
|
+
end
|
|
120
138
|
end
|
|
121
139
|
|
|
122
140
|
def headers
|
|
@@ -126,6 +144,22 @@ module Fontist
|
|
|
126
144
|
obj.headers.to_h.map { |k, v| [k.to_s, v] }.to_h || # rubocop:disable Style/HashTransformKeys, Metrics/LineLength
|
|
127
145
|
{}
|
|
128
146
|
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def extract_raw_url
|
|
151
|
+
obj = Helpers.url_object(@file)
|
|
152
|
+
obj.respond_to?(:url) ? obj.url : obj
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def github_aware_url(raw_url)
|
|
156
|
+
parsed = GitHubUrl.parse(raw_url)
|
|
157
|
+
if parsed.matched?
|
|
158
|
+
GitHubClient.authenticated_download_url(parsed)
|
|
159
|
+
else
|
|
160
|
+
raw_url
|
|
161
|
+
end
|
|
162
|
+
end
|
|
129
163
|
end
|
|
130
164
|
|
|
131
165
|
class ProgressBar
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "octokit"
|
|
2
|
+
|
|
3
|
+
module Fontist
|
|
4
|
+
module Utils
|
|
5
|
+
class GitHubClient
|
|
6
|
+
class << self
|
|
7
|
+
def authenticated_download_url(parsed_url)
|
|
8
|
+
return parsed_url.original_url unless parsed_url.matched?
|
|
9
|
+
|
|
10
|
+
client = create_client
|
|
11
|
+
release = fetch_release(client, parsed_url)
|
|
12
|
+
|
|
13
|
+
find_asset_url(release, parsed_url.asset) || parsed_url.original_url
|
|
14
|
+
rescue Octokit::Error => e
|
|
15
|
+
Fontist.ui.say("GitHub API error: #{e.message}. Falling back to direct download.")
|
|
16
|
+
parsed_url.original_url
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def create_client
|
|
22
|
+
if github_token
|
|
23
|
+
Octokit::Client.new(access_token: github_token)
|
|
24
|
+
else
|
|
25
|
+
Octokit::Client.new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def github_token
|
|
30
|
+
ENV.fetch("GITHUB_TOKEN", nil)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fetch_release(client, parsed_url)
|
|
34
|
+
client.release_for_tag("#{parsed_url.owner}/#{parsed_url.repo}", parsed_url.tag)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def find_asset_url(release, asset_name)
|
|
38
|
+
release.assets.find { |asset| asset.name == asset_name }&.browser_download_url
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Fontist
|
|
2
|
+
module Utils
|
|
3
|
+
class GitHubUrl
|
|
4
|
+
GITHUB_RELEASE_PATTERN =
|
|
5
|
+
%r{^https?://github\.com/(?<owner>[^/]+)/(?<repo>[^/]+)/releases/download/(?<tag>[^/]+)/(?<asset>.+)$}
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def match?(url)
|
|
9
|
+
parse(url).matched?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def parse(url)
|
|
13
|
+
url_string = url.to_s
|
|
14
|
+
match = url_string.match(GITHUB_RELEASE_PATTERN)
|
|
15
|
+
|
|
16
|
+
if match
|
|
17
|
+
ParsedUrl.new(
|
|
18
|
+
owner: match[:owner],
|
|
19
|
+
repo: match[:repo],
|
|
20
|
+
tag: match[:tag],
|
|
21
|
+
asset: match[:asset],
|
|
22
|
+
original_url: url_string
|
|
23
|
+
)
|
|
24
|
+
else
|
|
25
|
+
ParsedUrl.from_non_github_url(url_string)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class ParsedUrl
|
|
31
|
+
attr_reader :owner, :repo, :tag, :asset, :original_url
|
|
32
|
+
|
|
33
|
+
def initialize(owner:, repo:, tag:, asset:, original_url:)
|
|
34
|
+
@owner = owner
|
|
35
|
+
@repo = repo
|
|
36
|
+
@tag = tag
|
|
37
|
+
@asset = asset
|
|
38
|
+
@original_url = original_url
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.from_non_github_url(original_url)
|
|
42
|
+
new(owner: nil, repo: nil, tag: nil, asset: nil, original_url: original_url)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def matched?
|
|
46
|
+
!owner.nil?
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/fontist/version.rb
CHANGED
data/lib/fontist.rb
CHANGED
|
@@ -188,9 +188,12 @@ module Fontist
|
|
|
188
188
|
def self.formulas_repo_path_exists!
|
|
189
189
|
return true if Dir.exist?(Fontist.formulas_repo_path.join("Formulas"))
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
# Auto-update formulas repo if it doesn't exist (lazy initialization).
|
|
192
|
+
# This ensures formulas are always discoverable without requiring
|
|
193
|
+
# explicit `fontist update`.
|
|
194
|
+
Formula.update_formulas_repo
|
|
195
|
+
|
|
196
|
+
true
|
|
194
197
|
end
|
|
195
198
|
end
|
|
196
199
|
|
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: 2.1.
|
|
4
|
+
version: 2.1.2
|
|
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-02-
|
|
11
|
+
date: 2026-02-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: down
|
|
@@ -120,6 +120,20 @@ dependencies:
|
|
|
120
120
|
- - "~>"
|
|
121
121
|
- !ruby/object:Gem::Version
|
|
122
122
|
version: '0.7'
|
|
123
|
+
- !ruby/object:Gem::Dependency
|
|
124
|
+
name: lutaml-xsd
|
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - "~>"
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '1.0'
|
|
130
|
+
type: :runtime
|
|
131
|
+
prerelease: false
|
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - "~>"
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '1.0'
|
|
123
137
|
- !ruby/object:Gem::Dependency
|
|
124
138
|
name: marcel
|
|
125
139
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -148,6 +162,20 @@ dependencies:
|
|
|
148
162
|
- - "~>"
|
|
149
163
|
- !ruby/object:Gem::Version
|
|
150
164
|
version: '1.0'
|
|
165
|
+
- !ruby/object:Gem::Dependency
|
|
166
|
+
name: octokit
|
|
167
|
+
requirement: !ruby/object:Gem::Requirement
|
|
168
|
+
requirements:
|
|
169
|
+
- - "~>"
|
|
170
|
+
- !ruby/object:Gem::Version
|
|
171
|
+
version: '4.0'
|
|
172
|
+
type: :runtime
|
|
173
|
+
prerelease: false
|
|
174
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - "~>"
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '4.0'
|
|
151
179
|
- !ruby/object:Gem::Dependency
|
|
152
180
|
name: paint
|
|
153
181
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -242,6 +270,7 @@ extra_rdoc_files: []
|
|
|
242
270
|
files:
|
|
243
271
|
- ".github/workflows/deploy-pages.yml"
|
|
244
272
|
- ".github/workflows/discover-fonts.yml"
|
|
273
|
+
- ".github/workflows/formulas-auto-update-test.yml"
|
|
245
274
|
- ".github/workflows/post-rake.yml"
|
|
246
275
|
- ".github/workflows/rake-metanorma.yaml"
|
|
247
276
|
- ".github/workflows/rake.yml"
|
|
@@ -403,6 +432,8 @@ files:
|
|
|
403
432
|
- lib/fontist/utils/downloader.rb
|
|
404
433
|
- lib/fontist/utils/file_magic.rb
|
|
405
434
|
- lib/fontist/utils/file_ops.rb
|
|
435
|
+
- lib/fontist/utils/github_client.rb
|
|
436
|
+
- lib/fontist/utils/github_url.rb
|
|
406
437
|
- lib/fontist/utils/locking.rb
|
|
407
438
|
- lib/fontist/utils/system.rb
|
|
408
439
|
- lib/fontist/utils/ui.rb
|