Skip to content

Commit 6b4adc5

Browse files
Kickoff thanks workflow after tagging
This commit also refactors the github client support to make it possible to send requests that don't expect a response (like the workflow dispatch trigger).
1 parent 4adaec4 commit 6b4adc5

File tree

2 files changed

+138
-74
lines changed

2 files changed

+138
-74
lines changed

src/github.rs

Lines changed: 123 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ impl Github {
6464

6565
fn start_jwt_request(&mut self) -> anyhow::Result<()> {
6666
self.client.reset();
67+
self.client.useragent("rust-lang/promote-release").unwrap();
6768
let mut headers = curl::easy::List::new();
6869
headers.append(&format!("Authorization: Bearer {}", self.jwt()))?;
6970
self.client.http_headers(headers)?;
@@ -81,7 +82,11 @@ impl Github {
8182
struct InstallationResponse {
8283
id: u32,
8384
}
84-
let installation_id = send_request::<InstallationResponse>(&mut self.client)?.id;
85+
let installation_id = self
86+
.client
87+
.without_body()
88+
.send_with_response::<InstallationResponse>()?
89+
.id;
8590

8691
self.start_jwt_request()?;
8792
self.client.post(true)?;
@@ -92,7 +97,11 @@ impl Github {
9297
struct TokenResponse {
9398
token: String,
9499
}
95-
let token = send_request::<TokenResponse>(&mut self.client)?.token;
100+
let token = self
101+
.client
102+
.without_body()
103+
.send_with_response::<TokenResponse>()?
104+
.token;
96105
Ok(RepositoryClient {
97106
github: self,
98107
repo: repository.to_owned(),
@@ -104,13 +113,38 @@ impl Github {
104113
impl RepositoryClient<'_> {
105114
fn start_new_request(&mut self) -> anyhow::Result<()> {
106115
self.github.client.reset();
116+
self.github.client.useragent("rust-lang/promote-release")?;
107117
let mut headers = curl::easy::List::new();
108118
headers.append(&format!("Authorization: token {}", self.token))?;
109119
self.github.client.http_headers(headers)?;
110120
Ok(())
111121
}
112122

113123
pub(crate) fn tag(&mut self, tag: CreateTag<'_>) -> anyhow::Result<()> {
124+
#[derive(serde::Serialize)]
125+
struct CreateTagInternal<'a> {
126+
tag: &'a str,
127+
message: &'a str,
128+
/// sha of the object being tagged
129+
object: &'a str,
130+
#[serde(rename = "type")]
131+
type_: &'a str,
132+
tagger: CreateTagTaggerInternal<'a>,
133+
}
134+
135+
#[derive(serde::Serialize)]
136+
struct CreateTagTaggerInternal<'a> {
137+
name: &'a str,
138+
email: &'a str,
139+
}
140+
141+
#[derive(serde::Serialize)]
142+
struct CreateRefInternal<'a> {
143+
#[serde(rename = "ref")]
144+
ref_: &'a str,
145+
sha: &'a str,
146+
}
147+
114148
#[derive(serde::Deserialize)]
115149
struct CreatedTag {
116150
sha: String,
@@ -121,9 +155,10 @@ impl RepositoryClient<'_> {
121155
"https://api.github.com/repos/{repository}/git/tags",
122156
repository = self.repo,
123157
))?;
124-
let created = send_request_body::<CreatedTag, _>(
125-
&mut self.github.client,
126-
CreateTagInternal {
158+
let created = self
159+
.github
160+
.client
161+
.with_body(CreateTagInternal {
127162
tag: tag.tag_name,
128163
message: tag.message,
129164
object: tag.commit,
@@ -132,8 +167,8 @@ impl RepositoryClient<'_> {
132167
name: tag.tagger_name,
133168
email: tag.tagger_email,
134169
},
135-
},
136-
)?;
170+
})
171+
.send_with_response::<CreatedTag>()?;
137172

138173
// This mostly exists to make sure the request is successful rather than
139174
// really checking the created ref (which we already know).
@@ -149,13 +184,34 @@ impl RepositoryClient<'_> {
149184
"https://api.github.com/repos/{repository}/git/refs",
150185
repository = self.repo,
151186
))?;
152-
send_request_body::<CreatedTagRef, _>(
153-
&mut self.github.client,
154-
CreateRefInternal {
187+
self.github
188+
.client
189+
.with_body(CreateRefInternal {
155190
ref_: &format!("refs/tags/{}", tag.tag_name),
156191
sha: &created.sha,
157-
},
158-
)?;
192+
})
193+
.send_with_response::<CreatedTagRef>()?;
194+
195+
Ok(())
196+
}
197+
198+
pub(crate) fn workflow_dispatch(&mut self, workflow: &str, branch: &str) -> anyhow::Result<()> {
199+
#[derive(serde::Serialize)]
200+
struct Request<'a> {
201+
#[serde(rename = "ref")]
202+
ref_: &'a str,
203+
}
204+
self.start_new_request()?;
205+
self.github.client.post(true)?;
206+
self.github.client.url(&format!(
207+
"https://api.github.com/repos/{repository}/actions/workflows/{workflow}/dispatches",
208+
repository = self.repo,
209+
))?;
210+
211+
self.github
212+
.client
213+
.with_body(Request { ref_: branch })
214+
.send()?;
159215

