homebrew_automation 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|