@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
7
7
8
8
use crate :: { re, util:: comma} ;
9
9
10
- use super :: { issue_id:: Repository , labels:: GhLabel } ;
10
+ use super :: { issue_id:: Repository , labels:: GhLabel , milestone :: GhMilestone } ;
11
11
12
12
#[ derive( Clone , Debug , Serialize , Deserialize , PartialEq , Eq , PartialOrd , Ord ) ]
13
13
pub struct ExistingGithubIssue {
@@ -19,6 +19,7 @@ pub struct ExistingGithubIssue {
19
19
pub body : String ,
20
20
pub state : GithubIssueState ,
21
21
pub labels : Vec < GhLabel > ,
22
+ pub milestone : Option < GhMilestone > ,
22
23
}
23
24
24
25
#[ derive( Clone , Debug , Serialize , Deserialize , PartialEq , Eq , PartialOrd , Ord ) ]
@@ -39,6 +40,7 @@ struct ExistingGithubIssueJson {
39
40
body : String ,
40
41
state : GithubIssueState ,
41
42
labels : Vec < GhLabel > ,
43
+ milestone : Option < GhMilestone > ,
42
44
}
43
45
44
46
#[ derive( Debug , Serialize , Deserialize , PartialEq , Eq , PartialOrd , Ord ) ]
@@ -122,17 +124,34 @@ pub fn list_issues_in_milestone(
122
124
repository : & Repository ,
123
125
timeframe : & str ,
124
126
) -> anyhow:: Result < Vec < ExistingGithubIssue > > {
125
- let output = Command :: new ( "gh" )
126
- . arg ( "-R" )
127
+ list_issues ( repository, & [ ( "-m" , timeframe) ] )
128
+ }
129
+
130
+ pub fn list_tracking_issues ( repository : & Repository ) -> anyhow:: Result < Vec < ExistingGithubIssue > > {
131
+ list_issues ( repository, & [ ( "-l" , "C-tracking-issue" ) ] )
132
+ }
133
+
134
+ pub fn list_issues (
135
+ repository : & Repository ,
136
+ filter : & [ ( & str , & str ) ] ,
137
+ ) -> anyhow:: Result < Vec < ExistingGithubIssue > > {
138
+ let mut cmd = Command :: new ( "gh" ) ;
139
+
140
+ cmd. arg ( "-R" )
127
141
. arg ( & repository. to_string ( ) )
128
142
. arg ( "issue" )
129
143
. arg ( "list" )
130
- . arg ( "-m" )
131
- . arg ( timeframe)
132
144
. arg ( "-s" )
133
- . arg ( "all" )
145
+ . arg ( "all" ) ;
146
+
147
+ for ( opt, val) in filter {
148
+ cmd. arg ( opt) ;
149
+ cmd. arg ( val) ;
150
+ }
151
+
152
+ let output = cmd
134
153
. arg ( "--json" )
135
- . arg ( "title,assignees,number,comments,body,state,labels" )
154
+ . arg ( "title,assignees,number,comments,body,state,labels,milestone " )
136
155
. output ( )
137
156
. with_context ( || format ! ( "running github cli tool `gh`" ) ) ?;
138
157
@@ -180,6 +199,55 @@ pub fn create_issue(
180
199
}
181
200
}
182
201
202
+ pub fn change_milestone (
203
+ repository : & Repository ,
204
+ number : u64 ,
205
+ milestone : & str ,
206
+ ) -> anyhow:: Result < ( ) > {
207
+ let mut command = Command :: new ( "gh" ) ;
208
+ command
209
+ . arg ( "-R" )
210
+ . arg ( & repository. to_string ( ) )
211
+ . arg ( "issue" )
212
+ . arg ( "edit" )
213
+ . arg ( number. to_string ( ) )
214
+ . arg ( "-m" )
215
+ . arg ( milestone) ;
216
+
217
+ let output = command. output ( ) ?;
218
+ if !output. status . success ( ) {
219
+ Err ( anyhow:: anyhow!(
220
+ "failed to change milestone `{}`: {}" ,
221
+ number,
222
+ String :: from_utf8_lossy( & output. stderr)
223
+ ) )
224
+ } else {
225
+ Ok ( ( ) )
226
+ }
227
+ }
228
+
229
+ pub fn create_comment ( repository : & Repository , number : u64 , body : & str ) -> anyhow:: Result < ( ) > {
230
+ let output = Command :: new ( "gh" )
231
+ . arg ( "-R" )
232
+ . arg ( & repository. to_string ( ) )
233
+ . arg ( "issue" )
234
+ . arg ( "comment" )
235
+ . arg ( number. to_string ( ) )
236
+ . arg ( "-b" )
237
+ . arg ( body)
238
+ . output ( ) ?;
239
+
240
+ if !output. status . success ( ) {
241
+ Err ( anyhow:: anyhow!(
242
+ "failed to leave comment on issue `{}`: {}" ,
243
+ number,
244
+ String :: from_utf8_lossy( & output. stderr)
245
+ ) )
246
+ } else {
247
+ Ok ( ( ) )
248
+ }
249
+ }
250
+
183
251
pub fn sync_assignees (
184
252
repository : & Repository ,
185
253
number : u64 ,
@@ -216,7 +284,7 @@ pub fn sync_assignees(
216
284
217
285
pub const FLAGSHIP_LABEL : & str = "Flagship Goal" ;
218
286
219
- const LOCK_TEXT : & str = "This issue is intended for status updates only.\n \n For general questions or comments, please contact the owner(s) directly." ;
287
+ pub const LOCK_TEXT : & str = "This issue is intended for status updates only.\n \n For general questions or comments, please contact the owner(s) directly." ;
220
288
221
289
impl ExistingGithubIssue {
222
290
/// We use the presence of a "lock comment" as a signal that we successfully locked the issue.
@@ -255,25 +323,6 @@ pub fn lock_issue(repository: &Repository, number: u64) -> anyhow::Result<()> {
255
323
}
256
324
}
257
325
258
- // Leave a comment explaining what is going on.
259
- let output = Command :: new ( "gh" )
260
- . arg ( "-R" )
261
- . arg ( & repository. to_string ( ) )
262
- . arg ( "issue" )
263
- . arg ( "comment" )
264
- . arg ( number. to_string ( ) )
265
- . arg ( "-b" )
266
- . arg ( LOCK_TEXT )
267
- . output ( ) ?;
268
-
269
- if !output. status . success ( ) {
270
- return Err ( anyhow:: anyhow!(
271
- "failed to leave lock comment `{}`: {}" ,
272
- number,
273
- String :: from_utf8_lossy( & output. stderr)
274
- ) ) ;
275
- }
276
-
277
326
Ok ( ( ) )
278
327
}
279
328
@@ -308,6 +357,7 @@ impl From<ExistingGithubIssueJson> for ExistingGithubIssue {
308
357
body : e_i. body ,
309
358
state : e_i. state ,
310
359
labels : e_i. labels ,
360
+ milestone : e_i. milestone ,
311
361
}
312
362
}
313
363
}
0 commit comments