160216
Ok(())
161217
}
@@ -170,66 +226,68 @@ pub(crate) struct CreateTag<'a> {
170226
pub(crate) tagger_email: &'a str,
171227
}
172228

173-
#[derive(serde::Serialize)]
174-
struct CreateTagInternal<'a> {
175-
tag: &'a str,
176-
message: &'a str,
177-
/// sha of the object being tagged
178-
object: &'a str,
179-
#[serde(rename = "type")]
180-
type_: &'a str,
181-
tagger: CreateTagTaggerInternal<'a>,
229+
trait BodyExt {
230+
fn with_body<S>(&mut self, body: S) -> Request<'_, S>;
231+
fn without_body(&mut self) -> Request<'_, ()>;
182232
}
183233

184-
#[derive(serde::Serialize)]
185-
struct CreateTagTaggerInternal<'a> {
186-
name: &'a str,
187-
email: &'a str,
234+
impl BodyExt for Easy {
235+
fn with_body<S>(&mut self, body: S) -> Request<'_, S> {
236+
Request {
237+
body: Some(body),
238+
client: self,
239+
}
240+
}
241+
fn without_body(&mut self) -> Request<'_, ()> {
242+
Request {
243+
body: None,
244+
client: self,
245+
}
246+
}
188247
}
189248

190-
#[derive(serde::Serialize)]
191-
struct CreateRefInternal<'a> {
192-
#[serde(rename = "ref")]
193-
ref_: &'a str,
194-
sha: &'a str,
249+
struct Request<'a, S> {
250+
body: Option<S>,
251+
client: &'a mut Easy,
195252
}
196253

