fontist 1.20.0 → 1.21.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b84dad2469698853e57c647d63890c7f054f888db3d7c53367e7c17ac3ca3951
4
- data.tar.gz: ebc3c36071d189946c187af9c7a1e340ed703dc37b1eb40b821ce7557b4f2b3a
3
+ metadata.gz: 1f3590888173d9be1196a8ca87bdefb0a8e0f4d7104f89d50bfac819d4890baf
4
+ data.tar.gz: 77dccd8e142c9f47dd1be79496d1c7d22185594ad3e0a0f927b94fc35c9f08c3
5
5
  SHA512:
6
- metadata.gz: 3cb8a0632dde13a856ac227b3dfd6af2082fa9e4a4c27357e94664cfb3861b26ed16af5dd0891eb1542d3ec2ae4d82dbc8f57c8ae593f39610c071885f6e45d3
7
- data.tar.gz: a792eceef4a821e48acce6ccb7d863a9fbc7602ac8197f488a927a1e1563947740ff4863a6162af004dbfcc40789433980ce31aed0b2a674d6b10d74009b3b7d
6
+ metadata.gz: 41d6c8bc63c8484fddd51fb3aeca3f6338a58a08b3c880a7016e15e0d43252183dd0a0e9ef743680a9010508c79bda7f4b9d17db5d7e71eae897060ab2278e3e
7
+ data.tar.gz: 8910f191f347e60de4f0cf1b1c8d68a30c934fb8e4e00880f13246f49f4f42de18024a4c22d4eadd7860b74fe102f021f902ab359c040c5cd1ce08a714c2ce5b
@@ -0,0 +1,21 @@
1
+ name: release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ next_version:
7
+ description: |
8
+ Next release version. Possible values: x.y.z, major, minor, patch (or pre|rc|etc).
9
+ Also, you can pass 'skip' to skip 'git tag' and do 'gem push' for the current version
10
+ required: true
11
+ default: 'skip'
12
+ repository_dispatch:
13
+ types: [ do-release ]
14
+
15
+ jobs:
16
+ release:
17
+ uses: fontist/support/.github/workflows/release.yml@main
18
+ with:
19
+ next_version: ${{ github.event.inputs.next_version }}
20
+ secrets:
21
+ rubygems-api-key: ${{ secrets.FONTIST_CI_RUBYGEMS_API_KEY }}
@@ -1,8 +1,8 @@
1
1
  name: tebako-pack
2
2
 
3
3
  on:
4
- push:
5
- tags: [ 'v*' ]
4
+ repository_dispatch:
5
+ types: [ do-release ]
6
6
  workflow_dispatch:
7
7
 
8
8
  concurrency:
@@ -16,6 +16,7 @@ env:
16
16
  # does not cause bundler gem reinstalls
17
17
  # bundler/rubygems 2.3.22 is a minimal requirement to support gnu/musl differentiation
18
18
  # https://github.com/rubygems/rubygems/pull/4488
19
+ GOOGLE_FONTS_API_KEY: ${{secrets.FONTIST_CI_GOOGLE_FONTS_API_KEY}}
19
20
 
20
21
  jobs:
21
22
  prepare:
@@ -35,7 +36,7 @@ jobs:
35
36
  matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
36
37
 
37
38
  steps:
38
- - uses: actions/checkout@v3
39
+ - uses: actions/checkout@v4
39
40
 
40
41
  - uses: ruby/setup-ruby@v1
41
42
  with:
@@ -59,6 +60,7 @@ jobs:
59
60
 
60
61
  archlinux-test:
61
62
  name: Test on Arch Linux
63
+ needs: prepare
62
64
  runs-on: ubuntu-latest
63
65
  container:
64
66
  image: 'archlinux:latest'
@@ -66,12 +68,18 @@ jobs:
66
68
  fail-fast: false
67
69
 
68
70
  steps:
69
- - uses: actions/checkout@v3
70
-
71
71
  - name: Setup packages
72
- run: pacman -Syu --noconfirm git ruby binutils gcc autoconf make libffi
72
+ run: pacman -Syu --noconfirm git binutils gcc autoconf make libffi libyaml gmp
73
+
74
+ - uses: actions/checkout@v4
75
+
76
+ - uses: asdf-vm/actions/install@v3
77
+ with:
78
+ tool_versions: ruby ${{ needs.prepare.outputs.default-ruby-version }}
73
79
 
