-
Notifications
You must be signed in to change notification settings - Fork 11
/
update.rb
executable file
·227 lines (191 loc) · 6.05 KB
/
update.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/env nix-shell
#!nix-shell -p ruby curl -i ruby
require 'erb'
require 'fileutils'
require 'json'
require 'net/http'
require 'yaml'
def fetch_json(url, tries = 10)
if tries <= 0
raise "error fetching #{url}: too many retries/redirects"
end
puts "fetching #{url}"
uri = URI(url)
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") do |http|
http.request_get(uri.path + (uri.query.nil? ? "" : "?" + uri.query), {"Accept" => "application/json"}) do |response|
case response
when Net::HTTPSuccess
begin
return JSON.parse(response.read_body)
rescue JSON::JSONError
puts "received invalid JSON"
sleep 1
return fetch_json(url, tries - 1)
end
when Net::HTTPRedirection
puts "received HTTP redirect to #{response['location']}"
return fetch_json(response["location"], tries - 1)
else
puts "received HTTP error: #{response.code} #{response.message}"
sleep 1
return fetch_json(url, tries - 1)
end
end
end
end
def download(url, path)
system("curl", "-sfL", "-o", path, url)
end
def render_release(eval_id, release_name, server_url, repository, install_nix_action_version)
b = binding
ERB.new(File.read("RELEASE.md.erb")).result b
end
def rewrite(path, &block)
body = File.read(path)
new_body = yield body
File.write(path, new_body)
end
def get_eval(eval_id, skip_existing_tag = false)
release_name = nil
release_job = "build.x86_64-linux"
dist_jobs = [
"binaryTarball.aarch64-darwin",
"binaryTarball.aarch64-linux",
"binaryTarball.i686-linux",
"binaryTarball.x86_64-darwin",
"binaryTarball.x86_64-linux",
"buildStatic.aarch64-linux",
"buildStatic.x86_64-linux",
"dockerImage.aarch64-linux",
"dockerImage.x86_64-linux",
"installerScript",
]
extra_prefixes = ["build.", "buildStatic.", "tests.", "installTests.", "installerTests."]
exclude_jobs = ["tests.setuid.i686-linux"]
required_jobs = [release_job] + dist_jobs
downloads = []
# Fetch information from Hydra
res = fetch_json("https://hydra.nixos.org/eval/#{eval_id}")
res["builds"].each do |build_id|
data = fetch_json("https://hydra.nixos.org/build/#{build_id}")
job = data["job"]
if dist_jobs.none?(job) and extra_prefixes.none? { |prefix| job.start_with? prefix } or exclude_jobs.include?(job)
next
end
if data["buildstatus"].nil? or data["buildstatus"] > 0
puts "evaluation #{eval_id} has queued or failed job: #{job}"
return :failure
end
case job
when release_job
# Get the release name
release_name = data["nixname"]
puts "release name: #{release_name}"
when *dist_jobs
filename = data["buildproducts"]["1"]["name"]
downloads.push([
job,
build_id,
filename
])
end
required_jobs.delete(job)
end
if not required_jobs.empty?
puts "evaluation #{eval_id} is missing required jobs: #{required_jobs.join(", ")}"
return :failure
end
# Skip existing tags
tag_exists = system("git", "show-ref", "--tags", release_name, "--quiet")
if skip_existing_tag
if tag_exists.nil?
raise "git tag checking error"
end
if tag_exists
puts "skipping existing release"
return :skip
end
end
# Download files
downloads.each do |job, build_id, filename|
puts "downloading #{job}"
case job.split(".", 2).first
when "buildStatic"
dest = release_name + "-" + job.split(".", 2).last + "-static"
when "dockerImage"
dest = release_name + "-" + job.split(".", 2).last + "-container.tar.gz"
else
dest = filename
end
download("https://hydra.nixos.org/build/#{build_id}/download/1/#{filename}", "dist/#{dest}")
end
server_url = ENV.fetch('GITHUB_SERVER_URL', 'https://github.com')
repository = ENV.fetch('GITHUB_REPOSITORY', 'nix-community/nix-unstable-installer')
# Rewrite the installer
rewrite("dist/install") do |body|
body.gsub(
"url=https://releases.nixos.org/nix/",
"url=#{server_url}/#{repository}/releases/download/"
)
end
# Get cachix/install-nix-action version for the RELEASE file
begin
install_nix_action_version = YAML.load_file(".github/workflows/release.yml")["jobs"]["update"]["steps"].find { |step| step.has_key? "uses" and step["uses"].start_with? "cachix/install-nix-action@" }["uses"].split("@", 2).last
rescue Errno::ENOENT
install_nix_action_version = "master"
end
# Generate the RELEASE file
release_body = render_release(eval_id, release_name, server_url, repository, install_nix_action_version)
File.write("dist/RELEASE.md", release_body)
return release_name
end
def main(eval_id)
FileUtils.rm_rf "dist"
FileUtils.mkdir_p "dist"
if eval_id == nil
# Get latest evaluations from Hydra
latest_url = "https://hydra.nixos.org/jobset/nix/master/evals"
latest = fetch_json(latest_url)
# Iterate over evaluations
release_name = nil
eval_idx = 0
evals_checked = 0
while release_name.nil?
# Give up when too many evals have been tried
if evals_checked > 60
raise "error finding latest successful evaluation: too many evaluations checked"
end
# Implement Hydra pagination
if eval_idx >= latest["evals"].length
eval_idx = 0
latest = fetch_json(latest_url + latest["next"])
next
end
evals_checked += 1
eval_id = latest["evals"][eval_idx]["id"]
# Get evaluation details
eval_result = get_eval(eval_id, skip_existing_tag = true)
case eval_result
when :failure
eval_idx += 1
next
when :skip
release_name = ""
updated = false
else
release_name = eval_result
updated = true
end
end
# Output for CI automation
if ENV.fetch("GITHUB_ACTIONS", "false") == "true"
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |file|
file.puts "nix_release=#{release_name}"
file.puts "updated=#{updated}"
end
end
else
get_eval(eval_id)
end
end
main(ARGV[0])