Skip to content

Commit 227b821

Browse files
Exercise publisher draft preservation and immediate retry.
1 parent 4c60603 commit 227b821

3 files changed

Lines changed: 62 additions & 5 deletions

File tree

‎.github/workflows/draft-release-test.yaml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ jobs:
1616
GH_TOKEN: ${{ github.token }}
1717
RELEASE_TARGET: a93ad87cf4f5235452935767bd7e2abde753b069
1818
run: python3 scripts/draft-release-probe.py
19+
- uses: ruby/setup-ruby@a0102e0972be65f351c307e2d64b9314a57c8073
20+
with:
21+
ruby-version: "3.4"
22+
bundler-cache: true
23+
- name: Test publisher preservation and retry
24+
env:
25+
GH_TOKEN: ${{ github.token }}
26+
RELEASE_TARGET: a93ad87cf4f5235452935767bd7e2abde753b069
27+
run: bundle exec ruby -Ilib scripts/publisher-draft-probe.rb

‎lib/bake/gem/github/publisher.rb‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,16 @@ def github_release(tag)
120120
pages = JSON.parse(readlines("gh", "api", "--paginate", "--slurp", "repos/#{@repository}/releases?per_page=100", chdir: @root).join)
121121
matches = pages.flatten(1).select{|release| release.fetch("tag_name") == tag}
122122
raise "Multiple GitHub releases have the same tag." if matches.size > 1
123-
matches.first
123+
if release = matches.first
124+
return api("releases/#{release.fetch('id')}")
125+
end
126+
# Resolve pending tags directly when the REST list has not caught up:
127+
owner, name = @repository.split("/", 2)
128+
query = "query($owner: String!, $name: String!, $tag: String!) { repository(owner: $owner, name: $name) { release(tagName: $tag) { databaseId } } }"
129+
result = JSON.parse(readlines("gh", "api", "graphql", "-f", "query=#{query}", "-f", "owner=#{owner}", "-f", "name=#{name}", "-f", "tag=#{tag}", chdir: @root).join)
130+
if release = result.fetch("data").fetch("repository").fetch("release")
131+
api("releases/#{release.fetch('databaseId')}")
132+
end
124133
end
125134

126135
def guard_release(release, commit)
@@ -137,12 +146,15 @@ def preserve_release(receipt)
137146
tag = "v#{receipt.fetch(:version)}"
138147
release = github_release(tag)
139148
unless release
140-
Tempfile.create("release-notes") do |file|
141-
file.write("#{receipt.fetch(:pull_request_url)}\n\nSource: #{receipt.fetch(:commit)}\nSHA256: #{receipt.fetch(:sha256)}\n\nSee releases.md at the release tag for release notes.\n")
149+
Tempfile.create("release") do |file|
150+
file.write(JSON.generate(
151+
tag_name: tag, draft: true, target_commitish: receipt.fetch(:commit), name: "#{receipt.fetch(:name)} #{tag}",
152+
body: "#{receipt.fetch(:pull_request_url)}\n\nSource: #{receipt.fetch(:commit)}\nSHA256: #{receipt.fetch(:sha256)}\n\nSee releases.md at the release tag for release notes.\n"
153+
))
142154
file.flush
143-
system("gh", "release", "create", tag, "--repo", @repository, "--draft", "--target", receipt.fetch(:commit), "--title", "#{receipt.fetch(:name)} #{tag}", "--notes-file", file.path, chdir: @root)
155+
# The release list can remain stale after a successful creation:
156+
release = JSON.parse(readlines("gh", "api", "repos/#{@repository}/releases", "--method", "POST", "--input", file.path, chdir: @root).join)
144157
end
145-
release = github_release(tag) or raise "Draft release was not retained."
146158
end
147159
guard_release(release, receipt.fetch(:commit))
148160
assets = release.fetch("assets")

‎scripts/publisher-draft-probe.rb‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
require "bake/gem/github/publisher"
2+
require "tmpdir"
3+
4+
Dir.mktmpdir do |root|
5+
FileUtils.mkdir_p(File.join(root, "config"))
6+
FileUtils.mkdir_p(File.join(root, "pkg"))
7+
File.write(File.join(root, "config", "release.yaml"), YAML.dump("schema" => 1, "repository" => ENV.fetch("GITHUB_REPOSITORY"), "branch" => "main", "signing" => false))
8+
publisher = Bake::Gem::GitHub::Publisher.new(root)
9+
receipt = {
10+
name: "draft-probe", version: "0.0.0-probe-#{ENV.fetch('GITHUB_RUN_ID')}",
11+
commit: ENV.fetch("RELEASE_TARGET"), file: "draft-probe.gem", sha256: Digest::SHA256.hexdigest("Probe bytes"),
12+
pull_request_url: "Temporary publisher API test; no gem is published."
13+
}
14+
tag = "v#{receipt.fetch(:version)}"
15+
File.write(File.join(root, "pkg", "draft-probe.gem"), "Probe bytes")
16+
File.write(File.join(root, "pkg", "draft-probe.gem.sigstore.json"), "{}")
17+
File.write(File.join(root, "pkg", "provenance.sigstore.json"), "{}")
18+
File.write(File.join(root, "pkg", "release.json"), JSON.generate(receipt))
19+
begin
20+
raise "Probe release already exists" if publisher.send(:github_release, tag)
21+
first = publisher.send(:preserve_release, receipt)
22+
second = publisher.send(:preserve_release, receipt)
23+
raise "Created another draft on retry" unless first.fetch("id") == second.fetch("id")
24+
raise "Wrong number of assets" unless second.fetch("assets").size == 4
25+
download = File.join(root, "restored")
26+
publisher.system("gh", "release", "download", tag, "--repo", ENV.fetch("GITHUB_REPOSITORY"), "--dir", download, chdir: root)
27+
Dir.children(File.join(root, "pkg")).each do |name|
28+
raise "Restored file differs" unless File.binread(File.join(download, name)) == File.binread(File.join(root, "pkg", name))
29+
end
30+
puts "Publisher preserved four files, reused the same draft on immediate retry, and restored identical bytes."
31+
ensure
32+
if release = publisher.send(:github_release, tag)
33+
publisher.system("gh", "api", "repos/#{ENV.fetch('GITHUB_REPOSITORY')}/releases/#{release.fetch('id')}", "--method", "DELETE", chdir: root)
34+
end
35+
end
36+
end

0 commit comments

Comments
 (0)