74
- - run: bundle install
80
+ - run: |
81
+ gem install bundler
82
+ bundle install
75
83
 
76
84
  - name: Test
77
85
  run: bundle exec rspec --tag ~dev
@@ -83,14 +91,14 @@ jobs:
83
91
  needs: prepare
84
92
  if: needs.prepare.outputs.push-for-tag != 'true'
85
93
 
86
- continue-on-error: ${{ matrix.ruby.experimental || matrix.os == 'windows-latest' }} # workaround https://github.com/metanorma/metanorma/issues/288
94
+ continue-on-error: ${{ matrix.ruby.experimental }}
87
95
  strategy:
88
96
  fail-fast: false
89
97
  max-parallel: 5
90
98
  matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
91
99
 
92
100
  steps:
93
- - uses: actions/checkout@v3
101
+ - uses: actions/checkout@v4
94
102
  with:
95
103
  repository: metanorma/metanorma
96
104
 
@@ -117,14 +125,12 @@ jobs:
117
125
  runs-on: ubuntu-latest
118
126
  if: contains(github.ref, 'refs/tags/v')
119
127
  steps:
120
- - uses: actions/checkout@v3
121
-
122
- - uses: ruby/setup-ruby@v1
123
- with:
124
- ruby-version: 3.1
125
- bundler-cache: true
128
+ - uses: actions/checkout@v4
126
129
 
127
- - uses: actions-mn/gem-release@main
130
+ - name: Repository ready for release
131
+ uses: peter-evans/repository-dispatch@v3
128
132
  with:
129
- api-key: ${{ secrets.FONTIST_CI_RUBYGEMS_API_KEY }}
130
- release-command: bundle exec rake release
133
+ token: ${{ secrets.FONTIST_CI_PAT_TOKEN }}
134
+ repository: ${{ github.repository }}
135
+ event-type: do-release
136
+ client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "type": "do-release"}'
data/README.adoc CHANGED
@@ -1066,7 +1066,7 @@ Fontist's https://github.com/fontist/formulas[formula library] includes support
1066
1066
  for all openly-licensed fonts provided through Google Fonts, and maintains
1067
1067
  Fontist formulas for all such fonts.
1068
1068
 
1069
- https://github.com/fontist/formulas/blob/v3/.github/workflows/google.yml[A GHA
1069
+ https://github.com/fontist/formulas/blob/v4/.github/workflows/google.yml[A GHA
1070
1070
  workflow] checks for updated fonts on Google Fonts daily. In case an update is
1071
1071
  found, it's added to the repo by the workflow.
1072
1072
 
data/exe/fontist CHANGED
@@ -4,8 +4,7 @@ require "fontist"
4
4
  require "fontist/cli"
5
5
 
6
6
  fontist_cli = proc {
7
- status_code = Fontist::CLI.start(ARGV)
8
- exit status_code.is_a?(Integer) ? status_code : 1
7
+ Fontist::CLI.start(ARGV)
9
8
  }
10
9
 
11
10
  if ENV["SOCKS_PROXY"]
data/fontist.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_runtime_dependency "nokogiri", "~> 1.0"
37
37
  spec.add_runtime_dependency "mime-types", "~> 3.0"
38
38
  spec.add_runtime_dependency "sys-uname", "~> 1.2"
39
- spec.add_runtime_dependency "thor", "~> 1.2.1"
39
+ spec.add_runtime_dependency "thor", "~> 1.2", ">= 1.2.1"
40
40
  spec.add_runtime_dependency "git", "~> 1.0"
41
41
  spec.add_runtime_dependency "ttfunk", "~> 1.6"
42
42
  spec.add_runtime_dependency "plist", "~> 3.0"
