gembrew 0.1.0
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 +7 -0
- data/LICENSE +22 -0
- data/README.md +105 -0
- data/bin/gembrew +17 -0
- data/lib/gembrew/archive_store.rb +91 -0
- data/lib/gembrew/cli.rb +23 -0
- data/lib/gembrew/commands/build.rb +18 -0
- data/lib/gembrew/commands/check.rb +21 -0
- data/lib/gembrew/commands/init.rb +20 -0
- data/lib/gembrew/commands/shell.rb +17 -0
- data/lib/gembrew/config.rb +70 -0
- data/lib/gembrew/docker_check.rb +40 -0
- data/lib/gembrew/docker_shell.rb +39 -0
- data/lib/gembrew/error.rb +3 -0
- data/lib/gembrew/generator.rb +54 -0
- data/lib/gembrew/initializer.rb +63 -0
- data/lib/gembrew/renderer.rb +18 -0
- data/lib/gembrew/reporter.rb +40 -0
- data/lib/gembrew/resolver.rb +99 -0
- data/lib/gembrew/templates/compose.yaml.erb +29 -0
- data/lib/gembrew/templates/formula.rb.erb +39 -0
- data/lib/gembrew/templates/gembrew.yml.erb +15 -0
- data/lib/gembrew/version.rb +3 -0
- data/lib/gembrew.rb +11 -0
- metadata +110 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 46aaf0e8b85829162de73512e6c9fe6c892a7dc646513a6cd6d6d9265c06124b
|
|
4
|
+
data.tar.gz: 90de5846e5cc45f8e0458d0c0dcf6368ea4071595b152b72279cff91b4e349f1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 40a61361c893f96bc402ef4c4514236cb7ec4313386f3378e1dfcf026a4726a2fbfec786f6ccdd8120495d7c80adfe240f3b182e79d3890e5b2e6bfa2bff57ca
|
|
7
|
+
data.tar.gz: edf9bd6364b9c2409b43d1777739b9591d02adaee91475b72203b64dbfdd5c75f566aab6d632aa1998318f745120ccfb5102ad7f937b6f9be323817521e575e4
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Danny Ben Shitrit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Gembrew
|
|
2
|
+
|
|
3
|
+
Generate conventional Homebrew formulae for published Ruby command-line gems.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```shell
|
|
8
|
+
gem install gembrew
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
mkdir homebrew-tap
|
|
15
|
+
cd homebrew-tap
|
|
16
|
+
gembrew init GEM
|
|
17
|
+
gembrew build
|
|
18
|
+
gembrew check
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`init` requires an empty directory, with the exception of Git's `.git`
|
|
22
|
+
directory. It creates a tap repository containing `gembrew.yml`, `Formula/`,
|
|
23
|
+
and a Docker Compose environment under `support/`. `build` reads the
|
|
24
|
+
configuration and writes the formula.
|
|
25
|
+
|
|
26
|
+
The configuration filename is always `gembrew.yml`.
|
|
27
|
+
|
|
28
|
+
For example, `gembrew init example` generates:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
gem: example
|
|
32
|
+
output: Formula/example.rb
|
|
33
|
+
|
|
34
|
+
test: |-
|
|
35
|
+
system bin/"example", "--version"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Edit `gembrew.yml` as needed, then generate the formula:
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
gembrew build
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The required settings are `gem`, `output`, and exactly one of `test` or
|
|
45
|
+
`test_from_file`. Relative paths are resolved from `gembrew.yml`.
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
gem: example
|
|
49
|
+
output: Formula/example.rb
|
|
50
|
+
|
|
51
|
+
# Optional published gem version. The latest stable version is used by default.
|
|
52
|
+
version: "1.2.3"
|
|
53
|
+
|
|
54
|
+
# Optional gem metadata overrides:
|
|
55
|
+
desc: Example command-line application
|
|
56
|
+
homepage: https://example.com
|
|
57
|
+
license: MIT
|
|
58
|
+
executable: example
|
|
59
|
+
|
|
60
|
+
test: |-
|
|
61
|
+
system bin/"example", "--version"
|
|
62
|
+
|
|
63
|
+
# Use this instead of `test` to load the test body from another file:
|
|
64
|
+
# test_from_file: support/test.rb
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Gembrew resolves the generic Ruby dependency graph and generates a Homebrew
|
|
68
|
+
`resource` for every runtime dependency. Original `.gem` archives are reused
|
|
69
|
+
from RubyGems' local cache when available. Downloaded archives are retained in
|
|
70
|
+
`${XDG_CACHE_HOME:-~/.cache}/gembrew/gems`.
|
|
71
|
+
|
|
72
|
+
Open the generated Homebrew environment with:
|
|
73
|
+
|
|
74
|
+
```shell
|
|
75
|
+
gembrew shell
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The repository is mounted as a local tap, so formulae can be addressed by gem
|
|
79
|
+
name inside the shell:
|
|
80
|
+
|
|
81
|
+
```shell
|
|
82
|
+
brew style example
|
|
83
|
+
brew audit --new --online example
|
|
84
|
+
brew install --build-from-source example
|
|
85
|
+
brew test example
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run the complete workflow non-interactively in one clean container:
|
|
89
|
+
|
|
90
|
+
```shell
|
|
91
|
+
gembrew check
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This rebuilds the formula, then runs Homebrew style, online audit, source
|
|
95
|
+
installation, and the formula test. The generated Compose `check` service can
|
|
96
|
+
also be invoked directly.
|
|
97
|
+
|
|
98
|
+
## Contributing / Support
|
|
99
|
+
|
|
100
|
+
If you experience any issue, have a question or a suggestion, or if you wish
|
|
101
|
+
to contribute, feel free to [open an issue][issues].
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
[issues]: https://github.com/DannyBen/gembrew/issues
|
data/bin/gembrew
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'gembrew'
|
|
4
|
+
require 'gembrew/cli'
|
|
5
|
+
require 'colsole'
|
|
6
|
+
|
|
7
|
+
include Colsole
|
|
8
|
+
|
|
9
|
+
runner = Gembrew::CLI.runner
|
|
10
|
+
|
|
11
|
+
begin
|
|
12
|
+
exit runner.run ARGV
|
|
13
|
+
rescue => e
|
|
14
|
+
puts e.backtrace.reverse if ENV['DEBUG']
|
|
15
|
+
say! "rib` #{e.class} `\n#{e.message}"
|
|
16
|
+
exit 1
|
|
17
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'rubygems/package'
|
|
5
|
+
require 'gembrew/error'
|
|
6
|
+
require 'gembrew/reporter'
|
|
7
|
+
|
|
8
|
+
module Gembrew
|
|
9
|
+
class ArchiveStore
|
|
10
|
+
attr_reader :cache_path
|
|
11
|
+
|
|
12
|
+
def initialize(cache_path:, reporter: Reporter.new, command_runner: nil)
|
|
13
|
+
@cache_path = Pathname(cache_path)
|
|
14
|
+
@reporter = reporter
|
|
15
|
+
@command_runner = command_runner || method(:run_command)
|
|
16
|
+
@cache_path.mkpath
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def fetch(name, version, download_path, index: nil, total: nil)
|
|
20
|
+
if (path = locally_cached(name, version))
|
|
21
|
+
report :rubygems_cache, name, version, index: index, total: total
|
|
22
|
+
return path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if version && (path = gembrew_cached(name, version))
|
|
26
|
+
report :gembrew_cache, name, version, index: index, total: total
|
|
27
|
+
return path
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
download name, version, Pathname(download_path), index: index, total: total
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :reporter, :command_runner
|
|
36
|
+
|
|
37
|
+
def locally_cached(name, version)
|
|
38
|
+
return unless version
|
|
39
|
+
|
|
40
|
+
Gem.path.each do |gem_path|
|
|
41
|
+
path = Pathname(gem_path)/'cache'/"#{name}-#{version}.gem"
|
|
42
|
+
return path if valid_archive?(path, name, version)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def gembrew_cached(name, version)
|
|
49
|
+
path = cache_path/"#{name}-#{version}.gem"
|
|
50
|
+
path if valid_archive?(path, name, version)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def valid_archive?(path, name, version)
|
|
54
|
+
return false unless path.file?
|
|
55
|
+
|
|
56
|
+
spec = Gem::Package.new(path.to_s).spec
|
|
57
|
+
spec.name == name && spec.version == Gem::Version.new(version.to_s) &&
|
|
58
|
+
spec.platform == Gem::Platform::RUBY
|
|
59
|
+
rescue Gem::Package::Error
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def download(name, version, download_path, index:, total:)
|
|
64
|
+
report :downloading, name, version, index: index, total: total
|
|
65
|
+
before = download_path.children
|
|
66
|
+
command = ['gem', 'fetch', name, '--platform', 'ruby']
|
|
67
|
+
command.push '--version', version.to_s if version
|
|
68
|
+
command_runner.call(*command, chdir: download_path)
|
|
69
|
+
|
|
70
|
+
candidates = download_path.children - before
|
|
71
|
+
archive = candidates.find { |path| path.extname == '.gem' }
|
|
72
|
+
raise Error, "gem fetch did not produce a .gem file for #{name} #{version}" unless archive
|
|
73
|
+
|
|
74
|
+
spec = Gem::Package.new(archive.to_s).spec
|
|
75
|
+
cached_path = cache_path/"#{spec.name}-#{spec.version}.gem"
|
|
76
|
+
FileUtils.mv archive, cached_path, force: true
|
|
77
|
+
cached_path
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def report(state, name, version, index:, total:)
|
|
81
|
+
reporter.archive state, name, version, index: index, total: total
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def run_command(*command, chdir:)
|
|
85
|
+
result, status = Open3.capture2e(*command, chdir: chdir.to_s)
|
|
86
|
+
raise Error, result unless status.success?
|
|
87
|
+
|
|
88
|
+
result
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/gembrew/cli.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'mister_bin'
|
|
2
|
+
require 'gembrew/commands/build'
|
|
3
|
+
require 'gembrew/commands/check'
|
|
4
|
+
require 'gembrew/commands/init'
|
|
5
|
+
require 'gembrew/commands/shell'
|
|
6
|
+
require 'gembrew/version'
|
|
7
|
+
|
|
8
|
+
module Gembrew
|
|
9
|
+
class CLI
|
|
10
|
+
def self.runner
|
|
11
|
+
runner = MisterBin::Runner.new version: Gembrew::VERSION,
|
|
12
|
+
header: 'Gembrew - Homebrew Formula Generator for Ruby Gems',
|
|
13
|
+
footer: 'Run m`gembrew COMMAND --help` for more information'
|
|
14
|
+
|
|
15
|
+
runner.route 'init', to: Commands::Init
|
|
16
|
+
runner.route 'build', to: Commands::Build
|
|
17
|
+
runner.route 'check', to: Commands::Check
|
|
18
|
+
runner.route 'shell', to: Commands::Shell
|
|
19
|
+
|
|
20
|
+
runner
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'mister_bin'
|
|
2
|
+
require 'gembrew/generator'
|
|
3
|
+
|
|
4
|
+
module Gembrew
|
|
5
|
+
module Commands
|
|
6
|
+
class Build < MisterBin::Command
|
|
7
|
+
help 'Generate a Homebrew formula from gembrew.yml'
|
|
8
|
+
|
|
9
|
+
usage 'gembrew build'
|
|
10
|
+
usage 'gembrew build (-h|--help)'
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
output_path = Generator.new.call
|
|
14
|
+
say "Generated m`#{output_path}`"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'mister_bin'
|
|
2
|
+
require 'gembrew/docker_check'
|
|
3
|
+
require 'gembrew/generator'
|
|
4
|
+
|
|
5
|
+
module Gembrew
|
|
6
|
+
module Commands
|
|
7
|
+
class Check < MisterBin::Command
|
|
8
|
+
help 'Build and test the formula in a clean Homebrew environment'
|
|
9
|
+
|
|
10
|
+
usage 'gembrew check'
|
|
11
|
+
usage 'gembrew check (-h|--help)'
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
output_path = Generator.new.call
|
|
15
|
+
say "Generated m`#{output_path}`"
|
|
16
|
+
DockerCheck.new.call
|
|
17
|
+
say 'g`All checks passed`'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'mister_bin'
|
|
2
|
+
require 'gembrew/initializer'
|
|
3
|
+
|
|
4
|
+
module Gembrew
|
|
5
|
+
module Commands
|
|
6
|
+
class Init < MisterBin::Command
|
|
7
|
+
help 'Initialize a Homebrew tap repository for a Ruby gem'
|
|
8
|
+
|
|
9
|
+
usage 'gembrew init GEM'
|
|
10
|
+
usage 'gembrew init (-h|--help)'
|
|
11
|
+
|
|
12
|
+
param 'GEM', 'Published Ruby gem name'
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
Initializer.new(args['GEM']).call
|
|
16
|
+
say "Initialized m`#{Dir.pwd}` for bb`#{args['GEM']}`"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'mister_bin'
|
|
2
|
+
require 'gembrew/docker_shell'
|
|
3
|
+
|
|
4
|
+
module Gembrew
|
|
5
|
+
module Commands
|
|
6
|
+
class Shell < MisterBin::Command
|
|
7
|
+
help 'Open a Homebrew shell for this tap repository'
|
|
8
|
+
|
|
9
|
+
usage 'gembrew shell'
|
|
10
|
+
usage 'gembrew shell (-h|--help)'
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
DockerShell.new.call
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'gembrew/error'
|
|
4
|
+
|
|
5
|
+
module Gembrew
|
|
6
|
+
class Config
|
|
7
|
+
FILENAME = 'gembrew.yml'
|
|
8
|
+
ALLOWED_KEYS = %w[
|
|
9
|
+
gem version output desc homepage license executable test test_from_file
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
attr_reader :path
|
|
13
|
+
|
|
14
|
+
def initialize(project_path = Pathname.pwd)
|
|
15
|
+
@path = Pathname(project_path).expand_path/FILENAME
|
|
16
|
+
@data = load_data
|
|
17
|
+
validate
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def gem_name = data.fetch('gem').to_s
|
|
21
|
+
def version = data['version']&.to_s
|
|
22
|
+
def output_path = (path.dirname/data.fetch('output')).expand_path
|
|
23
|
+
def description = data['desc']
|
|
24
|
+
def homepage = data['homepage']
|
|
25
|
+
def license = data['license']
|
|
26
|
+
def executable = data['executable']
|
|
27
|
+
|
|
28
|
+
def test_body
|
|
29
|
+
@test_body ||= if data.has_key?('test')
|
|
30
|
+
data.fetch('test').to_s
|
|
31
|
+
else
|
|
32
|
+
test_path = path.dirname/data.fetch('test_from_file')
|
|
33
|
+
raise Error, "Test file does not exist: #{test_path}" unless test_path.file?
|
|
34
|
+
|
|
35
|
+
test_path.read
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
attr_reader :data
|
|
42
|
+
|
|
43
|
+
def load_data
|
|
44
|
+
raise Error, "Configuration does not exist: #{path}" unless path.file?
|
|
45
|
+
|
|
46
|
+
YAML.safe_load_file(path, aliases: false)
|
|
47
|
+
rescue Psych::Exception => e
|
|
48
|
+
raise Error, "Invalid configuration: #{e.message}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate
|
|
52
|
+
raise Error, 'Configuration must be a YAML mapping' unless data.is_a?(Hash)
|
|
53
|
+
|
|
54
|
+
unknown_keys = data.keys - ALLOWED_KEYS
|
|
55
|
+
raise Error, "Unknown configuration keys: #{unknown_keys.join(', ')}" if unknown_keys.any?
|
|
56
|
+
|
|
57
|
+
require_value 'gem'
|
|
58
|
+
require_value 'output'
|
|
59
|
+
|
|
60
|
+
return unless data.has_key?('test') == data.has_key?('test_from_file')
|
|
61
|
+
|
|
62
|
+
raise Error, 'Provide exactly one of test or test_from_file'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def require_value(key)
|
|
66
|
+
value = data[key]
|
|
67
|
+
raise Error, "#{key} is required" if value.nil? || value.to_s.empty?
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'gembrew/error'
|
|
3
|
+
|
|
4
|
+
module Gembrew
|
|
5
|
+
class DockerCheck
|
|
6
|
+
COMPOSE_PATH = Pathname('support/compose.yaml')
|
|
7
|
+
|
|
8
|
+
attr_reader :project_path
|
|
9
|
+
|
|
10
|
+
def initialize(project_path: Pathname.pwd, command_runner: nil)
|
|
11
|
+
@project_path = Pathname(project_path).expand_path
|
|
12
|
+
@command_runner = command_runner || method(:run_command)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
raise Error, "Compose file does not exist: #{compose_path}" unless compose_path.file?
|
|
17
|
+
|
|
18
|
+
success = command_runner.call(
|
|
19
|
+
'docker', 'compose', '-f', COMPOSE_PATH.to_s,
|
|
20
|
+
'run', '--rm', '--no-TTY', 'check',
|
|
21
|
+
chdir: project_path
|
|
22
|
+
)
|
|
23
|
+
raise Error, 'Homebrew checks failed' unless success
|
|
24
|
+
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
attr_reader :command_runner
|
|
31
|
+
|
|
32
|
+
def compose_path
|
|
33
|
+
project_path/COMPOSE_PATH
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run_command(*command, chdir:)
|
|
37
|
+
system(*command, chdir: chdir.to_s)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'gembrew/error'
|
|
3
|
+
|
|
4
|
+
module Gembrew
|
|
5
|
+
class DockerShell
|
|
6
|
+
COMPOSE_PATH = Pathname('support/compose.yaml')
|
|
7
|
+
|
|
8
|
+
attr_reader :project_path
|
|
9
|
+
|
|
10
|
+
def initialize(project_path: Pathname.pwd, command_runner: nil)
|
|
11
|
+
@project_path = Pathname(project_path).expand_path
|
|
12
|
+
@command_runner = command_runner || method(:run_command)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
raise Error, "Compose file does not exist: #{compose_path}" unless compose_path.file?
|
|
17
|
+
|
|
18
|
+
success = command_runner.call(
|
|
19
|
+
'docker', 'compose', '-f', COMPOSE_PATH.to_s, 'run', '--rm', 'brew',
|
|
20
|
+
chdir: project_path
|
|
21
|
+
)
|
|
22
|
+
raise Error, 'Homebrew shell exited with an error' unless success
|
|
23
|
+
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :command_runner
|
|
30
|
+
|
|
31
|
+
def compose_path
|
|
32
|
+
project_path/COMPOSE_PATH
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def run_command(*command, chdir:)
|
|
36
|
+
system(*command, chdir: chdir.to_s)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'gembrew/config'
|
|
2
|
+
require 'gembrew/renderer'
|
|
3
|
+
require 'gembrew/resolver'
|
|
4
|
+
|
|
5
|
+
module Gembrew
|
|
6
|
+
class Generator
|
|
7
|
+
def initialize(config: Config.new, resolver: Resolver.new, renderer: Renderer.new)
|
|
8
|
+
@config = config
|
|
9
|
+
@resolver = resolver
|
|
10
|
+
@renderer = renderer
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
resolution = resolver.resolve(config.gem_name, config.version)
|
|
15
|
+
output_path = config.output_path
|
|
16
|
+
output_path.dirname.mkpath
|
|
17
|
+
output_path.write renderer.render(formula_data(resolution))
|
|
18
|
+
output_path
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :config, :resolver, :renderer
|
|
24
|
+
|
|
25
|
+
def formula_data(resolution)
|
|
26
|
+
spec = resolution.spec
|
|
27
|
+
{
|
|
28
|
+
formula_name: formula_name(spec.name),
|
|
29
|
+
gem_name: spec.name,
|
|
30
|
+
version: spec.version.to_s,
|
|
31
|
+
sha256: resolution.sha256,
|
|
32
|
+
resources: resolution.resources,
|
|
33
|
+
}.merge metadata(spec)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def metadata(spec)
|
|
37
|
+
{
|
|
38
|
+
description: config.description || spec.summary,
|
|
39
|
+
homepage: config.homepage || inferred_homepage(spec),
|
|
40
|
+
license: config.license || spec.license || spec.licenses.first,
|
|
41
|
+
executable: config.executable || spec.executables.first || spec.name,
|
|
42
|
+
test_body: config.test_body,
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def formula_name(gem_name)
|
|
47
|
+
gem_name.split(/[-_]/).map(&:capitalize).join
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def inferred_homepage(spec)
|
|
51
|
+
spec.homepage.to_s.empty? ? spec.metadata['homepage_uri'] : spec.homepage
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'erb'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'gembrew/error'
|
|
5
|
+
|
|
6
|
+
module Gembrew
|
|
7
|
+
class Initializer
|
|
8
|
+
CONFIG_FILENAME = 'gembrew.yml'
|
|
9
|
+
|
|
10
|
+
attr_reader :gem_name, :project_path
|
|
11
|
+
|
|
12
|
+
def initialize(gem_name, project_path: Pathname.pwd)
|
|
13
|
+
@gem_name = gem_name
|
|
14
|
+
@project_path = Pathname(project_path).expand_path
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
raise Error, "Directory is not empty: #{project_path} (found: #{entry_names.join(', ')})" if entries.any?
|
|
19
|
+
|
|
20
|
+
formula_path.mkpath
|
|
21
|
+
support_path.mkpath
|
|
22
|
+
config_path.write render
|
|
23
|
+
compose_path.write render('compose.yaml.erb')
|
|
24
|
+
|
|
25
|
+
project_path
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def config_path
|
|
31
|
+
project_path/CONFIG_FILENAME
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def compose_path
|
|
35
|
+
support_path/'compose.yaml'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def formula_path
|
|
39
|
+
project_path/'Formula'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def support_path
|
|
43
|
+
project_path/'support'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def entries
|
|
47
|
+
@entries ||= project_path.children.reject { |path| path.basename.to_s == '.git' }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def entry_names
|
|
51
|
+
entries.map { |path| path.basename.to_s }.sort
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render(filename = 'gembrew.yml.erb')
|
|
55
|
+
template = asset_path filename
|
|
56
|
+
ERB.new(template.read, trim_mode: '-').result(binding)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def asset_path(filename)
|
|
60
|
+
Pathname(__dir__)/'templates'/filename
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'erb'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
|
|
4
|
+
module Gembrew
|
|
5
|
+
class Renderer
|
|
6
|
+
def render(locals)
|
|
7
|
+
context = Object.new
|
|
8
|
+
locals.each { |name, value| context.define_singleton_method(name) { value } }
|
|
9
|
+
ERB.new(template_path.read, trim_mode: '-').result(context.instance_eval { binding })
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def template_path
|
|
15
|
+
Pathname(__dir__)/'templates/formula.rb.erb'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'colsole'
|
|
2
|
+
|
|
3
|
+
module Gembrew
|
|
4
|
+
class Reporter
|
|
5
|
+
include Colsole
|
|
6
|
+
|
|
7
|
+
LABELS = {
|
|
8
|
+
rubygems_cache: ['g', 'RubyGems cache'],
|
|
9
|
+
gembrew_cache: ['g', 'Gembrew cache'],
|
|
10
|
+
downloading: %w[y Downloading],
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def archive(state, name, version, index: nil, total: nil)
|
|
14
|
+
color, label = LABELS.fetch state
|
|
15
|
+
parts = []
|
|
16
|
+
parts << counter(index, total) if index && total
|
|
17
|
+
parts << "#{color}`#{label.ljust label_width}`"
|
|
18
|
+
parts << ["nb`#{name}`", version&.to_s].compact.join(' ')
|
|
19
|
+
say parts.join(' ')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def resolving(name, version)
|
|
23
|
+
say "Resolving dependencies for nb`#{name}` #{version}..."
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def collecting(count)
|
|
27
|
+
say "Collecting nb`#{count}` resources..."
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def counter(index, total)
|
|
33
|
+
"%#{total.to_s.length}d/%d" % [index, total]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def label_width
|
|
37
|
+
@label_width ||= LABELS.values.map { |_color, label| label.length }.max
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
require 'digest'
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require 'rubygems/package'
|
|
6
|
+
require 'tmpdir'
|
|
7
|
+
require 'gembrew/archive_store'
|
|
8
|
+
require 'gembrew/error'
|
|
9
|
+
require 'gembrew/reporter'
|
|
10
|
+
|
|
11
|
+
module Gembrew
|
|
12
|
+
Resource = Data.define(:name, :version, :sha256)
|
|
13
|
+
Resolution = Data.define(:spec, :sha256, :resources)
|
|
14
|
+
|
|
15
|
+
class Resolver
|
|
16
|
+
attr_reader :cache_root
|
|
17
|
+
|
|
18
|
+
def initialize(cache_root: nil, reporter: Reporter.new, command_runner: nil, archive_store: nil)
|
|
19
|
+
@cache_root = Pathname(cache_root || default_cache_root)
|
|
20
|
+
@reporter = reporter
|
|
21
|
+
@command_runner = command_runner || method(:run_command)
|
|
22
|
+
@archive_store = archive_store || ArchiveStore.new(
|
|
23
|
+
cache_path: @cache_root/'gems', reporter: reporter, command_runner: @command_runner
|
|
24
|
+
)
|
|
25
|
+
temporary_root.mkpath
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def resolve(name, version = nil)
|
|
29
|
+
Dir.mktmpdir('build-', temporary_root.to_s) do |directory|
|
|
30
|
+
temporary = Pathname(directory)
|
|
31
|
+
root_archive = archive_store.fetch(name, version, temporary)
|
|
32
|
+
root_spec = Gem::Package.new(root_archive.to_s).spec
|
|
33
|
+
dependencies = dependency_specs(lock(root_spec, temporary), root_spec)
|
|
34
|
+
|
|
35
|
+
reporter.collecting dependencies.length
|
|
36
|
+
resources = dependencies.each_with_index.map do |spec, index|
|
|
37
|
+
resource spec, temporary, index + 1, dependencies.length
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Resolution.new(root_spec, Digest::SHA256.file(root_archive).hexdigest, resources)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
attr_reader :reporter, :command_runner, :archive_store
|
|
47
|
+
|
|
48
|
+
def resource(spec, temporary, index, total)
|
|
49
|
+
archive = archive_store.fetch spec.name, spec.version, temporary, index: index, total: total
|
|
50
|
+
Resource.new spec.name, spec.version.to_s, Digest::SHA256.file(archive).hexdigest
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def default_cache_root
|
|
54
|
+
cache_home = ENV.fetch('XDG_CACHE_HOME', File.expand_path('~/.cache'))
|
|
55
|
+
Pathname(cache_home)/'gembrew'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def temporary_root
|
|
59
|
+
cache_root/'tmp'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def lock(root_spec, temporary)
|
|
63
|
+
gemfile = temporary/'Gemfile'
|
|
64
|
+
lockfile = temporary/'Gemfile.lock'
|
|
65
|
+
gemfile.write <<~RUBY
|
|
66
|
+
source "https://rubygems.org"
|
|
67
|
+
gem #{root_spec.name.inspect}, "= #{root_spec.version}"
|
|
68
|
+
RUBY
|
|
69
|
+
|
|
70
|
+
reporter.resolving root_spec.name, root_spec.version
|
|
71
|
+
command_runner.call(
|
|
72
|
+
'bundle', 'lock', '--gemfile', gemfile.to_s, '--lockfile', lockfile.to_s,
|
|
73
|
+
'--add-platform', 'ruby',
|
|
74
|
+
chdir: temporary,
|
|
75
|
+
env: {
|
|
76
|
+
'BUNDLE_FORCE_RUBY_PLATFORM' => 'true',
|
|
77
|
+
'BUNDLE_GEMFILE' => gemfile.to_s,
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
lockfile
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def dependency_specs(lockfile, root_spec)
|
|
84
|
+
specs = Bundler::LockfileParser.new(lockfile.read).specs
|
|
85
|
+
selected = specs.group_by(&:name).values.map do |variants|
|
|
86
|
+
variants.find { |spec| spec.platform == Gem::Platform::RUBY } || variants.first
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
selected.reject { |spec| spec.name == root_spec.name }.sort_by(&:name)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def run_command(*command, chdir:, env: {})
|
|
93
|
+
result, status = Open3.capture2e(env, *command, chdir: chdir.to_s)
|
|
94
|
+
raise Error, result unless status.success?
|
|
95
|
+
|
|
96
|
+
result
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
services:
|
|
2
|
+
brew: &base
|
|
3
|
+
image: homebrew/brew:main
|
|
4
|
+
working_dir: /work
|
|
5
|
+
environment:
|
|
6
|
+
HOMEBREW_NO_AUTO_UPDATE: "1"
|
|
7
|
+
HOMEBREW_NO_INSTALL_CLEANUP: "1"
|
|
8
|
+
HOMEBREW_NO_INSTALL_FROM_API: "1"
|
|
9
|
+
GEMBREW_GEM: <%= gem_name %>
|
|
10
|
+
PS1: "\n\nbrew $ "
|
|
11
|
+
volumes:
|
|
12
|
+
- ..:/work
|
|
13
|
+
- ..:/home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/gembrew/homebrew-tap
|
|
14
|
+
command: ["bash", "--norc"]
|
|
15
|
+
stdin_open: true
|
|
16
|
+
tty: true
|
|
17
|
+
|
|
18
|
+
check:
|
|
19
|
+
<<: *base
|
|
20
|
+
command:
|
|
21
|
+
- bash
|
|
22
|
+
- -euc
|
|
23
|
+
- |
|
|
24
|
+
brew style "$${GEMBREW_GEM}"
|
|
25
|
+
brew audit --new --online "$${GEMBREW_GEM}"
|
|
26
|
+
brew install --build-from-source "$${GEMBREW_GEM}"
|
|
27
|
+
brew test "$${GEMBREW_GEM}"
|
|
28
|
+
stdin_open: false
|
|
29
|
+
tty: false
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Generated by Gembrew: https://github.com/dannyben/gembrew
|
|
2
|
+
# Manual edits are not recommended. Update gembrew.yml and regenerate with:
|
|
3
|
+
# gem install gembrew
|
|
4
|
+
# gembrew build
|
|
5
|
+
|
|
6
|
+
class <%= formula_name %> < Formula
|
|
7
|
+
desc <%= description.inspect %>
|
|
8
|
+
homepage <%= homepage.inspect %>
|
|
9
|
+
url "https://rubygems.org/downloads/<%= gem_name %>-<%= version %>.gem"
|
|
10
|
+
sha256 "<%= sha256 %>"
|
|
11
|
+
license <%= license.inspect %>
|
|
12
|
+
|
|
13
|
+
depends_on "ruby"
|
|
14
|
+
<% resources.each do |resource| -%>
|
|
15
|
+
|
|
16
|
+
resource <%= resource.name.inspect %> do
|
|
17
|
+
url "https://rubygems.org/downloads/<%= resource.name %>-<%= resource.version %>.gem"
|
|
18
|
+
sha256 "<%= resource.sha256 %>"
|
|
19
|
+
end
|
|
20
|
+
<% end -%>
|
|
21
|
+
|
|
22
|
+
def install
|
|
23
|
+
ENV["GEM_HOME"] = libexec
|
|
24
|
+
|
|
25
|
+
resources.each do |resource|
|
|
26
|
+
system "gem", "install", resource.cached_download,
|
|
27
|
+
"--ignore-dependencies", "--install-dir", libexec, "--no-document"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
system "gem", "install", cached_download,
|
|
31
|
+
"--ignore-dependencies", "--install-dir", libexec, "--no-document"
|
|
32
|
+
|
|
33
|
+
(bin/<%= executable.inspect %>).write_env_script libexec/"bin/<%= executable %>", GEM_HOME: ENV.fetch("GEM_HOME")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test do
|
|
37
|
+
<%= test_body.lines.map { |line| " #{line}" }.join %>
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
gem: <%= gem_name %>
|
|
2
|
+
output: Formula/<%= gem_name %>.rb
|
|
3
|
+
|
|
4
|
+
test: |-
|
|
5
|
+
system bin/"<%= gem_name %>", "--version"
|
|
6
|
+
|
|
7
|
+
# Optional overrides:
|
|
8
|
+
# version: "1.2.3"
|
|
9
|
+
# desc: Example command-line application
|
|
10
|
+
# homepage: https://example.com
|
|
11
|
+
# license: MIT
|
|
12
|
+
# executable: <%= gem_name %>
|
|
13
|
+
|
|
14
|
+
# To load the Homebrew test body from a Ruby file, replace `test` above with:
|
|
15
|
+
# test_from_file: support/test.rb
|
data/lib/gembrew.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'gembrew/archive_store'
|
|
2
|
+
require 'gembrew/config'
|
|
3
|
+
require 'gembrew/docker_check'
|
|
4
|
+
require 'gembrew/docker_shell'
|
|
5
|
+
require 'gembrew/error'
|
|
6
|
+
require 'gembrew/generator'
|
|
7
|
+
require 'gembrew/initializer'
|
|
8
|
+
require 'gembrew/reporter'
|
|
9
|
+
require 'gembrew/renderer'
|
|
10
|
+
require 'gembrew/resolver'
|
|
11
|
+
require 'gembrew/version'
|
metadata
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gembrew
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Danny Ben Shitrit
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bundler
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: colsole
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: mister_bin
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.9'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.9'
|
|
54
|
+
description: Generate conventional Homebrew formulae for published Ruby command-line
|
|
55
|
+
gems
|
|
56
|
+
email: db@dannyben.com
|
|
57
|
+
executables:
|
|
58
|
+
- gembrew
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- LICENSE
|
|
63
|
+
- README.md
|
|
64
|
+
- bin/gembrew
|
|
65
|
+
- lib/gembrew.rb
|
|
66
|
+
- lib/gembrew/archive_store.rb
|
|
67
|
+
- lib/gembrew/cli.rb
|
|
68
|
+
- lib/gembrew/commands/build.rb
|
|
69
|
+
- lib/gembrew/commands/check.rb
|
|
70
|
+
- lib/gembrew/commands/init.rb
|
|
71
|
+
- lib/gembrew/commands/shell.rb
|
|
72
|
+
- lib/gembrew/config.rb
|
|
73
|
+
- lib/gembrew/docker_check.rb
|
|
74
|
+
- lib/gembrew/docker_shell.rb
|
|
75
|
+
- lib/gembrew/error.rb
|
|
76
|
+
- lib/gembrew/generator.rb
|
|
77
|
+
- lib/gembrew/initializer.rb
|
|
78
|
+
- lib/gembrew/renderer.rb
|
|
79
|
+
- lib/gembrew/reporter.rb
|
|
80
|
+
- lib/gembrew/resolver.rb
|
|
81
|
+
- lib/gembrew/templates/compose.yaml.erb
|
|
82
|
+
- lib/gembrew/templates/formula.rb.erb
|
|
83
|
+
- lib/gembrew/templates/gembrew.yml.erb
|
|
84
|
+
- lib/gembrew/version.rb
|
|
85
|
+
homepage: https://github.com/DannyBen/gembrew
|
|
86
|
+
licenses:
|
|
87
|
+
- MIT
|
|
88
|
+
metadata:
|
|
89
|
+
bug_tracker_uri: https://github.com/DannyBen/gembrew/issues
|
|
90
|
+
changelog_uri: https://github.com/DannyBen/gembrew/blob/master/CHANGELOG.md
|
|
91
|
+
source_code_uri: https://github.com/DannyBen/gembrew
|
|
92
|
+
rubygems_mfa_required: 'true'
|
|
93
|
+
rdoc_options: []
|
|
94
|
+
require_paths:
|
|
95
|
+
- lib
|
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - ">="
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '3.3'
|
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '0'
|
|
106
|
+
requirements: []
|
|
107
|
+
rubygems_version: 4.0.17
|
|
108
|
+
specification_version: 4
|
|
109
|
+
summary: Homebrew formula generator for Ruby gems
|
|
110
|
+
test_files: []
|