197-
fn send_request_body<T: serde::de::DeserializeOwned, S: serde::Serialize>(
198-
client: &mut Easy,
199-
body: S,
200-
) -> anyhow::Result<T> {
201-
use std::io::Read;
202-
client.useragent("rust-lang/promote-release").unwrap();
203-
let mut response = Vec::new();
204-
let body = serde_json::to_vec(&body).unwrap();
205-
{
206-
let mut transfer = client.transfer();
207-
let mut body = &body[..];
208-
// The unwrap in the read_function is basically guaranteed to not
209-
// happen: reading into a slice can't fail. We can't use `?` since the
210-
// return type inside transfer isn't compatible with io::Error.
211-
transfer.read_function(move |dest| Ok(body.read(dest).unwrap()))?;
212-
transfer.write_function(|new_data| {
213-
response.extend_from_slice(new_data);
214-
Ok(new_data.len())
215-
})?;
216-
transfer.perform()?;
254+
impl<S: serde::Serialize> Request<'_, S> {
255+
fn send_with_response<T: serde::de::DeserializeOwned>(self) -> anyhow::Result<T> {
256+
use std::io::Read;
257+
let mut response = Vec::new();
258+
let body = self.body.map(|body| serde_json::to_vec(&body).unwrap());
259+
{
260+
let mut transfer = self.client.transfer();
261+
// The unwrap in the read_function is basically guaranteed to not
262+
// happen: reading into a slice can't fail. We can't use `?` since the
263+
// return type inside transfer isn't compatible with io::Error.
264+
if let Some(mut body) = body.as_deref() {
265+
transfer.read_function(move |dest| Ok(body.read(dest).unwrap()))?;
266+
}
267+
transfer.write_function(|new_data| {
268+
response.extend_from_slice(new_data);
269+
Ok(new_data.len())
270+
})?;
271+
transfer.perform()?;
272+
}
273+
serde_json::from_slice(&response)
274+
.with_context(|| format!("{}", String::from_utf8_lossy(&response)))
217275
}
218-
serde_json::from_slice(&response)
219-
.with_context(|| format!("{}", String::from_utf8_lossy(&response)))
220-
}
221276

222-
fn send_request<T: serde::de::DeserializeOwned>(client: &mut Easy) -> anyhow::Result<T> {
223-
client.useragent("rust-lang/promote-release").unwrap();
224-
let mut response = Vec::new();
225-
{
226-
let mut transfer = client.transfer();
227-
transfer.write_function(|new_data| {
228-
response.extend_from_slice(new_data);
229-
Ok(new_data.len())
230-
})?;
231-
transfer.perform()?;
277+
fn send(self) -> anyhow::Result<()> {
278+
use std::io::Read;
279+
let body = self.body.map(|body| serde_json::to_vec(&body).unwrap());
280+
{
281+
let mut transfer = self.client.transfer();
282+
// The unwrap in the read_function is basically guaranteed to not
283+
// happen: reading into a slice can't fail. We can't use `?` since the
284+
// return type inside transfer isn't compatible with io::Error.
285+
if let Some(mut body) = body.as_deref() {
286+
transfer.read_function(move |dest| Ok(body.read(dest).unwrap()))?;
287+
}
288+
transfer.perform()?;
289+
}
290+
291+
Ok(())
232292
}
233-
serde_json::from_slice(&response)
234-
.with_context(|| format!("{}", String::from_utf8_lossy(&response)))
235293
}

src/main.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use anyhow::Error;
2020
use chrono::Utc;
2121
use curl::easy::Easy;
2222
use fs2::FileExt;
23-
use github::CreateTag;
23+
use github::{CreateTag, Github};
2424
use rayon::prelude::*;
2525
use xz2::read::XzDecoder;
2626

@@ -623,8 +623,20 @@ impl Context {
623623
return Ok(());
624624
}
625625

626+
let mut github = if let Some(github) = self.config.github() {
627+
github
628+
} else {
629+
eprintln!("Skipping tagging - GitHub credentials not configured");
630+
return Ok(());
631+
};
632+
626633
if let Some(repo) = self.config.rustc_tag_repository.clone() {
627-
self.tag_repository(signer, &repo, rustc_commit)?;
634+
self.tag_repository(signer, &mut github, &repo, rustc_commit)?;
635+
636+
// Once we've tagged rustc, kick off a thanks workflow run.
637+
github
638+
.token("rust-lang/thanks")?
639+
.workflow_dispatch("ci.yml", "master")?;
628640
}
629641

630642
Ok(())
@@ -633,16 +645,10 @@ impl Context {
633645
fn tag_repository(
634646
&mut self,
635647
signer: &mut Signer,
648+
github: &mut Github,
636649
repository: &str,
637650
commit: &str,
638651
) -> Result<(), Error> {
639-
let mut github = if let Some(github) = self.config.github() {
640-
github
641-
} else {
642-
eprintln!("Skipping tagging - GitHub credentials not configured");
643-
return Ok(());
644-
};
645-
646652
let version = self.current_version.as_ref().expect("has current version");
647653
let tag_name = version.to_owned();
648654
let username = "rust-lang/promote-release";

0 commit comments

Comments
 (0)