homebrew_automation 0.0.9 → 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 +4 -4
- data/bin/homebrew_automation.rb +2 -96
- data/lib/homebrew_automation.rb +1 -4
- data/lib/homebrew_automation/bintray.rb +2 -121
- data/lib/homebrew_automation/bintray/client.rb +125 -0
- data/lib/homebrew_automation/bintray/version.rb +83 -0
- data/lib/homebrew_automation/bottle.rb +1 -0
- data/lib/homebrew_automation/cli.rb +2 -0
- data/lib/homebrew_automation/cli/cli.rb +22 -0
- data/lib/homebrew_automation/cli/formula_commands.rb +31 -0
- data/lib/homebrew_automation/cli/workflow_commands.rb +84 -0
- data/lib/homebrew_automation/formula.rb +26 -0
- data/lib/homebrew_automation/{mac-os.rb → mac_os.rb} +6 -1
- data/lib/homebrew_automation/source_dist.rb +5 -5
- data/lib/homebrew_automation/tap.rb +6 -6
- data/lib/homebrew_automation/version.rb +1 -1
- data/lib/homebrew_automation/workflow.rb +35 -79
- metadata +25 -5
- data/lib/homebrew_automation/bottle_gatherer.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 240ff77ce84a9af442b04b396ddba4f32014d95d02ebf7fda5ff5904e71aa8bf
|
4
|
+
data.tar.gz: 898384c47dbfee5484b26d4faa5ee2352398c8b8fa2f26b500adcff9e1c1182d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffc1fbb67e9b31b5aeca7b0b11c2f5c6ef6c694e4e4af9bcb1ffbc4a27e20f2be1bbcb1416f4735176543d0ab929519c01e15c51a096fa0400195e796bee97af
|
7
|
+
data.tar.gz: f6c5a599c570f9183201101fb9197fbf063bffb845bdf343ddf00e91687c41d565f482d2290c46002f0ddb7c2ff24feb6b74a1df6a77e73a8731ae3141665199
|
data/bin/homebrew_automation.rb
CHANGED
@@ -1,99 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require_relative '../lib/homebrew_automation.rb'
|
6
|
-
|
7
|
-
class FormulaCommands < Thor
|
8
|
-
|
9
|
-
desc 'put-sdist', 'Update the URL and sha256 checksum of the source tarball'
|
10
|
-
option :url, :required => true
|
11
|
-
option :sha256, :required => true
|
12
|
-
def put_sdist
|
13
|
-
before = HomebrewAutomation::Formula.parse_string($stdin.read)
|
14
|
-
after = before.put_sdist options[:url], options[:sha256]
|
15
|
-
$stdout.write after
|
16
|
-
end
|
17
|
-
|
18
|
-
desc 'put-bottle', 'Insert or update a bottle reference for a given OS'
|
19
|
-
option :os, :required => true
|
20
|
-
option :sha256, :required => true
|
21
|
-
def put_bottle
|
22
|
-
before = HomebrewAutomation::Formula.parse_string($stdin.read)
|
23
|
-
after = before.put_bottle(options[:os], options[:sha256])
|
24
|
-
$stdout.write after
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
class WorkflowCommands < Thor
|
30
|
-
class_option :tap_user, :required => true
|
31
|
-
class_option :tap_repo, :required => true
|
32
|
-
class_option :tap_token, :required => true
|
33
|
-
class_option :bintray_user, :required => true
|
34
|
-
class_option :bintray_token, :required => true
|
35
|
-
|
36
|
-
desc 'build-and-upload', 'Build binary tarball from source tarball, then upload to Bintray'
|
37
|
-
long_desc <<-HERE_HERE
|
38
|
-
Since we're uploading to Bintray, we need a Bintray API KEY at `bintray_token`.
|
39
|
-
|
40
|
-
`formula_name` defaults to the same as `source_repo`.
|
41
|
-
`formula_version` defaults to `source_tag` with a leading `v` stripped off.
|
42
|
-
HERE_HERE
|
43
|
-
option :source_user, :required => true
|
44
|
-
option :source_repo, :required => true
|
45
|
-
option :source_tag, :required => true
|
46
|
-
option :formula_name
|
47
|
-
option :formula_version
|
48
|
-
def build_and_upload
|
49
|
-
workflow.build_and_upload_bottle(
|
50
|
-
HomebrewAutomation::SourceDist.new(
|
51
|
-
options[:source_user],
|
52
|
-
options[:source_repo],
|
53
|
-
options[:source_tag]),
|
54
|
-
formula_name: options[:formula_name],
|
55
|
-
version_name: options[:formula_version])
|
56
|
-
end
|
57
|
-
|
58
|
-
desc 'gather-and-publish', 'Make the Tap aware of new Bottles'
|
59
|
-
long_desc <<-HERE_HERE
|
60
|
-
See what bottles have been built and uploaded to Bintray, then publish them into the Tap.
|
61
|
-
|
62
|
-
Since we're publishing updates to the Formula in our Tap, we need Git push access to the
|
63
|
-
Tap repo on Github via a Github OAuth token via `tap_token`.
|
64
|
-
|
65
|
-
`formula-name` should be both the formula name as appears in the Tap and also the Bintray package name.
|
66
|
-
`formula-version` should be the Bintray "Version" name.
|
67
|
-
HERE_HERE
|
68
|
-
option :formula_name, :required => true
|
69
|
-
option :formula_version, :required => true
|
70
|
-
def gather_and_publish
|
71
|
-
workflow.gather_and_publish_bottles(
|
72
|
-
options[:formula_name],
|
73
|
-
options[:formula_version])
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def workflow
|
79
|
-
HomebrewAutomation::Workflow.new(
|
80
|
-
HomebrewAutomation::Tap.new(options[:tap_user], options[:tap_repo], options[:tap_token]),
|
81
|
-
HomebrewAutomation::Bintray.new(options[:bintray_user], options[:bintray_token]))
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
class MyCliApp < Thor
|
87
|
-
|
88
|
-
desc 'formula (...)', 'Modify Formula DSL source (read stdin, write stdout)'
|
89
|
-
subcommand "formula", FormulaCommands
|
90
|
-
|
91
|
-
desc 'bottle (...)', 'Workflows for dealing with binary artifacts'
|
92
|
-
subcommand "bottle", WorkflowCommands
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
|
97
|
-
MyCliApp.start(ARGV)
|
98
|
-
|
3
|
+
require_relative '../lib/homebrew_automation/cli.rb'
|
99
4
|
|
5
|
+
HomebrewAutomation::CLI::MyCliApp.start(ARGV)
|
data/lib/homebrew_automation.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
|
2
2
|
# Help you manipulate Homebrew Formula files, Bottles etc.
|
3
|
-
module HomebrewAutomation
|
4
|
-
end
|
5
3
|
|
6
4
|
require_relative 'homebrew_automation/bintray.rb'
|
7
5
|
require_relative 'homebrew_automation/bottle.rb'
|
8
|
-
require_relative 'homebrew_automation/bottle_gatherer.rb'
|
9
6
|
require_relative 'homebrew_automation/formula.rb'
|
10
|
-
require_relative 'homebrew_automation/
|
7
|
+
require_relative 'homebrew_automation/mac_os.rb'
|
11
8
|
require_relative 'homebrew_automation/source_dist.rb'
|
12
9
|
require_relative 'homebrew_automation/tap.rb'
|
13
10
|
require_relative 'homebrew_automation/version.rb'
|
@@ -1,122 +1,3 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
require 'uri'
|
5
|
-
require 'rest-client'
|
6
|
-
|
7
|
-
module HomebrewAutomation
|
8
|
-
|
9
|
-
# A bare-bones Bintray API client that implements only the methods needed for
|
10
|
-
# Homebrew things.
|
11
|
-
class Bintray
|
12
|
-
|
13
|
-
# @param username [String] Bintray username; for me this was not my email address
|
14
|
-
# @param api_key [String] Bearer-token-like key; generated in the Bintray web UI
|
15
|
-
# @param http [RestClient.Class] The +RestClient+ class itself
|
16
|
-
# @param base_url [String] Include the +https://+; exclude the trailing slash.
|
17
|
-
def initialize(
|
18
|
-
username,
|
19
|
-
api_key,
|
20
|
-
http: RestClient,
|
21
|
-
base_url: "https://bintray.com/api/v1"
|
22
|
-
)
|
23
|
-
@username = username
|
24
|
-
@api_key = api_key
|
25
|
-
@base_url = base_url
|
26
|
-
@http = http # allow injecting mocks for testing
|
27
|
-
end
|
28
|
-
|
29
|
-
# <tt>POST /packages/:subject/:repo/:package/versions</tt>
|
30
|
-
#
|
31
|
-
# Redundant: Bintray seems to create nonexistant versions for you if you
|
32
|
-
# just try to upload files into it.
|
33
|
-
#
|
34
|
-
# @param repo_name [String]
|
35
|
-
# @param package_name [String]
|
36
|
-
# @param version_name [String]
|
37
|
-
# @return [RestClient::Response]
|
38
|
-
def create_version(repo_name, package_name, version_name)
|
39
|
-
safe_repo = URI.escape(repo_name)
|
40
|
-
safe_pkg = URI.escape(package_name)
|
41
|
-
@http.post(
|
42
|
-
rel("/packages/#{safe_username}/#{safe_repo}/#{safe_pkg}/versions"),
|
43
|
-
{ name: version_name }.to_json,
|
44
|
-
api_headers
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
|
-
# <tt>PUT /content/:subject/:repo/:package/:version/:file_path[?publish=0/1][?override=0/1][?explode=0/1]</tt>
|
49
|
-
#
|
50
|
-
# Bintray seems to expect the byte sequence of the file to be written straight out into the
|
51
|
-
# HTTP request body, optionally via <tt>Transfer-Encoding: chunked</tt>. So we pass the +content+ String
|
52
|
-
# straight through to RestClient
|
53
|
-
#
|
54
|
-
# @param repo_name [String]
|
55
|
-
# @param package_name [String]
|
56
|
-
# @param version_name [String]
|
57
|
-
# @param filename [String] The filename within one Bintray repository, e.g. +hack-assembler-0.1.1.17.high_sierra.bottle.tar.gz+
|
58
|
-
# @param content [String] The bytes for the file, e.g. from a +File.read+
|
59
|
-
# @return [RestClient::Response]
|
60
|
-
def upload_file(repo_name, package_name, version_name, filename, content, publish: 1)
|
61
|
-
safe_repo = URI.escape(repo_name)
|
62
|
-
safe_pkg = URI.escape(package_name)
|
63
|
-
safe_ver = URI.escape(version_name)
|
64
|
-
safe_filename = URI.escape(filename)
|
65
|
-
safe_publish = URI.escape(publish.to_s)
|
66
|
-
@http.put(
|
67
|
-
rel("/content/#{safe_username}/#{safe_repo}/#{safe_pkg}/#{safe_ver}/#{safe_filename}?publish=#{safe_publish}"),
|
68
|
-
content,
|
69
|
-
auth_headers
|
70
|
-
)
|
71
|
-
end
|
72
|
-
|
73
|
-
# <tt>GET /packages/:subject/:repo/:package/versions/:version/files[?include_unpublished=0/1]</tt>
|
74
|
-
#
|
75
|
-
# Useful when seeing what bottles have already been built.
|
76
|
-
#
|
77
|
-
# @param repo_name [String]
|
78
|
-
# @param package_name [String]
|
79
|
-
# @param version_name [String]
|
80
|
-
# @return [RestClient::Response]
|
81
|
-
def get_all_files_in_version(repo_name, package_name, version_name)
|
82
|
-
safe_repo = URI.escape(repo_name)
|
83
|
-
safe_pkg = URI.escape(package_name)
|
84
|
-
safe_ver = URI.escape(version_name)
|
85
|
-
@http.get(
|
86
|
-
rel("/packages/#{safe_username}/#{safe_repo}/#{safe_pkg}/versions/#{safe_ver}/files"),
|
87
|
-
auth_headers)
|
88
|
-
end
|
89
|
-
|
90
|
-
# Bintray username, URI-escaped.
|
91
|
-
#
|
92
|
-
# @return [String]
|
93
|
-
def safe_username
|
94
|
-
URI.escape(@username)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Resolve a relative path into a URL using the current base_url
|
98
|
-
#
|
99
|
-
# @param slash_subpath [String]
|
100
|
-
# @return [String]
|
101
|
-
def rel(slash_subpath)
|
102
|
-
@base_url + slash_subpath
|
103
|
-
end
|
104
|
-
|
105
|
-
# @return [Hash]
|
106
|
-
def api_headers
|
107
|
-
{ "Content-Type" => "application/json" }.update auth_headers
|
108
|
-
end
|
109
|
-
|
110
|
-
# Implement HTTP Basich Auth, as per RFC 7617.
|
111
|
-
#
|
112
|
-
# Let's not bring in a library just for these two lines.
|
113
|
-
#
|
114
|
-
# @return [Hash]
|
115
|
-
def auth_headers
|
116
|
-
cred = Base64.strict_encode64("#{@username}:#{@api_key}")
|
117
|
-
{ Authorization: "Basic #{cred}" }
|
118
|
-
end
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
2
|
+
require_relative 'bintray/client.rb'
|
3
|
+
require_relative 'bintray/version.rb'
|
@@ -0,0 +1,125 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
require 'base64'
|
4
|
+
require 'uri'
|
5
|
+
require 'rest-client'
|
6
|
+
|
7
|
+
module HomebrewAutomation
|
8
|
+
end
|
9
|
+
|
10
|
+
module HomebrewAutomation::Bintray
|
11
|
+
|
12
|
+
# A bare-bones Bintray API client that implements only the methods needed for
|
13
|
+
# Homebrew things.
|
14
|
+
class Client
|
15
|
+
|
16
|
+
# @param username [String] Bintray username; for me this was not my email address
|
17
|
+
# @param api_key [String] Bearer-token-like key; generated in the Bintray web UI
|
18
|
+
# @param http [RestClient.Class] The +RestClient+ class itself
|
19
|
+
# @param base_url [String] Include the +https://+; exclude the trailing slash.
|
20
|
+
def initialize(
|
21
|
+
username,
|
22
|
+
api_key,
|
23
|
+
http: RestClient,
|
24
|
+
base_url: "https://bintray.com/api/v1"
|
25
|
+
)
|
26
|
+
@username = username
|
27
|
+
@api_key = api_key
|
28
|
+
@base_url = base_url
|
29
|
+
@http = http # allow injecting mocks for testing
|
30
|
+
end
|
31
|
+
|
32
|
+
# <tt>POST /packages/:subject/:repo/:package/versions</tt>
|
33
|
+
#
|
34
|
+
# Redundant: Bintray seems to create nonexistant versions for you if you
|
35
|
+
# just try to upload files into it.
|
36
|
+
#
|
37
|
+
# @param repo_name [String]
|
38
|
+
# @param package_name [String]
|
39
|
+
# @param version_name [String]
|
40
|
+
# @return [RestClient::Response]
|
41
|
+
def create_version(repo_name, package_name, version_name)
|
42
|
+
safe_repo = URI.escape(repo_name)
|
43
|
+
safe_pkg = URI.escape(package_name)
|
44
|
+
@http.post(
|
45
|
+
rel("/packages/#{safe_username}/#{safe_repo}/#{safe_pkg}/versions"),
|
46
|
+
{ name: version_name }.to_json,
|
47
|
+
api_headers
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# <tt>PUT /content/:subject/:repo/:package/:version/:file_path[?publish=0/1][?override=0/1][?explode=0/1]</tt>
|
52
|
+
#
|
53
|
+
# Bintray seems to expect the byte sequence of the file to be written straight out into the
|
54
|
+
# HTTP request body, optionally via <tt>Transfer-Encoding: chunked</tt>. So we pass the +content+ String
|
55
|
+
# straight through to RestClient
|
56
|
+
#
|
57
|
+
# @param repo_name [String]
|
58
|
+
# @param package_name [String]
|
59
|
+
# @param version_name [String]
|
60
|
+
# @param filename [String] The filename within one Bintray repository, e.g. +hack-assembler-0.1.1.17.high_sierra.bottle.tar.gz+
|
61
|
+
# @param content [String] The bytes for the file, e.g. from a +File.read+
|
62
|
+
# @return [RestClient::Response]
|
63
|
+
def upload_file(repo_name, package_name, version_name, filename, content, publish: 1)
|
64
|
+
safe_repo = URI.escape(repo_name)
|
65
|
+
safe_pkg = URI.escape(package_name)
|
66
|
+
safe_ver = URI.escape(version_name)
|
67
|
+
safe_filename = URI.escape(filename)
|
68
|
+
safe_publish = URI.escape(publish.to_s)
|
69
|
+
@http.put(
|
70
|
+
rel("/content/#{safe_username}/#{safe_repo}/#{safe_pkg}/#{safe_ver}/#{safe_filename}?publish=#{safe_publish}"),
|
71
|
+
content,
|
72
|
+
auth_headers
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
# <tt>GET /packages/:subject/:repo/:package/versions/:version/files[?include_unpublished=0/1]</tt>
|
77
|
+
#
|
78
|
+
# Useful when seeing what bottles have already been built.
|
79
|
+
#
|
80
|
+
# @param repo_name [String]
|
81
|
+
# @param package_name [String]
|
82
|
+
# @param version_name [String]
|
83
|
+
# @return [RestClient::Response]
|
84
|
+
def get_all_files_in_version(repo_name, package_name, version_name)
|
85
|
+
safe_repo = URI.escape(repo_name)
|
86
|
+
safe_pkg = URI.escape(package_name)
|
87
|
+
safe_ver = URI.escape(version_name)
|
88
|
+
@http.get(
|
89
|
+
rel("/packages/#{safe_username}/#{safe_repo}/#{safe_pkg}/versions/#{safe_ver}/files"),
|
90
|
+
auth_headers)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Bintray username, URI-escaped.
|
94
|
+
#
|
95
|
+
# @return [String]
|
96
|
+
def safe_username
|
97
|
+
URI.escape(@username)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Resolve a relative path into a URL using the current base_url
|
101
|
+
#
|
102
|
+
# @param slash_subpath [String]
|
103
|
+
# @return [String]
|
104
|
+
def rel(slash_subpath)
|
105
|
+
@base_url + slash_subpath
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return [Hash]
|
109
|
+
def api_headers
|
110
|
+
{ "Content-Type" => "application/json" }.update auth_headers
|
111
|
+
end
|
112
|
+
|
113
|
+
# Implement HTTP Basich Auth, as per RFC 7617.
|
114
|
+
#
|
115
|
+
# Let's not bring in a library just for these two lines.
|
116
|
+
#
|
117
|
+
# @return [Hash]
|
118
|
+
def auth_headers
|
119
|
+
cred = Base64.strict_encode64("#{@username}:#{@api_key}")
|
120
|
+
{ Authorization: "Basic #{cred}" }
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module HomebrewAutomation
|
5
|
+
end
|
6
|
+
|
7
|
+
module HomebrewAutomation::Bintray
|
8
|
+
|
9
|
+
# A representation of a Bintray +Version+
|
10
|
+
#
|
11
|
+
# As per Bintray, a +Version+ is part of a +Package+ is part of a +Repository+.
|
12
|
+
class Version
|
13
|
+
|
14
|
+
# @param client [Client] Connection to Bintray servers
|
15
|
+
# @param repo_name [String]
|
16
|
+
# @param package_name [String]
|
17
|
+
# @param version_name [String]
|
18
|
+
def initialize(client, repo_name, package_name, version_name)
|
19
|
+
@client = client
|
20
|
+
@repo_name = repo_name
|
21
|
+
@package_name = package_name
|
22
|
+
@version_name = version_name
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :repo_name, :package_name, :version_name
|
26
|
+
|
27
|
+
# Create this +Version+
|
28
|
+
#
|
29
|
+
# This assumes the +Package+ and +Repository+ already exists. If they do
|
30
|
+
# not, consider creating them manually via the Bintray web UI.
|
31
|
+
def create!
|
32
|
+
@client.create_version(@repo_name, @package_name, @version_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Upload a file to be part of this +Version+
|
36
|
+
#
|
37
|
+
# This is probably your Homebrew Bottle binary tarball.
|
38
|
+
#
|
39
|
+
# @param filename [String]
|
40
|
+
# @param content [String] the bytes in the file
|
41
|
+
def upload_file!(filename, content)
|
42
|
+
@client.upload_file(
|
43
|
+
@repo_name,
|
44
|
+
@package_name,
|
45
|
+
@version_name,
|
46
|
+
filename,
|
47
|
+
content)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Download metadata about files that exist on Bintray for this +Version+
|
51
|
+
#
|
52
|
+
# @return [Hash] mapping from OS (as appears in part of the filenames) to sha256 checksum
|
53
|
+
def gather_bottles
|
54
|
+
resp = @client.get_all_files_in_version(@repo_name, @package_name, @version_name)
|
55
|
+
_assert_match((200..207), resp.code)
|
56
|
+
json = JSON.parse(resp.body)
|
57
|
+
_assert_match(Array, json)
|
58
|
+
pairs = json.map do |f|
|
59
|
+
os = _parse_for_os(f['name'])
|
60
|
+
checksum = f['sha256']
|
61
|
+
[os, checksum]
|
62
|
+
end
|
63
|
+
Hash[pairs]
|
64
|
+
end
|
65
|
+
|
66
|
+
def _assert_match(cond, x)
|
67
|
+
unless cond === x
|
68
|
+
p x
|
69
|
+
raise StandardError.new(x)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param bottle_filename [String] filename
|
74
|
+
# @return [String] OS name
|
75
|
+
def _parse_for_os(bottle_filename)
|
76
|
+
File.extname(
|
77
|
+
File.basename(bottle_filename, '.bottle.tar.gz')).
|
78
|
+
sub(/^\./, '')
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
require_relative 'formula_commands.rb'
|
5
|
+
require_relative 'workflow_commands.rb'
|
6
|
+
|
7
|
+
module HomebrewAutomation
|
8
|
+
module CLI
|
9
|
+
|
10
|
+
class MyCliApp < Thor
|
11
|
+
|
12
|
+
desc 'formula (...)', 'Modify Formula DSL source (read stdin, write stdout)'
|
13
|
+
subcommand "formula", FormulaCommands
|
14
|
+
|
15
|
+
desc 'bottle (...)', 'Workflows for dealing with binary artifacts'
|
16
|
+
subcommand "bottle", WorkflowCommands
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
require_relative '../../homebrew_automation/formula.rb'
|
5
|
+
|
6
|
+
module HomebrewAutomation
|
7
|
+
module CLI
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class HomebrewAutomation::CLI::FormulaCommands < Thor
|
12
|
+
|
13
|
+
desc 'put-sdist', 'Update the URL and sha256 checksum of the source tarball'
|
14
|
+
option :url, :required => true
|
15
|
+
option :sha256, :required => true
|
16
|
+
def put_sdist
|
17
|
+
before = HomebrewAutomation::Formula.parse_string($stdin.read)
|
18
|
+
after = before.put_sdist options[:url], options[:sha256]
|
19
|
+
$stdout.write after
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'put-bottle', 'Insert or update a bottle reference for a given OS'
|
23
|
+
option :os, :required => true
|
24
|
+
option :sha256, :required => true
|
25
|
+
def put_bottle
|
26
|
+
before = HomebrewAutomation::Formula.parse_string($stdin.read)
|
27
|
+
after = before.put_bottle(options[:os], options[:sha256])
|
28
|
+
$stdout.write after
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
require_relative '../../homebrew_automation.rb'
|
5
|
+
|
6
|
+
module HomebrewAutomation
|
7
|
+
module CLI
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class HomebrewAutomation::CLI::WorkflowCommands < Thor
|
12
|
+
class_option :source_user, :required => true
|
13
|
+
class_option :source_repo, :required => true
|
14
|
+
class_option :source_tag, :required => true
|
15
|
+
class_option :tap_user, :required => true
|
16
|
+
class_option :tap_repo, :required => true
|
17
|
+
class_option :tap_token, :required => true
|
18
|
+
class_option :formula_name
|
19
|
+
class_option :bintray_user, :required => true
|
20
|
+
class_option :bintray_token, :required => true
|
21
|
+
class_option :bintray_repo
|
22
|
+
class_option :bintray_package
|
23
|
+
class_option :bintray_version
|
24
|
+
|
25
|
+
desc 'build-and-upload', 'Build binary tarball from source tarball, then upload to Bintray'
|
26
|
+
long_desc <<-HERE_HERE
|
27
|
+
Since we're uploading to Bintray, we need a Bintray API KEY at `bintray_token`.
|
28
|
+
HERE_HERE
|
29
|
+
def build_and_upload
|
30
|
+
workflow.build_and_upload_bottle(sdist, tap, formula_name, bintray_version)
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'gather-and-publish', 'Make the Tap aware of new Bottles'
|
34
|
+
long_desc <<-HERE_HERE
|
35
|
+
See what bottles have been built and uploaded to Bintray, then publish them into the Tap.
|
36
|
+
|
37
|
+
Since we're publishing updates to the Formula in our Tap, we need Git push access to the
|
38
|
+
Tap repo on Github via a Github OAuth token via `tap_token`.
|
39
|
+
HERE_HERE
|
40
|
+
def gather_and_publish
|
41
|
+
workflow.gather_and_publish_bottles(sdist, tap, formula_name, bintray_version)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def sdist
|
47
|
+
HomebrewAutomation::SourceDist.new(
|
48
|
+
options[:source_user],
|
49
|
+
options[:source_repo],
|
50
|
+
options[:source_tag])
|
51
|
+
end
|
52
|
+
|
53
|
+
def tap
|
54
|
+
HomebrewAutomation::Tap.new(
|
55
|
+
options[:tap_user],
|
56
|
+
options[:tap_repo],
|
57
|
+
options[:tap_token])
|
58
|
+
end
|
59
|
+
|
60
|
+
# DOC: default values here
|
61
|
+
def formula_name
|
62
|
+
options[:formula_name] || sdist.repo
|
63
|
+
end
|
64
|
+
|
65
|
+
def bintray_client
|
66
|
+
HomebrewAutomation::Bintray::Client.new(
|
67
|
+
options[:bintray_user],
|
68
|
+
options[:bintray_token])
|
69
|
+
end
|
70
|
+
|
71
|
+
# DOC: default values here
|
72
|
+
def bintray_version
|
73
|
+
HomebrewAutomation::Bintray::Version.new(
|
74
|
+
bintray_client,
|
75
|
+
options[:bintray_repo] || "homebrew-bottles",
|
76
|
+
options[:bintray_package] || sdist.repo,
|
77
|
+
options[:bintray_version] || sdist.tag.sub(/^v/, ''))
|
78
|
+
end
|
79
|
+
|
80
|
+
def workflow
|
81
|
+
HomebrewAutomation::Workflow.new
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -82,6 +82,20 @@ module HomebrewAutomation
|
|
82
82
|
put_bottle_version(os, sha256))
|
83
83
|
end
|
84
84
|
|
85
|
+
# Remove sha256 references to all bottles
|
86
|
+
#
|
87
|
+
# This may leave the Formula in a non-standard state. Please add at lease
|
88
|
+
# one bottle referene back, otherwise why are you even declaring a bottles block
|
89
|
+
# at all.
|
90
|
+
#
|
91
|
+
# @return [Formula] a new instance of Formula with the changes applied
|
92
|
+
def rm_all_bottles
|
93
|
+
Formula.new update(
|
94
|
+
@ast,
|
95
|
+
bot_begin_path,
|
96
|
+
rm_all_bottle_sha256s)
|
97
|
+
end
|
98
|
+
|
85
99
|
# Both formulae are +==+, as per +Parser::AST::Node#==+.
|
86
100
|
#
|
87
101
|
# In practice, I think this means both formulae are equivalent in terms of
|
@@ -137,6 +151,18 @@ module HomebrewAutomation
|
|
137
151
|
}
|
138
152
|
end
|
139
153
|
|
154
|
+
# Drop all bottles
|
155
|
+
# rm_all_bottle_sha256s :: Proc (Node -> Node)
|
156
|
+
def rm_all_bottle_sha256s
|
157
|
+
-> (bot_begin) {
|
158
|
+
bot_begin.updated(
|
159
|
+
nil, # keep the node type unchanged
|
160
|
+
bot_begin.children.reject(
|
161
|
+
# Get ride of all bottles condtionlessly
|
162
|
+
&by_msg('sha256')))
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
140
166
|
# Build a new AST Node
|
141
167
|
# String -> String -> Node
|
142
168
|
def new_sha256 sha256, os
|
@@ -11,7 +11,12 @@ module HomebrewAutomation
|
|
11
11
|
#
|
12
12
|
# @return [String]
|
13
13
|
def self.identify_version
|
14
|
-
version =
|
14
|
+
version =
|
15
|
+
begin
|
16
|
+
`sw_vers -productVersion`
|
17
|
+
rescue Errno::ENOENT # if we're not on a Mac
|
18
|
+
nil
|
19
|
+
end
|
15
20
|
mac_to_homebrew.
|
16
21
|
select { |pattern, _| pattern === version }.
|
17
22
|
map { |_, description| description }.
|
@@ -7,10 +7,11 @@ module HomebrewAutomation
|
|
7
7
|
class SourceDist
|
8
8
|
|
9
9
|
# Assign args to attributes {#user}, {#repo}, {#tag}
|
10
|
-
def initialize user, repo, tag
|
10
|
+
def initialize user, repo, tag, http: RestClient
|
11
11
|
@user = user
|
12
12
|
@repo = repo
|
13
13
|
@tag = tag
|
14
|
+
@http = http
|
14
15
|
end
|
15
16
|
|
16
17
|
# Github username, as appears in Github URLs
|
@@ -41,12 +42,11 @@ module HomebrewAutomation
|
|
41
42
|
#
|
42
43
|
# Lazy and memoized.
|
43
44
|
#
|
44
|
-
# @param fake [String] inject fake file contents (for testing)
|
45
45
|
# @return [String] contents of the file
|
46
|
-
def contents
|
47
|
-
@contents = @contents ||
|
46
|
+
def contents
|
47
|
+
@contents = @contents ||
|
48
48
|
begin
|
49
|
-
resp =
|
49
|
+
resp = @http.get url
|
50
50
|
case resp.code
|
51
51
|
when 200
|
52
52
|
resp.body.to_s
|
@@ -18,15 +18,15 @@ module HomebrewAutomation
|
|
18
18
|
@keep_submodule = keep_submodule
|
19
19
|
end
|
20
20
|
|
21
|
-
# Github
|
21
|
+
# Github repo name, as appears in Github URLs
|
22
22
|
#
|
23
23
|
# @return [String]
|
24
|
-
attr_reader :
|
24
|
+
attr_reader :repo
|
25
25
|
|
26
|
-
#
|
26
|
+
# Repo URL, as expected by Git
|
27
27
|
#
|
28
28
|
# @return [String]
|
29
|
-
attr_reader :
|
29
|
+
attr_reader :url
|
30
30
|
|
31
31
|
# Github OAuth token
|
32
32
|
#
|
@@ -63,9 +63,9 @@ module HomebrewAutomation
|
|
63
63
|
# @return [Formula] as returned from the block,
|
64
64
|
# assuming it obediantly returns a {Formula}.
|
65
65
|
def on_formula(formula, &block)
|
66
|
-
name = "#{formula}.rb"
|
66
|
+
name = "#{formula}.rb" # DOC
|
67
67
|
block ||= ->(n) { n }
|
68
|
-
Dir.chdir 'Formula' do
|
68
|
+
Dir.chdir 'Formula' do # DOC
|
69
69
|
File.open name, 'r' do |old_file|
|
70
70
|
File.open "#{name}.new", 'w' do |new_file|
|
71
71
|
new_file.write(
|
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
|
-
require_relative './
|
3
|
-
require_relative './
|
2
|
+
require_relative './mac_os.rb'
|
3
|
+
require_relative './bintray.rb'
|
4
|
+
require_relative './source_dist.rb'
|
4
5
|
|
5
6
|
module HomebrewAutomation
|
6
7
|
|
@@ -9,71 +10,31 @@ module HomebrewAutomation
|
|
9
10
|
# Each method in this class probably makes sense to be exposed as a CLI command.
|
10
11
|
class Workflow
|
11
12
|
|
12
|
-
# Assign params to attributes.
|
13
|
-
#
|
14
|
-
# See {#tap} and {#bintray}.
|
15
|
-
#
|
16
|
-
# @param tap [Tap]
|
17
|
-
# @param bintray [Bintray]
|
18
|
-
# @param bintray_bottle_repo [String] Really should be somehow a part of the +bintray+ param
|
19
|
-
def initialize(
|
20
|
-
tap,
|
21
|
-
bintray,
|
22
|
-
bintray_bottle_repo: 'homebrew-bottles')
|
23
|
-
@tap = tap
|
24
|
-
@bintray = bintray
|
25
|
-
@bintray_bottle_repo = bintray_bottle_repo
|
26
|
-
end
|
27
|
-
|
28
|
-
# The Tap holding the Formulae for which we might want to build or publish bottles.
|
29
|
-
#
|
30
|
-
# @return [Tap]
|
31
|
-
attr_reader :tap
|
32
|
-
|
33
|
-
# An API client
|
34
|
-
#
|
35
|
-
# @return [Bintray]
|
36
|
-
attr_reader :bintray
|
37
|
-
|
38
13
|
# Build and upload a bottle.
|
39
14
|
#
|
40
|
-
# The
|
41
|
-
# is built goes to {#bintray}.
|
15
|
+
# The built Bottle tarball gets uploaded to Bintray.
|
42
16
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# The optional params overwrite the above implication.
|
48
|
-
#
|
49
|
-
# @param source_dist [HomebrewAutomation::SourceDist] Source tarball
|
50
|
-
# @param formula_name [String] Formula name as appears in the Tap, which should be the same as the Bintray "Package" name
|
51
|
-
# @param version_name [String] Bintray package "Version" name; defaults to stripping leading `v` from the Git tag.
|
17
|
+
# @param sdist [SourceDist]
|
18
|
+
# @param tap [Tap]
|
19
|
+
# @param formula_name [String] the name of the formula in the Tap
|
20
|
+
# @param bversion [Bintray::Version]
|
52
21
|
# @return [Bottle]
|
53
|
-
def build_and_upload_bottle(
|
54
|
-
formula_name ||= source_dist.repo
|
55
|
-
version_name ||= source_dist.tag.sub(/^v/, '')
|
22
|
+
def build_and_upload_bottle(sdist, tap, formula_name, bversion)
|
56
23
|
os_name = MacOS.identify_version
|
57
|
-
|
58
|
-
|
59
|
-
formula.put_sdist(
|
24
|
+
tap.with_git_clone do
|
25
|
+
tap.on_formula(formula_name) do |formula|
|
26
|
+
formula.put_sdist(sdist.url, sdist.sha256)
|
60
27
|
end
|
61
|
-
|
28
|
+
tap.git_commit_am "Throwaway commit; just for building bottles"
|
62
29
|
|
63
30
|
local_tap_url = File.realpath('.')
|
64
31
|
bottle = Bottle.new(local_tap_url, formula_name, os_name)
|
65
32
|
bottle.build
|
66
33
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
@bintray.upload_file(
|
72
|
-
@bintray_bottle_repo,
|
73
|
-
formula_name,
|
74
|
-
version_name,
|
75
|
-
bottle.filename,
|
76
|
-
bottle.content)
|
34
|
+
# Bintray auto-creates Versions on file-upload.
|
35
|
+
# Re-creating an existing Version results in a 409.
|
36
|
+
#bversion.create!
|
37
|
+
bversion.upload_file!(bottle.filename, bottle.content)
|
77
38
|
|
78
39
|
bottle
|
79
40
|
end
|
@@ -86,34 +47,29 @@ module HomebrewAutomation
|
|
86
47
|
# the {#tap} repository to make an existing Formula aware of the Bottles
|
87
48
|
# we're gathered (as such "publishing" the bottles).
|
88
49
|
#
|
89
|
-
# @param
|
90
|
-
# @param
|
50
|
+
# @param sdist [SourceDist]
|
51
|
+
# @param tap [Tap]
|
52
|
+
# @param formula_name [String] the name of the formula in the Tap
|
53
|
+
# @param bversion [Bintray::Version]
|
91
54
|
# @return [Formula]
|
92
|
-
def gather_and_publish_bottles(formula_name,
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
55
|
+
def gather_and_publish_bottles(sdist, tap, formula_name, bversion)
|
56
|
+
tap.with_git_clone do
|
57
|
+
tap.on_formula(formula_name) do |formula|
|
58
|
+
bottles = bversion.gather_bottles
|
59
|
+
bottles.reduce(
|
60
|
+
formula.
|
61
|
+
put_sdist(sdist.url, sdist.sha256).
|
62
|
+
rm_all_bottles
|
63
|
+
) do |f, (os, checksum)|
|
64
|
+
f.put_bottle(os, checksum)
|
65
|
+
end
|
101
66
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@tap.on_formula(formula_name) do |formula|
|
107
|
-
gatherer.put_bottles_into(formula)
|
108
|
-
end
|
109
|
-
|
110
|
-
@tap.git_config
|
111
|
-
@tap.git_commit_am "Add bottles for #{formula_name}@#{version_name}"
|
112
|
-
@tap.git_push
|
67
|
+
tap.git_config
|
68
|
+
tap.git_commit_am "Add bottles for #{formula_name}@#{bversion.version_name}"
|
69
|
+
tap.git_push
|
113
70
|
end
|
114
71
|
end
|
115
72
|
|
116
|
-
|
117
73
|
end
|
118
74
|
|
119
75
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: homebrew_automation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- easoncxz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ripper-tags
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: thor
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,10 +146,15 @@ files:
|
|
132
146
|
- bin/homebrew_automation.rb
|
133
147
|
- lib/homebrew_automation.rb
|
134
148
|
- lib/homebrew_automation/bintray.rb
|
149
|
+
- lib/homebrew_automation/bintray/client.rb
|
150
|
+
- lib/homebrew_automation/bintray/version.rb
|
135
151
|
- lib/homebrew_automation/bottle.rb
|
136
|
-
- lib/homebrew_automation/
|
152
|
+
- lib/homebrew_automation/cli.rb
|
153
|
+
- lib/homebrew_automation/cli/cli.rb
|
154
|
+
- lib/homebrew_automation/cli/formula_commands.rb
|
155
|
+
- lib/homebrew_automation/cli/workflow_commands.rb
|
137
156
|
- lib/homebrew_automation/formula.rb
|
138
|
-
- lib/homebrew_automation/
|
157
|
+
- lib/homebrew_automation/mac_os.rb
|
139
158
|
- lib/homebrew_automation/source_dist.rb
|
140
159
|
- lib/homebrew_automation/tap.rb
|
141
160
|
- lib/homebrew_automation/version.rb
|
@@ -160,7 +179,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
179
|
- !ruby/object:Gem::Version
|
161
180
|
version: '0'
|
162
181
|
requirements: []
|
163
|
-
|
182
|
+
rubyforge_project:
|
183
|
+
rubygems_version: 2.7.7
|
164
184
|
signing_key:
|
165
185
|
specification_version: 4
|
166
186
|
summary: Build bottles and update Formulae
|
@@ -1,49 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative './mac-os.rb'
|
3
|
-
require_relative './formula.rb'
|
4
|
-
|
5
|
-
module HomebrewAutomation
|
6
|
-
|
7
|
-
# See what Bottles have already been built and uploaded to Bintray
|
8
|
-
class BottleGatherer
|
9
|
-
|
10
|
-
# @param json [Array<Hash>] JSON from a +RestClient::Response+ containing the list of files from Bintray
|
11
|
-
def initialize(json)
|
12
|
-
@json = json
|
13
|
-
@bottles = nil
|
14
|
-
end
|
15
|
-
|
16
|
-
# The bottles gathered.
|
17
|
-
#
|
18
|
-
# @return [Hash<String, String>] with keys being OS names (in Homebrew-form) and values being SHA256 checksums
|
19
|
-
def bottles
|
20
|
-
return @bottles if @bottles
|
21
|
-
pairs = @json.map do |f|
|
22
|
-
os = _parse_for_os(f['name'])
|
23
|
-
checksum = f['sha256']
|
24
|
-
[os, checksum]
|
25
|
-
end
|
26
|
-
@bottles = Hash[pairs]
|
27
|
-
end
|
28
|
-
|
29
|
-
# Put all bottles gathered here into the given formula, then return the result
|
30
|
-
#
|
31
|
-
# @param formula [HomebrewAutomation::Formula]
|
32
|
-
# @return [HomebrewAutomation::Formula]
|
33
|
-
def put_bottles_into(formula)
|
34
|
-
bottles.reduce(formula) do |formula, (os, checksum)|
|
35
|
-
formula.put_bottle(os, checksum)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param bottle_filename [String] filename
|
40
|
-
# @return [String] OS name
|
41
|
-
def _parse_for_os(bottle_filename)
|
42
|
-
File.extname(
|
43
|
-
File.basename(bottle_filename, '.bottle.tar.gz')).
|
44
|
-
sub(/^\./, '')
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|