@@ -0,0 +1,79 @@
1
+ require "fontist/utils/ui"
2
+
3
+ module Fontist
4
+ module ThorExt
5
+ # Sources:
6
+ # - https://github.com/mattbrictson/gem/blob/main/lib/example/thor_ext.rb
7
+ # - https://mattbrictson.com/blog/fixing-thor-cli-behavior
8
+ #
9
+ # Configures Thor to behave more like a typical CLI, with better help
10
+ # and error handling.
11
+ #
12
+ # - Passing -h or --help to a command will show help for that command.
13
+ # - Unrecognized options will be treated as errors.
14
+ # - Error messages will be printed in red to stderr, without stack trace.
15
+ # - Errors will cause Thor to exit with a non-zero status.
16
+ #
17
+ # To take advantage of this behavior, your CLI should subclass Thor
18
+ # and extend this module.
19
+ #
20
+ # class CLI < Thor
21
+ # extend ThorExt::Start
22
+ # end
23
+ #
24
+ # Start your CLI with:
25
+ #
26
+ # CLI.start
27
+ #
28
+ # In tests, prevent Kernel.exit from being called when an error occurs,
29
+ # like this:
30
+ #
31
+ # CLI.start(args, exit_on_failure: false)
32
+ module Start
33
+ def self.extended(base)
34
+ super
35
+ base.check_unknown_options!
36
+ end
37
+
38
+ def start(given_args = ARGV, config = {})
39
+ config[:shell] ||= Thor::Base.shell.new
40
+ handle_help_switches(given_args) do |args|
41
+ dispatch(nil, args, nil, config)
42
+ end
43
+ rescue StandardError => e
44
+ handle_exception_on_start(e, config)
45
+ end
46
+
47
+ private
48
+
49
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
50
+ def handle_help_switches(given_args)
51
+ yield(given_args.dup)
52
+ rescue Thor::UnknownArgumentError => e
53
+ retry_with_args = []
54
+
55
+ if given_args.first == "help"
56
+ retry_with_args = ["help"] if given_args.length > 1
57
+ elsif e.unknown.intersect?(%w[-h --help])
58
+ retry_with_args = ["help", (given_args - e.unknown).first]
59
+ end
60
+ raise unless retry_with_args.any?
61
+
62
+ yield(retry_with_args)
63
+ end
64
+
65
+ def handle_exception_on_start(error, config)
66
+ return if error.is_a?(Errno::EPIPE)
67
+ raise if Fontist.ui.debug? || !config.fetch(:exit_on_failure, true)
68
+
69
+ message = error.message.to_s
70
+ if message.empty? || !error.is_a?(Thor::Error)
71
+ message.prepend("[#{error.class}] ")
72
+ end
73
+ config[:shell]&.say_error(message, :red)
74
+ exit(false)
75
+ end
76
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
77
+ end
78
+ end
79
+ end
data/lib/fontist/cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "thor"
2
2
  require "fontist/cli/class_options"
3
+ require "fontist/cli/thor_ext"
3
4
  require "fontist/repo_cli"
4
5
  require "fontist/cache_cli"
5
6
  require "fontist/import_cli"
@@ -9,6 +10,7 @@ require "fontist/config_cli"
9
10
  module Fontist
10
11
  class CLI < Thor
11
12
  include ClassOptions
13
+ extend ThorExt::Start
12
14
 
13
15
  STATUS_SUCCESS = 0
14
16
  STATUS_UNKNOWN_ERROR = 1
@@ -44,7 +44,8 @@ module Fontist
44
44
  def default_values
45
45
  { fonts_path: Fontist.fontist_path.join("fonts"),
46
46
  open_timeout: 10,
47
- read_timeout: 10 }
47
+ read_timeout: 10,
48
+ google_fonts_key: nil }
48
49
  end
49
50
 
50
51
  def persist
data/lib/fontist/font.rb CHANGED
@@ -216,6 +216,11 @@ module Fontist
216
216
  confirmation = check_and_confirm_required_license(formula)
217
217
  paths = font_installer(formula).install(confirmation: confirmation)
218
218
 
219
+ if paths.nil? || paths.empty?
220
+ Fontist.ui.error("Fonts not found in formula #{formula}")
221
+ return
222
+ end
223
+
219
224
  Fontist.ui.say("Fonts installed at:")
220
225
  paths.each do |path|
221
226
  Fontist.ui.say("- #{path}")
@@ -1,5 +1,7 @@
1
1
  require "fontist/utils"
2
2
  require "excavate"
