Skip to content

Commit d09ce15

Browse files
committed
Ensure labels exist before creating PRs
`gh` does not create labels automatically when you create a PR referencing one. If the label does not exist, the PR creation fails. This commit adds an `ensure_labels` step before creating the PRs that lists and creates the labels that will be used in the following steps.
1 parent c27ee4e commit d09ce15

4 files changed

Lines changed: 77 additions & 0 deletions

File tree

lib/executor.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def handle_open(action)
111111
)
112112
end
113113

114+
@gh.ensure_labels(@labels)
114115
@git.checkout_fresh_branch(branch: spec.branch, base: @base)
115116
committed = pin_packages_and_commit(spec)
116117
if !committed

lib/gh_client.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ def create_pr(branch:, base:, title:, body:, labels: [])
6565
result.stdout.strip[%r{/pull/(\d+)}, 1]&.to_i
6666
end
6767

68+
# Ensures every label in +labels+ exists in the repo, creating any that
69+
# are missing. Called once before opening PRs so that `create_pr` never
70+
# fails with "label does not exist". Missing labels are created with a
71+
# neutral default color; existing labels are left untouched.
72+
def ensure_labels(labels)
73+
return if labels.empty?
74+
existing = list_label_names
75+
labels.each do |label|
76+
next if existing.include?(label)
77+
@runner.run(
78+
"gh", "label", "create", label,
79+
"--repo", @repo,
80+
"--color", "0075ca"
81+
)
82+
end
83+
end
84+
6885
# Edits an existing PR's title and body. Used after force-pushing a
6986
# changed branch — the body must be re-rendered so the metadata
7087
# block reflects the new package set.
@@ -98,6 +115,19 @@ def close_pr(number:, comment: nil)
98115

99116
private
100117

118+
def list_label_names
119+
result = @runner.run(
120+
"gh", "label", "list",
121+
"--repo", @repo,
122+
"--json", "name",
123+
"--limit", "200"
124+
)
125+
return [] unless result.success?
126+
JSON.parse(result.stdout.force_encoding("UTF-8")).map { |l| l["name"] }
127+
rescue JSON::ParserError
128+
[]
129+
end
130+
101131
def parse_pr_list(stdout, branch_prefix)
102132
parsed = JSON.parse(stdout.force_encoding("UTF-8"))
103133
# `head:foo/` is a *search* term, not a strict filter — GitHub's

test/executor_test.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ def initialize
2626
@next_pr_number = 1000
2727
end
2828

29+
def ensure_labels(labels)
30+
end
31+
2932
def create_pr(branch:, base:, title:, body:, labels: [])
3033
@created << {branch: branch, base: base, title: title, body: body, labels: labels}
3134
n = @next_pr_number

test/gh_client_test.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,49 @@ def test_list_open_prs_propagates_gh_failure
115115
assert_includes err.message, "auth required"
116116
end
117117

118+
# ---- ensure_labels ----
119+
120+
def test_ensure_labels_is_a_no_op_when_labels_is_empty
121+
@client.ensure_labels([])
122+
assert_equal 0, @runner.calls.size
123+
end
124+
125+
def test_ensure_labels_creates_missing_labels
126+
@runner.add(
127+
pattern: ["gh", "label", "list", "--repo", REPO, "--json", "name", "--limit", "200"],
128+
stdout: +%([{"name":"dependencies"}])
129+
)
130+
@runner.add(
131+
pattern: ["gh", "label", "create", "javascript", "--repo", REPO, "--color", "0075ca"],
132+
stdout: +""
133+
)
134+
@client.ensure_labels(%w[dependencies javascript])
135+
assert_equal 2, @runner.calls.size
136+
end
137+
138+
def test_ensure_labels_skips_labels_that_already_exist
139+
@runner.add(
140+
pattern: ["gh", "label", "list", "--repo", REPO, "--json", "name", "--limit", "200"],
141+
stdout: +%([{"name":"dependencies"},{"name":"javascript"}])
142+
)
143+
@client.ensure_labels(%w[dependencies javascript])
144+
assert_equal 1, @runner.calls.size
145+
end
146+
147+
def test_ensure_labels_tolerates_gh_label_list_failure
148+
@runner.add(
149+
pattern: ["gh", "label", "list", "--repo", REPO, "--json", "name", "--limit", "200"],
150+
stderr: "not found",
151+
exit_code: 1
152+
)
153+
@runner.add(
154+
pattern: ["gh", "label", "create", "dependencies", "--repo", REPO, "--color", "0075ca"],
155+
stdout: +""
156+
)
157+
@client.ensure_labels(%w[dependencies])
158+
assert_equal 2, @runner.calls.size
159+
end
160+
118161
# ---- create_pr ----
119162

120163
def test_create_pr_invokes_gh_with_title_body_branch_and_base

0 commit comments

Comments
 (0)