3
+ require_relative "resources/archive_resource"
4
+ require_relative "resources/google_resource"
3
5
 
4
6
  module Fontist
5
7
  class FontInstaller
@@ -48,63 +50,32 @@ module Fontist
48
50
  end
49
51
 
50
52
  def install_font
51
- fonts_paths = run_in_temp_dir { extract }
53
+ fonts_paths = do_install_font
52
54
  fonts_paths.empty? ? nil : fonts_paths
53
55
  end
54
56
 
55
- def run_in_temp_dir
56
- Dir.mktmpdir(nil, Dir.tmpdir) do |dir|
57
- @temp_dir = Pathname.new(dir)
58
-
59
- result = yield
60
-
61
- @temp_dir = nil
62
-
63
- result
64
- end
65
- end
66
-
67
- def extract
68
- archive = download_file(@formula.resources.first)
69
-
70
- install_fonts_from_archive(archive)
71
- end
72
-
73
- def install_fonts_from_archive(archive)
74
- Fontist.ui.say(%(Installing font "#{@formula.key}".))
57
+ def do_install_font
58
+ Fontist.ui.say(%(Installing from formula "#{@formula.key}".))
75
59
 
76
60
  Array.new.tap do |fonts_paths|
77
- Excavate::Archive.new(archive.path).files(recursive_packages: true) do |path|
61
+ resource.files(source_files) do |path|
78
62
  fonts_paths << install_font_file(path) if font_file?(path)
79
63
  end
80
64
  end
81
65
  end
82
66
 
83
- def download_file(source)
84
- errors = []
85
- source.urls.each do |request|
86
- url = request.respond_to?(:url) ? request.url : request
87
- Fontist.ui.say(%(Downloading font "#{@formula.key}" from #{url}))
88
-
89
- result = try_download_file(request, source)
90
- return result unless result.is_a?(Errors::InvalidResourceError)
91
-
92
- errors << result
93
- end
67
+ def resource
68
+ resource_class = if @formula.source == "google"
69
+ Resources::GoogleResource
70
+ else
71
+ Resources::ArchiveResource
72
+ end
94
73
 
95
- raise Errors::InvalidResourceError, errors.join(" ")
74
+ resource_class.new(resource_options, no_progress: @no_progress)
96
75
  end
97
76
 
98
- def try_download_file(request, source)
99
- Fontist::Utils::Downloader.download(
100
- request,
101
- sha: source.sha256,
102
- file_size: source.file_size,
103
- progress_bar: !@no_progress
104
- )
105
- rescue Errors::InvalidResourceError => e
106
- Fontist.ui.say(e.message)
107
- e
77
+ def resource_options
78
+ @formula.resources.first
108
79
  end
109
80
 
110
81
  def font_file?(path)
@@ -116,16 +87,16 @@ module Fontist
116
87
  end
117
88
 
118
89
  def source_files
119
- @source_files ||= @formula.fonts.flat_map do |font|
120
- next [] if @font_name && !font.name.casecmp?(@font_name)
121
-
122
- font_files(font)
90
+ @source_files ||= fonts.flat_map do |font|
91
+ font.styles.map do |style|
92
+ style.source_font || style.font
93
+ end
123
94
  end
124
95
  end
125
96
 
126
- def font_files(font)
127
- font.styles.map do |style|
128
- style.source_font || style.font
97
+ def fonts
98
+ @formula.fonts.select do |font|
99
+ @font_name.nil? || font.name.casecmp?(@font_name)
129
100
  end
130
101
  end
131
102
 
@@ -109,6 +109,12 @@ module Fontist
109
109
  @data.key?("resources")
110
110
  end
111
111
 
112
+ def source
113
+ return unless @data["resources"]
114
+
115
+ @data["resources"].values.first["source"]
116
+ end
117
+
112
118
  def path
113
119
  @path
114
120
  end
@@ -1,3 +1,5 @@
1
+ require "ostruct"
2
+
1
3
  module Fontist
2
4
  module Helpers
3
5
  def self.parse_to_object(data)
@@ -1,6 +1,5 @@
1
1
  require "fontist/import"
2
2
  require_relative "recursive_extraction"
3
- require_relative "helpers/hash_helper"
4
3
  require_relative "formula_builder"
5
4
 
6
5
  module Fontist
@@ -12,31 +11,102 @@ module Fontist
12
11
  end
13
12
 
14
13
  def call
15
- save(builder)
14
+ builder.save
16
15
  end
17
16
 
18
17
  private
19
18
 
20
19
  def builder
21
20
  builder = FormulaBuilder.new
22
- setup_strings(builder, archive)
21
+ setup_strings(builder)
23
22
  setup_files(builder)
24
23
  builder
25
24
  end
26
25
 
27
- def setup_strings(builder, archive)
28
- builder.archive = archive
29
- builder.url = @url
26
+ def setup_strings(builder)
30
27
  builder.options = @options
28
+ builder.resources = resources
31
29
  end
32
30
 
33
31
  def setup_files(builder)
34
- builder.extractor = extractor
32
+ builder.operations = extractor.operations
35
33
  builder.font_files = extractor.font_files
36
34
  builder.font_collection_files = extractor.font_collection_files
37
35
  builder.license_text = extractor.license_text
38
36
  end
39
37
 
38
+ def resources
39
+ @resources ||= { filename(archive) => resource_options }
40
+ end
41
+
42
+ def filename(file)
43
+ if file.respond_to?(:original_filename)
44
+ file.original_filename
45
+ else
46
+ File.basename(file)
47
+ end
48
+ end
49
+
50
+ def resource_options
51
+ if @options[:skip_sha]
52
+ resource_options_without_sha
53
+ else
54
+ resource_options_with_sha
55
+ end
56
+ end
57
+
58
+ def resource_options_without_sha
59
+ { urls: [@url] + mirrors, file_size: file_size }
60
+ end
61
+
62
+ def resource_options_with_sha
63
+ urls = []
64
+ sha = []
65
+ downloads do |url, path|
66
+ urls << url
67
+ sha << Digest::SHA256.file(path).to_s
68
+ end
69
+
70
+ sha = prepare_sha256(sha)
71
+
72
+ { urls: urls, sha256: sha, file_size: file_size }
73
+ end
74
+
75
+ def downloads
76
+ yield @url, archive
77
+
78
+ mirrors.each do |url|
79
+ path = download_mirror(url)
80
+ next unless path
81
+
82
+ yield url, path
83
+ end
84
+ end
85
+
86
+ def mirrors
87
+ @options[:mirror] || []
88
+ end
89
+
90
+ def download_mirror(url)
91
+ Fontist::Utils::Downloader.download(url, progress_bar: true).path
92
+ rescue Errors::InvalidResourceError
93
+ Fontist.ui.error("WARN: a mirror is not found '#{url}'")
94
+ nil
95
+ end
96
+
97
+ def prepare_sha256(input)
98
+ output = input.uniq
99
+ return output.first if output.size == 1
100
+
101
+ checksums = output.join(", ")
102
+ Fontist.ui.error("WARN: SHA256 differs (#{checksums})")
103
+ output
104
+ end
105
+
106
+ def file_size
107
+ File.size(archive)
108
+ end
109
+
40
110
  def extractor
41
111
  @extractor ||=
42
112
  RecursiveExtraction.new(archive,
@@ -53,34 +123,6 @@ module Fontist
53
123
 
54
124
  Fontist::Utils::Downloader.download(url, progress_bar: true).path
55
125
  end
56
-
57
- def save(builder)
58
- path = vacant_path
59
- yaml = YAML.dump(Helpers::HashHelper.stringify_keys(builder.formula))
60
- File.write(path, yaml)
61
- path
62
- end
63
-
64
- def vacant_path
65
- path = path_from_name
66
- return path unless @options[:keep_existing] && File.exist?(path)
67
-
68
- 2.upto(9) do |i|
69
- candidate = path.sub(/\.yml$/, "#{i}.yml")
70
- return candidate unless File.exist?(candidate)
71
- end
72
-
73
- raise Errors::GeneralError, "Formula #{path} already exists."
74
- end
75
-
76
- def path_from_name
77
- filename = Import.name_to_filename(builder.name)
78
- if @options[:formula_dir]
79
- File.join(@options[:formula_dir], filename)
80
- else
81
- filename
82
- end
83
- end
84
126
  end
85
127
  end
86
128
  end